You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ne...@apache.org on 2022/01/25 09:57:08 UTC
[netbeans] branch delivery updated: Fixes to LSP client/server related to semantic highlighting.
This is an automated email from the ASF dual-hosted git repository.
neilcsmith pushed a commit to branch delivery
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/delivery by this push:
new 82a0ab7 Fixes to LSP client/server related to semantic highlighting.
new 8387f9f Merge pull request #3492 from jlahoda/LSP-client-semantic-fixes
82a0ab7 is described below
commit 82a0ab7d7bc5e4211710bcf754a5c751b3c6e22c
Author: Jan Lahoda <jl...@netbeans.org>
AuthorDate: Sat Jan 22 17:52:56 2022 +0100
Fixes to LSP client/server related to semantic highlighting.
---
.../netbeans/modules/lsp/client/LSPBindings.java | 13 +++
.../client/bindings/CompletionProviderImpl.java | 6 +-
.../lsp/client/bindings/SemanticHighlight.java | 73 ++++++----------
.../textmate/lexer/resources/fontsColors.xml | 2 +
.../netbeans/modules/java/lsp/server/Utils.java | 4 +
.../server/protocol/TextDocumentServiceImpl.java | 7 +-
.../java/lsp/server/protocol/ServerTest.java | 99 +++++++++++++++++++++-
7 files changed, 150 insertions(+), 54 deletions(-)
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
index b2f180d..2fcb1cd 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/LSPBindings.java
@@ -37,6 +37,7 @@ import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
@@ -56,6 +57,8 @@ import org.eclipse.lsp4j.InitializeParams;
import org.eclipse.lsp4j.InitializeResult;
import org.eclipse.lsp4j.ResourceOperationKind;
import org.eclipse.lsp4j.SemanticTokens;
+import org.eclipse.lsp4j.SemanticTokensCapabilities;
+import org.eclipse.lsp4j.SemanticTokensClientCapabilitiesRequests;
import org.eclipse.lsp4j.SemanticTokensLegend;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.SymbolCapabilities;
@@ -367,6 +370,7 @@ public class LSPBindings {
dsc.setHierarchicalDocumentSymbolSupport(true);
dsc.setSymbolKind(new SymbolKindCapabilities(Arrays.asList(SymbolKind.values())));
tdcc.setDocumentSymbol(dsc);
+ tdcc.setSemanticTokens(new SemanticTokensCapabilities(new SemanticTokensClientCapabilitiesRequests(true), KNOWN_TOKEN_TYPES, KNOWN_TOKEN_MODIFIERS, Arrays.asList()));
WorkspaceClientCapabilities wcc = new WorkspaceClientCapabilities();
wcc.setWorkspaceEdit(new WorkspaceEditCapabilities());
wcc.getWorkspaceEdit().setDocumentChanges(true);
@@ -387,6 +391,15 @@ public class LSPBindings {
}
}
}
+ private static final List<String> KNOWN_TOKEN_TYPES = Collections.unmodifiableList(Arrays.asList(
+ "namespace", "package", "function", "method", "macro", "parameter",
+ "variable", "struct", "enum", "class", "typeAlias", "typeParameter",
+ "field", "enumMember", "keyword"
+ ));
+
+ private static final List<String> KNOWN_TOKEN_MODIFIERS = Collections.unmodifiableList(Arrays.asList(
+ "static", "definition", "declaration"
+ ));
public static synchronized Set<LSPBindings> getAllBindings() {
Set<LSPBindings> allBindings = Collections.newSetFromMap(new IdentityHashMap<>());
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
index 0fba1fc..0869a66 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/CompletionProviderImpl.java
@@ -42,6 +42,7 @@ import org.eclipse.lsp4j.CompletionList;
import org.eclipse.lsp4j.CompletionOptions;
import org.eclipse.lsp4j.CompletionParams;
import org.eclipse.lsp4j.InitializeResult;
+import org.eclipse.lsp4j.InsertReplaceEdit;
import org.eclipse.lsp4j.MarkupContent;
import org.eclipse.lsp4j.ParameterInformation;
import org.eclipse.lsp4j.ServerCapabilities;
@@ -189,13 +190,14 @@ public class CompletionProviderImpl implements CompletionProvider {
commit("");
}
private void commit(String appendText) {
- if (i.getTextEdit().isRight()) {
+ Either<TextEdit, InsertReplaceEdit> edit = i.getTextEdit();
+ if (edit != null && edit.isRight()) {
//TODO: the NetBeans client does not current support InsertReplaceEdits, should not happen
Completion.get().hideDocumentation();
Completion.get().hideCompletion();
return ;
}
- TextEdit te = i.getTextEdit().getLeft();
+ TextEdit te = edit != null ? edit.getLeft() : null;
NbDocument.runAtomic((StyledDocument) doc, () -> {
try {
int endPos;
diff --git a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java
index e40323a..d63113f 100644
--- a/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java
+++ b/ide/lsp.client/src/org/netbeans/modules/lsp/client/bindings/SemanticHighlight.java
@@ -18,12 +18,12 @@
*/
package org.netbeans.modules.lsp.client.bindings;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
+import java.util.Collection;
+import java.util.Enumeration;
import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
@@ -32,11 +32,9 @@ import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.SemanticTokens;
import org.eclipse.lsp4j.SemanticTokensLegend;
import org.eclipse.lsp4j.SemanticTokensParams;
+import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
import org.eclipse.lsp4j.TextDocumentIdentifier;
-import org.eclipse.lsp4j.generator.JsonRpcData;
-import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
import org.netbeans.api.editor.mimelookup.MimeLookup;
-import org.netbeans.api.editor.mimelookup.MimePath;
import org.netbeans.api.editor.mimelookup.MimeRegistration;
import org.netbeans.api.editor.settings.AttributesUtilities;
import org.netbeans.api.editor.settings.FontColorSettings;
@@ -65,6 +63,10 @@ public class SemanticHighlight implements BackgroundTask {
@Override
public void run(LSPBindings bindings, FileObject file) {
try {
+ SemanticTokensWithRegistrationOptions options = bindings.getInitResult().getCapabilities().getSemanticTokensProvider();
+ if (options == null) {
+ return ;
+ }
OffsetsBag target = new OffsetsBag(doc);
SemanticTokensParams params = new SemanticTokensParams(new TextDocumentIdentifier(Utils.toURI(file)));
SemanticTokens tokens = bindings.getTextDocumentService().semanticTokensFull(params).get();
@@ -130,7 +132,7 @@ public class SemanticHighlight implements BackgroundTask {
AttributeSet statik = isStatic ? fcs.getTokenFontColors("mod-static") : null;
if (colors != null && statik != null) {
- return AttributesUtilities.createComposite(colors, statik);
+ return AttributesUtilities.createComposite(adjustAttributes(colors), adjustAttributes(statik));
} else if (colors != null) {
return colors;
} else if (statik != null) {
@@ -153,6 +155,22 @@ public class SemanticHighlight implements BackgroundTask {
return bag;
}
+ private static AttributeSet adjustAttributes(AttributeSet as) {
+ Collection<Object> attrs = new LinkedList<Object>();
+
+ for (Enumeration<?> e = as.getAttributeNames(); e.hasMoreElements(); ) {
+ Object key = e.nextElement();
+ Object value = as.getAttribute(key);
+
+ if (value != Boolean.FALSE) {
+ attrs.add(key);
+ attrs.add(value);
+ }
+ }
+
+ return AttributesUtilities.createImmutable(attrs.toArray());
+ }
+
@MimeRegistration(mimeType="", service=HighlightsLayerFactory.class)
public static final class HighlightsLayerFactoryImpl implements HighlightsLayerFactory {
@@ -164,44 +182,5 @@ public class SemanticHighlight implements BackgroundTask {
}
}
-
-// @JsonRpcData
-// public static class SemanticTokensParams {
-//
-// private TextDocumentIdentifier textDocument;
-//
-// public SemanticTokensParams() {
-// }
-//
-// public SemanticTokensParams(TextDocumentIdentifier textDocument) {
-// this.textDocument = textDocument;
-// }
-//
-// public TextDocumentIdentifier getTextDocument() {
-// return textDocument;
-// }
-//
-// public void setTextDocument(TextDocumentIdentifier textDocument) {
-// this.textDocument = textDocument;
-// }
-//
-// }
-//
-// @JsonRpcData
-// public static class SemanticTokens {
-// private List<Number> data;
-//
-// public List<Number> getData() {
-// return data;
-// }
-//
-// public void setData(List<Number> data) {
-// this.data = data;
-// }
-//
-// }
-// public interface SemanticService {
-// @JsonRequest("textDocument/semanticTokens/full")
-// public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams params);
-// }
+
}
diff --git a/ide/textmate.lexer/src/org/netbeans/modules/textmate/lexer/resources/fontsColors.xml b/ide/textmate.lexer/src/org/netbeans/modules/textmate/lexer/resources/fontsColors.xml
index 27acc9b..2c1fcbb 100644
--- a/ide/textmate.lexer/src/org/netbeans/modules/textmate/lexer/resources/fontsColors.xml
+++ b/ide/textmate.lexer/src/org/netbeans/modules/textmate/lexer/resources/fontsColors.xml
@@ -67,6 +67,7 @@
<fontcolor name="mod-typeParameter" default="identifier"/>
<fontcolor name="mod-field" default="field"/>
<fontcolor name="mod-enumMember" default="field"/>
+ <fontcolor name="mod-keyword" default="keyword"/>
<fontcolor name="mod-namespace-declaration" default="identifier"/>
<fontcolor name="mod-package-declaration" default="identifier"/>
@@ -82,6 +83,7 @@
<fontcolor name="mod-typeParameter-declaration" default="identifier"/>
<fontcolor name="mod-field-declaration" default="field"/>
<fontcolor name="mod-enumMember-declaration" default="field"/>
+ <fontcolor name="mod-keyword-declaration" default="keyword"/>
<fontcolor name="mod-static" >
<font style="italic" />
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
index 46ec105..3bb6bcb 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/Utils.java
@@ -68,12 +68,14 @@ public class Utils {
case ENUM:
return SymbolKind.Enum;
case CLASS:
+ case RECORD:
return SymbolKind.Class;
case ANNOTATION_TYPE:
return SymbolKind.Interface;
case INTERFACE:
return SymbolKind.Interface;
case ENUM_CONSTANT:
+ case RECORD_COMPONENT:
return SymbolKind.EnumMember;
case FIELD:
return SymbolKind.Field; //TODO: constant
@@ -110,6 +112,7 @@ public class Utils {
case INTERFACE:
case ENUM:
case ANNOTATION_TYPE:
+ case RECORD:
TypeElement te = (TypeElement) e;
StringBuilder sb = new StringBuilder();
sb.append(fqn ? te.getQualifiedName() : te.getSimpleName());
@@ -140,6 +143,7 @@ public class Utils {
return sb.toString();
case FIELD:
case ENUM_CONSTANT:
+ case RECORD_COMPONENT:
return e.getSimpleName().toString();
case CONSTRUCTOR:
case METHOD:
diff --git a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
index 8a52631..0f62d46 100644
--- a/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
+++ b/java/java.lsp.server/src/org/netbeans/modules/java/lsp/server/protocol/TextDocumentServiceImpl.java
@@ -1947,8 +1947,8 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
static BiConsumer<String, Object> HOOK_NOTIFICATION = null;
private static final Map<ColoringAttributes, List<String>> COLORING_TO_TOKEN_TYPE_CANDIDATES = new HashMap<ColoringAttributes, List<String>>() {{
- put(ColoringAttributes.FIELD, Arrays.asList("member"));
- put(ColoringAttributes.RECORD_COMPONENT, Arrays.asList("member"));
+ put(ColoringAttributes.FIELD, Arrays.asList("field", "member"));
+ put(ColoringAttributes.RECORD_COMPONENT, Arrays.asList("field", "member"));
put(ColoringAttributes.LOCAL_VARIABLE, Arrays.asList("variable"));
put(ColoringAttributes.PARAMETER, Arrays.asList("parameter"));
put(ColoringAttributes.METHOD, Arrays.asList("method", "function"));
@@ -1959,6 +1959,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
put(ColoringAttributes.ANNOTATION_TYPE, Arrays.asList("interface"));
put(ColoringAttributes.ENUM, Arrays.asList("enum"));
put(ColoringAttributes.TYPE_PARAMETER_DECLARATION, Arrays.asList("typeParameter"));
+ put(ColoringAttributes.KEYWORD, Arrays.asList("keyword"));
}};
private static final Map<ColoringAttributes, List<String>> COLORING_TO_TOKEN_MODIFIERS_CANDIDATES = new HashMap<ColoringAttributes, List<String>>() {{
@@ -2011,7 +2012,7 @@ public class TextDocumentServiceImpl implements TextDocumentService, LanguageCli
modifiers |= mod;
}
}
- if (tokenType.isPresent()) {
+ if (tokenType.isPresent() && tokenType.get() >= 0) {
result.add(line - lastLine);
result.add((int) (currentOffset - currentLineStart - column));
result.add(e.getKey().length());
diff --git a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index ad652e7..49c5fb9 100644
--- a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++ b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -159,11 +159,13 @@ import org.netbeans.modules.java.lsp.server.refactoring.ChangeMethodParameterUI;
import org.netbeans.modules.java.lsp.server.refactoring.ParameterUI;
import org.netbeans.modules.java.lsp.server.ui.MockHtmlViewer;
import org.netbeans.modules.java.source.BootClassPathUtil;
+import org.netbeans.modules.java.source.parsing.JavacParser;
import org.netbeans.modules.parsing.api.ResultIterator;
import org.netbeans.modules.parsing.impl.indexing.implspi.CacheFolderProvider;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.java.queries.AnnotationProcessingQueryImplementation;
+import org.netbeans.spi.java.queries.SourceLevelQueryImplementation;
import org.netbeans.spi.lsp.ErrorProvider;
import org.netbeans.spi.project.ProjectFactory;
import org.netbeans.spi.project.ProjectState;
@@ -710,10 +712,12 @@ public class ServerTest extends NbTestCase {
" public void innerMethod() {\n" +
" }\n" +
" }\n" +
+ " record R(int i) {}\n" +
"}\n";
try (Writer w = new FileWriter(src)) {
w.write(code);
}
+ file2SourceLevel.put(FileUtil.toFileObject(src.getParentFile()), "17");
FileUtil.refreshFor(getWorkDir());
Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LspClient() {
@Override
@@ -758,7 +762,7 @@ public class ServerTest extends NbTestCase {
" character = 0\n" +
" ]\n" +
" end = Position [\n" +
- " line = 8\n" +
+ " line = 9\n" +
" character = 1\n" +
" ]\n" +
"]:(Class:Inner:Range [\n" +
@@ -779,6 +783,24 @@ public class ServerTest extends NbTestCase {
" line = 6\n" +
" character = 9\n" +
" ]\n" +
+ "]:()), Class:R:Range [\n" +
+ " start = Position [\n" +
+ " line = 8\n" +
+ " character = 4\n" +
+ " ]\n" +
+ " end = Position [\n" +
+ " line = 8\n" +
+ " character = 22\n" +
+ " ]\n" +
+ "]:(Field:i:Range [\n" +
+ " start = Position [\n" +
+ " line = 8\n" +
+ " character = 13\n" +
+ " ]\n" +
+ " end = Position [\n" +
+ " line = 8\n" +
+ " character = 18\n" +
+ " ]\n" +
"]:()), Field:field:Range [\n" +
" start = Position [\n" +
" line = 1\n" +
@@ -4713,6 +4735,66 @@ public class ServerTest extends NbTestCase {
"3:20-26:method:[]");
}
+ public void testSemanticHighlighting2() throws Exception {
+ File src = new File(getWorkDir(), "Test.java");
+ src.getParentFile().mkdirs();
+ String code = "public class Test {\n" +
+ " private static final int C = 0;\n" +
+ " public int method(int p) {\n" +
+ " int l = p + method(0);\n" +
+ " }\n" +
+ "}\n";
+ try (Writer w = new FileWriter(src)) {
+ w.write(code);
+ }
+ FileUtil.refreshFor(getWorkDir());
+ Launcher<LanguageServer> serverLauncher = LSPLauncher.createClientLauncher(new LanguageClient() {
+ @Override
+ public void telemetryEvent(Object arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void publishDiagnostics(PublishDiagnosticsParams params) {
+ }
+
+ @Override
+ public void showMessage(MessageParams arg0) {
+ }
+
+ @Override
+ public CompletableFuture<MessageActionItem> showMessageRequest(ShowMessageRequestParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ @Override
+ public void logMessage(MessageParams arg0) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+ }, client.getInputStream(), client.getOutputStream());
+ serverLauncher.startListening();
+ LanguageServer server = serverLauncher.getRemoteProxy();
+ ClientCapabilities clientCaps = new ClientCapabilities();
+ TextDocumentClientCapabilities textCaps = new TextDocumentClientCapabilities();
+ clientCaps.setTextDocument(textCaps);
+ SemanticTokensCapabilities sematicTokensCapabilities = new SemanticTokensCapabilities(true);
+ sematicTokensCapabilities.setTokenTypes(Arrays.asList("field", "method", "function", "class", "interface", "enum", "typeParameter"));
+ sematicTokensCapabilities.setTokenModifiers(Arrays.asList("declaration", "static"));
+ textCaps.setSemanticTokens(sematicTokensCapabilities);
+ InitializeParams initParams = new InitializeParams();
+ initParams.setCapabilities(clientCaps);
+ InitializeResult result = server.initialize(initParams).get();
+ assertNotNull(result.getCapabilities().getSemanticTokensProvider());
+ SemanticTokensLegend legend = result.getCapabilities().getSemanticTokensProvider().getLegend();
+ server.getTextDocumentService().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(toURI(src), "java", 0, code)));
+ assertColoring(legend,
+ server.getTextDocumentService().semanticTokensFull(new SemanticTokensParams(new TextDocumentIdentifier(toURI(src)))).get(),
+ "0:13-17:class:[declaration]",
+ "1:29-30:field:[declaration, static]",
+ "2:15-21:method:[declaration]",
+ "3:20-26:method:[]");
+ }
+
private void assertColoring(SemanticTokensLegend legend, SemanticTokens tokens, String... expected) {
List<String> coloring = new ArrayList<>();
int line = 0;
@@ -4943,7 +5025,19 @@ public class ServerTest extends NbTestCase {
public void saveProject(Project project) throws IOException, ClassCastException {
}
}
-
+
+ private static final Map<FileObject, String> file2SourceLevel = new HashMap<>();
+
+ @ServiceProvider(service=SourceLevelQueryImplementation.class)
+ public static final class SourceLevelImpl implements SourceLevelQueryImplementation {
+
+ @Override
+ public String getSourceLevel(FileObject javaFile) {
+ return file2SourceLevel.getOrDefault(javaFile, "1.8");
+ }
+
+ }
+
private static volatile ProgressCommand progressCommandInstance;
@ServiceProvider(service = CodeActionsProvider.class)
@@ -5183,5 +5277,6 @@ public class ServerTest extends NbTestCase {
static {
System.setProperty("SourcePath.no.source.filter", "true");
+ JavacParser.DISABLE_SOURCE_LEVEL_DOWNGRADE = true;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists