From 7dd5fda109d1180cd8c606afa99128af1f38dc2b Mon Sep 17 00:00:00 2001 From: Alexander Hellwig Date: Tue, 29 Sep 2026 10:41:31 +0200 Subject: [PATCH] Improve CD4Code language server --- language-server/README.md | 14 +++-- .../completion/CD4CodeCompletionProvider.java | 30 ++++++++++ .../CD4CodeLexerProvider.java | 24 ++++++++ .../_lsp/CD4CodeLanguageServerTest.java | 55 +++++++++++++++++++ .../test/resources/cd4code/SharedFeatures.cd | 7 +++ 5 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 language-server/src/main/java/de/monticore/cd4code/_lsp/features/completion/CD4CodeCompletionProvider.java create mode 100644 language-server/src/main/java/de/monticore/cd4code/_lsp/features/syntax_highlighting/CD4CodeLexerProvider.java create mode 100644 language-server/src/test/java/de/monticore/cd4code/_lsp/CD4CodeLanguageServerTest.java create mode 100644 language-server/src/test/resources/cd4code/SharedFeatures.cd diff --git a/language-server/README.md b/language-server/README.md index d049b6314..6ad47b3e8 100644 --- a/language-server/README.md +++ b/language-server/README.md @@ -1,8 +1,8 @@ -# CD4Analysis language -This project contains the language server for CD4Analysis. +# CD4Analysis and CD4Code language servers +This project contains language servers for CD4Analysis and CD4Code. -It can be build with Gradle: -- `gradle build`, the resulting jar can be found in build/lib +Build the executable servers with `gradle :language-server:packCD4AnalysisLanguageServer` +and `gradle :language-server:packCD4CodeLanguageServer`. The JARs are written to `target/libs`. References for the CD4Analysis: - https://github.com/MontiCore/cd4analysis @@ -10,5 +10,7 @@ References for the CD4Analysis: You can customize the resulting LSP via the TOP mechanism. -# Running the server -`gradle runCD4AnalysisVscodePluginAttached` +# Running the servers +Use `gradle :language-server:runCD4AnalysisVscodePluginAttached -PbuildVscodePlugin=true` +or `gradle :language-server:runCD4CodeVscodePluginAttached -PbuildVscodePlugin=true` to run a +server with its VS Code plugin attached. diff --git a/language-server/src/main/java/de/monticore/cd4code/_lsp/features/completion/CD4CodeCompletionProvider.java b/language-server/src/main/java/de/monticore/cd4code/_lsp/features/completion/CD4CodeCompletionProvider.java new file mode 100644 index 000000000..0a0c714d9 --- /dev/null +++ b/language-server/src/main/java/de/monticore/cd4code/_lsp/features/completion/CD4CodeCompletionProvider.java @@ -0,0 +1,30 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd4code._lsp.features.completion; + +import de.mclsg.lsp.CommonLanguageServer; +import de.mclsg.lsp.ISymbolUsageResolutionProvider; +import de.mclsg.lsp.document_management.DocumentManager; +import de.monticore.cd4analysis._lsp.features.completion.strategy.CD4AnalysisAssociationCardinalityCompletionStrategy; +import de.monticore.cd4analysis._lsp.features.completion.strategy.CD4AnalysisAssociationCompletionStrategy; +import de.monticore.cd4analysis._lsp.features.completion.strategy.CD4AnalysisAssociationNavigationCompletionStrategy; +import de.monticore.cd4code._lsp.language_access.CD4CodeLanguageAccess; + +public class CD4CodeCompletionProvider extends CD4CodeCompletionProviderTOP { + + public CD4CodeCompletionProvider(CommonLanguageServer languageServer, + DocumentManager documentManager, CD4CodeLanguageAccess languageAccess, + ISymbolUsageResolutionProvider symbolUsageResolutionProvider) { + super(languageServer, documentManager, languageAccess, symbolUsageResolutionProvider); + + completionStrategyManager.registerCompletionStrategy( + new CD4AnalysisAssociationCompletionStrategy(symbolUsageResolutionProvider, + documentManager)); + completionStrategyManager.registerCompletionStrategy( + new CD4AnalysisAssociationCardinalityCompletionStrategy(symbolUsageResolutionProvider, + documentManager)); + completionStrategyManager.registerCompletionStrategy( + new CD4AnalysisAssociationNavigationCompletionStrategy(symbolUsageResolutionProvider, + documentManager)); + } + +} diff --git a/language-server/src/main/java/de/monticore/cd4code/_lsp/features/syntax_highlighting/CD4CodeLexerProvider.java b/language-server/src/main/java/de/monticore/cd4code/_lsp/features/syntax_highlighting/CD4CodeLexerProvider.java new file mode 100644 index 000000000..724f72f5d --- /dev/null +++ b/language-server/src/main/java/de/monticore/cd4code/_lsp/features/syntax_highlighting/CD4CodeLexerProvider.java @@ -0,0 +1,24 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd4code._lsp.features.syntax_highlighting; + +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightAttributeTypeRule; +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightClassNameRule; +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightEnumMemberNameRule; +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightEnumNameRule; +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightInterfaceNameRule; +import de.monticore.cd4analysis._lsp.features.syntax_highlighting.rule.HighlightPackageNameRule; +import de.monticore.cd4code._lsp.language_access.CD4CodeLanguageAccess; + +public class CD4CodeLexerProvider extends CD4CodeLexerProviderTOP { + + public CD4CodeLexerProvider(CD4CodeLanguageAccess languageAccess) { + super(languageAccess); + addClassificationRule(new HighlightClassNameRule()); + addClassificationRule(new HighlightInterfaceNameRule()); + addClassificationRule(new HighlightAttributeTypeRule()); + addClassificationRule(new HighlightPackageNameRule()); + addClassificationRule(new HighlightEnumNameRule()); + addClassificationRule(new HighlightEnumMemberNameRule()); + } + +} diff --git a/language-server/src/test/java/de/monticore/cd4code/_lsp/CD4CodeLanguageServerTest.java b/language-server/src/test/java/de/monticore/cd4code/_lsp/CD4CodeLanguageServerTest.java new file mode 100644 index 000000000..f95319d94 --- /dev/null +++ b/language-server/src/test/java/de/monticore/cd4code/_lsp/CD4CodeLanguageServerTest.java @@ -0,0 +1,55 @@ +/* (c) https://github.com/MontiCore/monticore */ +package de.monticore.cd4code._lsp; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import de.mclsg.lsp.document_management.DocumentManager; +import de.mclsg.lsp.features.sematic_tokens.impl.SemanticTokenTypesWrapper; +import de.mclsg.lsp.modelpath.multiproject.ProjectLayoutBuilder; +import de.mclsg.lsp.util.AsyncUtilWithSyncExec; +import de.monticore.cd4analysis._lsp.MockLanguageClient; +import de.monticore.cd4code._lsp.features.syntax_highlighting.CD4CodeLexerProvider; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.eclipse.lsp4j.CodeLensParams; +import org.eclipse.lsp4j.InitializedParams; +import org.eclipse.lsp4j.TextDocumentIdentifier; +import org.junit.jupiter.api.Test; + +class CD4CodeLanguageServerTest { + + @Test + void indexesCodeModelAndReusesAnalysisFeatures() throws IOException { + AsyncUtilWithSyncExec.init(); + Path modelDirectory = Paths.get("src", "test", "resources", "cd4code"); + Path model = modelDirectory.resolve("SharedFeatures.cd"); + DocumentManager documentManager = new DocumentManager(); + CD4CodeLanguageServer server = new CD4CodeLanguageServerBuilder().layout( + new ProjectLayoutBuilder().projectpath(modelDirectory).build()).documentManager( + documentManager).build(); + server.connect(new MockLanguageClient()); + server.initialized(new InitializedParams()); + + String uri = model.toUri().toString(); + assertTrue(documentManager.getDocumentInformation(uri).isPresent()); + assertNotNull(documentManager.getDocumentInformation(uri).orElseThrow().ast); + + var lenses = server.getTextDocumentService().codeLens(new CodeLensParams( + new TextDocumentIdentifier(uri))).join(); + assertEquals(2, lenses.size()); + assertTrue(lenses.stream().allMatch(lens -> lens.getCommand().getTitle().equals( + "Part of 1 Association"))); + + var tokens = new CD4CodeLexerProvider(server.getLanguageAccess()).getTokensForInput(Files + .readString(model)); + assertFalse(tokens.isEmpty()); + assertTrue(tokens.stream().anyMatch(token -> token.getName().equals( + SemanticTokenTypesWrapper.Class.value))); + } + +} diff --git a/language-server/src/test/resources/cd4code/SharedFeatures.cd b/language-server/src/test/resources/cd4code/SharedFeatures.cd new file mode 100644 index 000000000..0f7dd7d15 --- /dev/null +++ b/language-server/src/test/resources/cd4code/SharedFeatures.cd @@ -0,0 +1,7 @@ +classdiagram SharedFeatures { + class Source { + public void run(); + } + class Target; + association Source -> Target; +}