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 <jlah...@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-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists