diff --git a/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java b/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java
index 011e30450..3ea667866 100644
--- a/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java
+++ b/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java
@@ -250,6 +250,7 @@ public static ClientCapabilities create(Object experimental) {
"documentation",
"defaultLibrary"*/
));
+ semanticTokensCapabilities.setMultilineTokenSupport(Boolean.TRUE);
semanticTokensCapabilities.setServerCancelSupport(Boolean.TRUE);
var semanticTokensClientCapabilitiesRequests = new SemanticTokensClientCapabilitiesRequests(Boolean.TRUE, Boolean.FALSE);
semanticTokensCapabilities.setFormats(List.of(TokenFormat.Relative));
diff --git a/src/test/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LuaSemanticTokensTest.java b/src/test/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LuaSemanticTokensTest.java
new file mode 100644
index 000000000..8db75cd26
--- /dev/null
+++ b/src/test/java/com/redhat/devtools/lsp4ij/features/semanticTokens/LuaSemanticTokensTest.java
@@ -0,0 +1,119 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Red Hat, Inc.
+ * Distributed under license by Red Hat, Inc. All rights reserved.
+ * This program is made available under the terms of the
+ * Eclipse Public License v2.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v20.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ ******************************************************************************/
+package com.redhat.devtools.lsp4ij.features.semanticTokens;
+
+import com.redhat.devtools.lsp4ij.fixtures.LSPSemanticTokensFixtureTestCase;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Completion tests by emulating LSP 'textDocument/semanticTokens' responses
+ * from the Lua language server.
+ */
+public class LuaSemanticTokensTest extends LSPSemanticTokensFixtureTestCase {
+
+ public LuaSemanticTokensTest() {
+ // Use *.luax instead of *.lua to avoid consuming the lua textmate
+ super("*.luax");
+ }
+
+
+ public void testSemanticTokens() {
+ // 1. Test completion items result
+ assertSemanticTokens("test.luax",
+ """
+ --[[
+ ** {==================================================================
+ ** Testing memory limits
+ ** ===================================================================
+ --]]
+
+ print("memory-allocation errors")
+ """,
+ """
+ {
+ "data": [
+ 0,
+ 2,
+ 4,
+ 17,
+ 0,
+ 6,
+ 0,
+ 5,
+ 12,
+ 512
+ ]
+ }
+ """,
+ """
+ --[[
+ ** {==================================================================
+ ** Testing memory limits
+ ** ===================================================================
+ --]]
+
+ print("memory-allocation errors")
+ """
+ );
+ }
+
+ private void assertSemanticTokens(@NotNull String fileName,
+ @NotNull String editorContentText,
+ @NotNull String jsonSemanticTokens,
+ @NotNull String expected) {
+ String semanticProvider = """
+ {
+ "legend": {
+ "tokenTypes": [
+ "namespace",
+ "type",
+ "class",
+ "enum",
+ "interface",
+ "struct",
+ "typeParameter",
+ "parameter",
+ "variable",
+ "property",
+ "enumMember",
+ "event",
+ "function",
+ "method",
+ "macro",
+ "keyword",
+ "modifier",
+ "comment",
+ "string",
+ "number",
+ "regexp",
+ "operator",
+ "decorator"
+ ],
+ "tokenModifiers": [
+ "declaration",
+ "definition",
+ "readonly",
+ "static",
+ "deprecated",
+ "abstract",
+ "async",
+ "modification",
+ "documentation",
+ "defaultLibrary",
+ "global"
+ ]
+ },
+ "range": true,
+ "full": true
+ }""";
+ assertSemanticTokens(fileName, editorContentText, semanticProvider, jsonSemanticTokens, expected);
+ }
+}
diff --git a/src/test/java/com/redhat/devtools/lsp4ij/fixtures/LSPSemanticTokensFixtureTestCase.java b/src/test/java/com/redhat/devtools/lsp4ij/fixtures/LSPSemanticTokensFixtureTestCase.java
new file mode 100644
index 000000000..973529a11
--- /dev/null
+++ b/src/test/java/com/redhat/devtools/lsp4ij/fixtures/LSPSemanticTokensFixtureTestCase.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2024 Red Hat, Inc.
+ * Distributed under license by Red Hat, Inc. All rights reserved.
+ * This program is made available under the terms of the
+ * Eclipse Public License v2.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v20.html
+ *
+ * Contributors:
+ * Red Hat, Inc. - initial API and implementation
+ ******************************************************************************/
+package com.redhat.devtools.lsp4ij.fixtures;
+
+import com.redhat.devtools.lsp4ij.JSONUtils;
+import com.redhat.devtools.lsp4ij.features.semanticTokens.inspector.SemanticTokensInspectorData;
+import com.redhat.devtools.lsp4ij.features.semanticTokens.inspector.SemanticTokensInspectorListener;
+import com.redhat.devtools.lsp4ij.features.semanticTokens.inspector.SemanticTokensInspectorManager;
+import com.redhat.devtools.lsp4ij.mock.MockLanguageServer;
+import org.eclipse.lsp4j.SemanticTokens;
+import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Base class test case to test LSP 'textDocument/semanticTokens' feature.
+ */
+public abstract class LSPSemanticTokensFixtureTestCase extends LSPCodeInsightFixtureTestCase {
+
+ public LSPSemanticTokensFixtureTestCase(String... fileNamePatterns) {
+ super(fileNamePatterns);
+ }
+
+ /**
+ * Test LSP semanticTokens.
+ *
+ * @param fileName the file name used to match registered language servers.
+ * @param editorContentText the editor content text.
+ * @param jsonSemanticProvider the LSP SemanticTokensWithRegistrationOptions as JSON string.
+ * @param jsonSemanticTokens the LSP SemanticTokens as JSON string.
+ * @param expected the expected IJ SemanticTokens inspector data.
+ */
+ public void assertSemanticTokens(@NotNull String fileName,
+ @NotNull String editorContentText,
+ @NotNull String jsonSemanticProvider,
+ @NotNull String jsonSemanticTokens,
+ @NotNull String expected) {
+ var semanticProvider = JSONUtils.getLsp4jGson().fromJson(jsonSemanticProvider, SemanticTokensWithRegistrationOptions.class);
+ var semanticTokens = JSONUtils.getLsp4jGson().fromJson(jsonSemanticTokens, SemanticTokens.class);
+ assertSemanticTokens(fileName, editorContentText, semanticProvider, semanticTokens, expected);
+ }
+
+ /**
+ * Test LSP semanticTokens.
+ *
+ * @param fileName the file name used to match registered language servers.
+ * @param editorContentText the editor content text.
+ * @param semanticProvider the LSP SemanticTokensWithRegistrationOptions.
+ * @param semanticTokens the LSP SemanticTokens.
+ * @param expected the expected IJ SemanticTokens inspector data.
+ */
+ public void assertSemanticTokens(@NotNull String fileName,
+ @NotNull String editorContentText,
+ @NotNull SemanticTokensWithRegistrationOptions semanticProvider,
+ @NotNull SemanticTokens semanticTokens,
+ @NotNull String expected) {
+
+ var project = myFixture.getProject();
+ var refData = new AtomicReference();
+ SemanticTokensInspectorListener listener = data -> refData.set(data);
+ SemanticTokensInspectorManager.getInstance(project).addSemanticTokensInspectorListener(listener);
+ try {
+
+ MockLanguageServer.INSTANCE.setTimeToProceedQueries(200);
+
+ var serverCapabilities = MockLanguageServer.INSTANCE.defaultServerCapabilities();
+ serverCapabilities.setSemanticTokensProvider(semanticProvider);
+ MockLanguageServer.reset(() -> serverCapabilities);
+ MockLanguageServer.INSTANCE.setSemanticTokens(semanticTokens);
+
+ // Open editor for a given file name and content (which declares to know where the completion is triggered).
+ myFixture.configureByText(fileName, editorContentText);
+ myFixture.doHighlighting();
+
+ assertNotNull(refData.get());
+ String actual = SemanticTokensInspectorManager.format(refData.get(), true, false, false, project);
+ assertEquals(expected, actual);
+
+ }
+ finally {
+ SemanticTokensInspectorManager.getInstance(project).removeSemanticTokensInspectorListener(listener);
+ }
+ }
+
+}
diff --git a/src/test/java/com/redhat/devtools/lsp4ij/mock/MockLanguageServer.java b/src/test/java/com/redhat/devtools/lsp4ij/mock/MockLanguageServer.java
index 7db643a8a..0e4863bcf 100644
--- a/src/test/java/com/redhat/devtools/lsp4ij/mock/MockLanguageServer.java
+++ b/src/test/java/com/redhat/devtools/lsp4ij/mock/MockLanguageServer.java
@@ -162,6 +162,10 @@ public void setCompletionList(CompletionList completionList) {
this.textDocumentService.setMockCompletionList(completionList);
}
+ public void setSemanticTokens(SemanticTokens semanticTokens) {
+ this.textDocumentService.setSemanticTokens(semanticTokens);
+ }
+
public void setHover(Hover hover) {
this.textDocumentService.setMockHover(hover);
}
@@ -320,4 +324,5 @@ public String toString() {
return "MockLanguageServer [started=" + started + ", delay=" + delay + ", remoteProxies=" + remoteProxies.size()
+ ", inFlight=" + inFlight.size() + "]";
}
+
}
\ No newline at end of file
diff --git a/src/test/java/com/redhat/devtools/lsp4ij/mock/MockTextDocumentService.java b/src/test/java/com/redhat/devtools/lsp4ij/mock/MockTextDocumentService.java
index 6fb5c3b40..60406c3f9 100644
--- a/src/test/java/com/redhat/devtools/lsp4ij/mock/MockTextDocumentService.java
+++ b/src/test/java/com/redhat/devtools/lsp4ij/mock/MockTextDocumentService.java
@@ -152,7 +152,7 @@ public CompletableFuture> codeLens(CodeLensParams param
File file = new File(URI.create(params.getTextDocument().getUri()));
if (file.exists() && file.length() > 100) {
return CompletableFuture.completedFuture(Collections.singletonList(new CodeLens(
- new Range(new Position(1, 0), new Position(1, 1)), new Command("Hi, I'm a CodeLens", null), null)));
+ new Range(new Position(1, 0), new Position(1, 1)), new Command("Hi, I'm a CodeLens", "mock.command"), null)));
}
return CompletableFuture.completedFuture(Collections.emptyList());
}
@@ -410,4 +410,5 @@ public void setFoldingRanges(List foldingRanges) {
public CompletableFuture> foldingRange(FoldingRangeRequestParams params) {
return CompletableFuture.completedFuture(this.foldingRanges);
}
+
}
\ No newline at end of file