Skip to content

Commit

Permalink
Merge pull request apache#7807 from matthiasblaesing/lsp_mark_provider
Browse files Browse the repository at this point in the history
lsp.client: mark occurrences sidebar and make occurrences highlighting configurable
  • Loading branch information
matthiasblaesing authored Oct 18, 2024
2 parents cddac02 + 1d5453f commit c7da182
Show file tree
Hide file tree
Showing 12 changed files with 681 additions and 19 deletions.
9 changes: 9 additions & 0 deletions ide/lsp.client/nbproject/project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@
<specification-version>1.12</specification-version>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.editor.errorstripe</code-name-base>
<build-prerequisite/>
<compile-dependency/>
<run-dependency>
<release-version>2</release-version>
<implementation-version/>
</run-dependency>
</dependency>
<dependency>
<code-name-base>org.netbeans.modules.editor.fold</code-name-base>
<build-prerequisite/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
*/
package org.netbeans.modules.lsp.client.bindings;

import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
Expand All @@ -33,7 +35,6 @@
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightParams;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.netbeans.api.editor.mimelookup.MimeLookup;
Expand All @@ -44,20 +45,28 @@
import org.netbeans.modules.lsp.client.LSPBindings;
import org.netbeans.modules.lsp.client.LSPBindings.BackgroundTask;
import org.netbeans.modules.lsp.client.Utils;
import org.netbeans.modules.lsp.client.options.MarkOccurencesSettings;
import org.netbeans.spi.editor.highlighting.HighlightsLayer;
import org.netbeans.spi.editor.highlighting.HighlightsLayerFactory;
import org.netbeans.spi.editor.highlighting.HighlightsSequence;
import org.netbeans.spi.editor.highlighting.ZOrder;
import org.netbeans.spi.editor.highlighting.support.OffsetsBag;
import org.openide.filesystems.FileObject;
import org.openide.loaders.DataObject;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;

import static org.netbeans.modules.lsp.client.options.MarkOccurencesSettingsNames.ON_OFF;
import static org.netbeans.modules.lsp.client.options.MarkOccurencesSettingsNames.KEEP_MARKS;

/**
*
* @author lahvac
*/
public class MarkOccurrences implements BackgroundTask, CaretListener, PropertyChangeListener {

public static final Color ES_COLOR = new Color(175, 172, 102);

private final JTextComponent component;
private Document doc;
private int caretPos;
Expand All @@ -73,47 +82,71 @@ public MarkOccurrences(JTextComponent component) {
}

@Override
@NbBundle.Messages(
"LBL_ES_TOOLTIP=Mark Occurrences"
)
public void run(LSPBindings bindings, FileObject file) {
Document localDoc;
int localCaretPos;

synchronized (this) {
localDoc = this.doc;
localCaretPos = this.caretPos;
}
getHighlightsBag(localDoc).setHighlights(computeHighlights(localDoc, localCaretPos));

if (!MarkOccurencesSettings.getCurrentNode().getBoolean(ON_OFF, true)) {
getHighlightsBag(doc).setHighlights(HighlightsSequence.EMPTY);
OccurrencesMarkProvider.get(doc).setOccurrences(doc, null, ES_COLOR, Bundle.LBL_ES_TOOLTIP());
return;
}

List<int[]> highlights = computeHighlights(localDoc, localCaretPos);

if (highlights != null && !highlights.isEmpty()) {
AttributeSet attr = getColoring(localDoc);
OffsetsBag occurrenesBag = new OffsetsBag(localDoc);
highlights.forEach(h -> {
occurrenesBag.addHighlight(
h[0],
h[1],
attr
);
});
getHighlightsBag(localDoc).setHighlights(occurrenesBag);
OccurrencesMarkProvider.get(localDoc).setOccurrences(localDoc, highlights, ES_COLOR, Bundle.LBL_ES_TOOLTIP());
} else if (!MarkOccurencesSettings.getCurrentNode().getBoolean(KEEP_MARKS, true)) {
getHighlightsBag(doc).setHighlights(HighlightsSequence.EMPTY);
OccurrencesMarkProvider.get(doc).setOccurrences(doc, null, ES_COLOR, Bundle.LBL_ES_TOOLTIP());
}
}

private OffsetsBag computeHighlights(Document doc, int caretPos) {
AttributeSet attr = getColoring(doc);
OffsetsBag result = new OffsetsBag(doc);
private List<int[]> computeHighlights(Document doc, int caretPos) {
if(caretPos < 0) {
return result;
return null;
}
FileObject file = NbEditorUtilities.getFileObject(doc);
if (file == null) {
return result;
return null;
}
LSPBindings server = LSPBindings.getBindings(file);
if (server == null) {
return result;
return null;
}
if (!Utils.isEnabled(server.getInitResult().getCapabilities().getDocumentHighlightProvider())) {
return result;
return null;
}
String uri = Utils.toURI(file);
try {
List<? extends DocumentHighlight> highlights
= server.getTextDocumentService().documentHighlight(new DocumentHighlightParams(new TextDocumentIdentifier(uri), Utils.createPosition(doc, caretPos))).get();
if (highlights == null) {
return result;
}
for (DocumentHighlight h : highlights) {
result.addHighlight(Utils.getOffset(doc, h.getRange().getStart()), Utils.getOffset(doc, h.getRange().getEnd()), attr);
}
return result;
return server
.getTextDocumentService()
.documentHighlight(new DocumentHighlightParams(new TextDocumentIdentifier(uri), Utils.createPosition(doc, caretPos)))
.get()
.stream()
.map(h -> new int[]{Utils.getOffset(doc, h.getRange().getStart()),Utils.getOffset(doc, h.getRange().getEnd())})
.collect(Collectors.toList());
} catch (BadLocationException | InterruptedException | ExecutionException ex) {
Exceptions.printStackTrace(ex);
return result;
return null;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.lsp.client.bindings;

import java.awt.Color;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import javax.swing.text.Document;
import javax.swing.text.StyledDocument;
import org.netbeans.modules.editor.errorstripe.privatespi.Mark;
import org.netbeans.modules.editor.errorstripe.privatespi.MarkProvider;
import org.netbeans.modules.editor.errorstripe.privatespi.Status;
import org.openide.text.NbDocument;

/**
* This is based on the OccurrencesMarkProvider from csl.api
*/
public class OccurrencesMarkProvider extends MarkProvider {

private static final Map<Document, Reference<OccurrencesMarkProvider>> providers = new WeakHashMap<>();

@SuppressWarnings("NestedAssignment")
public static synchronized OccurrencesMarkProvider get(Document doc) {
Reference<OccurrencesMarkProvider> ref = providers.get(doc);
OccurrencesMarkProvider p = ref != null ? ref.get() : null;

if (p == null) {
p = new OccurrencesMarkProvider();
providers.put(doc, new WeakReference(p));
}

return p;
}

private List<Mark> occurrences = Collections.emptyList();

@Override
public List<Mark> getMarks() {
return Collections.unmodifiableList(occurrences);
}

public void setOccurrences(final Document doc, final List<int[]> bag, final Color color, final String tooltip) {

List<Mark> old;

synchronized (this) {
old = occurrences;

if(doc.getProperty(OccurrencesMarkProvider.class) == null || bag == null) {
occurrences = Collections.emptyList();
} else {
occurrences = bag
.stream()
.map(hs -> new MarkImpl(doc, hs[0], color, tooltip))
.collect(Collectors.toList());
}

}

firePropertyChange(PROP_MARKS, old, occurrences);
}

private static final class MarkImpl implements Mark {

private final int line;
private final Color color;
private final String tooltip;

public MarkImpl(Document doc, int startOffset, Color color, String tooltip) {
this.line = NbDocument.findLineNumber((StyledDocument) doc, startOffset);
this.color = color;
this.tooltip = tooltip;
}

@Override
public int getType() {
return TYPE_ERROR_LIKE;
}

@Override
public Status getStatus() {
return Status.STATUS_OK;
}

@Override
public int getPriority() {
return PRIORITY_DEFAULT;
}

@Override
public Color getEnhancedColor() {
return color;
}

@Override
public int[] getAssignedLines() {
return new int[] {line, line};
}

@Override
public String getShortDescription() {
return tooltip;
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.lsp.client.bindings;

import javax.swing.text.JTextComponent;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.modules.editor.errorstripe.privatespi.MarkProvider;
import org.netbeans.modules.editor.errorstripe.privatespi.MarkProviderCreator;
import org.netbeans.spi.editor.mimelookup.MimeLocation;

@MimeLocation(subfolderName="UpToDateStatusProvider")
@MimeRegistration(mimeType = "", service = MarkProviderCreator.class)
public class OccurrencesMarkProviderCreator implements MarkProviderCreator {

public OccurrencesMarkProviderCreator() {
}

@Override
public MarkProvider createMarkProvider(JTextComponent pane) {
return OccurrencesMarkProvider.get(pane.getDocument());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ private void ensureDidOpenSent(Document doc, boolean sync) {
}

doc.putProperty(HyperlinkProviderImpl.class, true);
doc.putProperty(OccurrencesMarkProvider.class, true);

String uri = Utils.toURI(file);
String[] text = new String[1];
Expand Down
20 changes: 20 additions & 0 deletions ide/lsp.client/src/org/netbeans/modules/lsp/client/layer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,25 @@
<attr name="instanceCreate" methodvalue="org.netbeans.modules.lsp.client.bindings.BreadcrumbsImpl.createSideBarFactory" />
</file>
</folder>
<!-- Folder is required so that the (pseudo) mimetype is recognized -->
<folder name="text">
<folder name="x-generic-lsp">
<attr name="displayName" bundlevalue="org.netbeans.modules.lsp.client.options.Bundle#text/x-generic-lsp"/>
</folder>
</folder>
</folder>
<folder name="OptionsDialog">
<folder name="Editor">
<folder name="MarkOccurrences">
<folder name="text">
<folder name="x-generic-lsp">
<file name="LSPMarkOccurences.instance">
<attr name="instanceOf" stringvalue="org.netbeans.spi.options.OptionsPanelController"/>
<attr name="instanceCreate" newvalue="org.netbeans.modules.lsp.client.options.MarkOccurencesOptionsPanelController"/>
</file>
</folder>
</folder>
</folder>
</folder>
</folder>
</filesystem>
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ LanguageDescriptionPanel.browseIcon.text=...
LanguageServersPanel.add.text=&Add...
LanguageServersPanel.edit.text=&Edit...
LanguageServersPanel.remove.text=&Remove

CTL_OnOff_CheckBox=Mark &Occurrences Of Symbol Under Caret
CTL_KeepMarks_CheckBox=Keep Mark&s
ACSD_OnOff_CB=Checkbox switching mark occurences on/off
ACSD_Marks_CB=Keep Marks Checkbox

text/x-generic-lsp=Language Server Protocol Client (generic)
Loading

0 comments on commit c7da182

Please sign in to comment.