From 5f2d875af3fe4d8e80910b5563a809ee125b4ba6 Mon Sep 17 00:00:00 2001 From: azerr Date: Sat, 5 Oct 2024 14:45:01 +0200 Subject: [PATCH] feat: Support documentSelector in registerCapability Fixes #517 Signed-off-by: azerr --- .../lsp4ij/DocumentContentSynchronizer.java | 58 +-- .../redhat/devtools/lsp4ij/LSPIJUtils.java | 17 - .../devtools/lsp4ij/LanguageServerItem.java | 385 ------------------ .../lsp4ij/LanguageServerWrapper.java | 113 ++--- .../features/AbstractLSPDocumentFeature.java | 1 - .../client/features/AbstractLSPFeature.java | 3 + .../client/features/LSPClientFeatures.java | 185 ++++++++- .../client/features/LSPCodeActionFeature.java | 37 +- .../client/features/LSPCodeLensFeature.java | 64 +-- .../client/features/LSPColorFeature.java | 39 -- .../client/features/LSPCompletionFeature.java | 33 +- .../features/LSPDeclarationFeature.java | 29 +- .../client/features/LSPDefinitionFeature.java | 29 +- .../client/features/LSPDiagnosticFeature.java | 4 + .../features/LSPDocumentColorFeature.java | 58 +++ .../features/LSPDocumentHighlightFeature.java | 28 +- .../features/LSPDocumentLinkFeature.java | 25 +- .../features/LSPDocumentSymbolFeature.java | 24 +- .../features/LSPFoldingRangeFeature.java | 29 +- .../client/features/LSPFormattingFeature.java | 48 ++- .../client/features/LSPHoverFeature.java | 42 +- .../features/LSPImplementationFeature.java | 26 +- .../client/features/LSPInlayHintFeature.java | 42 +- .../client/features/LSPReferencesFeature.java | 26 +- .../client/features/LSPRenameFeature.java | 38 +- .../features/LSPSemanticTokensFeature.java | 12 +- .../features/LSPSignatureHelpFeature.java | 28 +- .../features/LSPTypeDefinitionFeature.java | 25 +- .../client/features/LSPUsageFeature.java | 16 +- .../features/LSPWorkspaceSymbolFeature.java | 14 +- .../features/AbstractLSPGoToAction.java | 10 +- .../LSPLazyCodeActionIntentionAction.java | 4 +- .../features/codeLens/LSPCodeLensSupport.java | 11 +- .../features/color/LSPColorSupport.java | 4 +- .../declaration/LSPGoToDeclarationAction.java | 8 +- .../diagnostics/LSPDiagnosticAnnotator.java | 2 +- .../diagnostics/LSPDiagnosticsForServer.java | 13 +- .../LSPFormattingAndRangeBothService.java | 2 +- .../formatting/LSPFormattingOnlyService.java | 4 +- .../formatting/LSPFormattingSupport.java | 6 +- .../LSPGoToImplementationAction.java | 8 +- .../inlayhint/LSPInlayHintsProvider.java | 2 +- .../inlayhint/LSPInlayHintsSupport.java | 9 +- .../references/LSPGoToReferenceAction.java | 10 +- .../features/rename/LSPRenameHandler.java | 2 +- .../LSPGoToTypeDefinitionAction.java | 8 +- .../internal/ClientCapabilitiesFactory.java | 48 +-- .../CodeActionCapabilityRegistry.java | 90 ++++ .../CodeLensCapabilityRegistry.java | 82 ++++ .../CompletionCapabilityRegistry.java | 82 ++++ .../DeclarationCapabilityRegistry.java | 66 +++ .../DefinitionCapabilityRegistry.java | 66 +++ .../DocumentColorCapabilityRegistry.java | 66 +++ .../DocumentFormattingCapabilityRegistry.java | 66 +++ .../DocumentHighlightCapabilityRegistry.java | 67 +++ .../DocumentLinkCapabilityRegistry.java | 66 +++ ...mentRangeFormattingCapabilityRegistry.java | 67 +++ .../DocumentSymbolCapabilityRegistry.java | 66 +++ .../ExtendedDocumentSelector.java | 71 ++++ .../FoldingRangeCapabilityRegistry.java | 66 +++ .../capabilities/HoverCapabilityRegistry.java | 66 +++ .../ImplementationCapabilityRegistry.java | 66 +++ .../InlayHintCapabilityRegistry.java | 87 ++++ .../ReferencesCapabilityRegistry.java | 66 +++ .../RenameCapabilityRegistry.java | 87 ++++ .../SignatureHelpCapabilityRegistry.java | 66 +++ .../TextDocumentServerCapabilityRegistry.java | 149 +++++++ .../TypeDefinitionCapabilityRegistry.java | 66 +++ .../definition/LanguageServerDefinition.java | 166 +++++++- .../lsp4ij/usages/LSPUsageSupport.java | 11 +- 70 files changed, 2576 insertions(+), 804 deletions(-) delete mode 100644 src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPColorFeature.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDocumentColorFeature.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeActionCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeLensCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CompletionCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DeclarationCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DefinitionCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentColorCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentFormattingCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentHighlightCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentLinkCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentRangeFormattingCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentSymbolCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ExtendedDocumentSelector.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/FoldingRangeCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/HoverCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ImplementationCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/InlayHintCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ReferencesCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/RenameCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/SignatureHelpCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TextDocumentServerCapabilityRegistry.java create mode 100644 src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TypeDefinitionCapabilityRegistry.java diff --git a/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java b/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java index 9b22ad5b1..89a110ac0 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/DocumentContentSynchronizer.java @@ -10,20 +10,17 @@ ******************************************************************************/ package com.redhat.devtools.lsp4ij; -import com.intellij.lang.Language; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; -import com.intellij.openapi.fileEditor.FileDocumentManager; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDocumentManager; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.messages.Either; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.net.URI; import java.util.ArrayList; @@ -50,8 +47,9 @@ public class DocumentContentSynchronizer implements DocumentListener { public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServerWrapper, @NotNull URI fileUri, + @NotNull VirtualFile file, @NotNull Document document, - TextDocumentSyncKind syncKind) { + @Nullable TextDocumentSyncKind syncKind) { this.languageServerWrapper = languageServerWrapper; this.fileUri = fileUri.toASCIIString(); this.syncKind = syncKind != null ? syncKind : TextDocumentSyncKind.Full; @@ -62,7 +60,7 @@ public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServer textDocument.setUri(this.fileUri); textDocument.setText(document.getText()); - @NotNull String languageId = getLanguageId(document, languageServerWrapper); + @NotNull String languageId = languageServerWrapper.getServerDefinition().getLanguageId(file, languageServerWrapper.getProject()); textDocument.setLanguageId(languageId); textDocument.setVersion(++version); didOpenFuture = languageServerWrapper @@ -89,54 +87,6 @@ public DocumentContentSynchronizer(@NotNull LanguageServerWrapper languageServer changeEvents = new ArrayList<>(); } - /** - * Returns the LSP language id defined in mapping otherwise the {@link Language#getID()} otherwise the {@link FileType#getName()} otherwise 'unknown'. - * - * @param document the document. - * @param languageServer the language server. - * @return the LSP language id. - */ - private static @NotNull String getLanguageId(@NotNull Document document, @NotNull LanguageServerWrapper languageServer) { - VirtualFile file = FileDocumentManager.getInstance().getFile(document); - if (file == null) { - return FileTypes.UNKNOWN.getName().toLowerCase(); - } - - Project project = languageServer.getProject(); - - // 1. Try to get the LSP languageId by using language mapping - Language language = LSPIJUtils.getFileLanguage(file, project); - String languageId = languageServer.getLanguageId(language); - if (languageId != null) { - return languageId; - } - - // 2. Try to get the LSP languageId by using the fileType mapping - FileType fileType = file.getFileType(); - languageId = languageServer.getLanguageId(fileType); - if (languageId != null) { - return languageId; - } - - // 3. Try to get the LSP languageId by using the file name pattern mapping - languageId = languageServer.getLanguageId(file.getName()); - if (languageId != null) { - return languageId; - } - - // At this step there is no mapping - - // We return the language Id if it exists or file type name - // with 'lower case' to try to map the recommended languageId specified at - // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem - if (language != null) { - // The language exists, use its ID with lower case - return language.getID().toLowerCase(); - } - // Returns the existing file type or 'unknown' with lower case - return file.getName().toLowerCase(); - } - @Override public void documentChanged(@NotNull DocumentEvent event) { DocumentListener.super.documentChanged(event); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java b/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java index 80dc64844..f744e274c 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LSPIJUtils.java @@ -321,23 +321,6 @@ private static Language doGetFileLanguage(@NotNull VirtualFile file, @NotNull Pr return LanguageUtil.getLanguageForPsi(project, file); } - private static T toTextDocumentPositionParamsCommon(T param, int offset, Document document) { - Position start = toPosition(offset, document); - param.setPosition(start); - TextDocumentIdentifier id = new TextDocumentIdentifier(); - URI uri = toUri(document); - if (uri != null) { - id.setUri(uri.toASCIIString()); - } - param.setTextDocument(id); - return param; - } - - public static HoverParams toHoverParams(int offset, Document document) { - return toTextDocumentPositionParamsCommon(new HoverParams(), offset, document); - } - - /** * Returns the Uri of the virtual file corresponding to the specified document. * diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerItem.java b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerItem.java index 36845cb7a..7f0516d11 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerItem.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerItem.java @@ -112,96 +112,6 @@ public ServerCapabilities getServerCapabilities() { return serverCapabilities; } - /** - * Returns true if the language server can support resolve completion and false otherwise. - * - * @return true if the language server can support resolve completion and false otherwise. - */ - public boolean isResolveCompletionSupported() { - return isResolveCompletionSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support signature help and false otherwise. - * - * @return true if the language server can support signature help and false otherwise. - */ - public boolean isSignatureHelpSupported() { - return isSignatureHelpSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support resolve code lens and false otherwise. - * - * @return true if the language server can support resolve code lens and false otherwise. - */ - public boolean isResolveCodeLensSupported() { - return isResolveCodeLensSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support resolve inlay hint and false otherwise. - * - * @return true if the language server can support resolve inlay hint and false otherwise. - */ - public boolean isResolveInlayHintSupported() { - return isResolveInlayHintSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support references and false otherwise. - * - * @return true if the language server can support references and false otherwise. - */ - public boolean isReferencesSupported() { - return isReferencesSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support implementation and false otherwise. - * - * @return true if the language server can support implementation and false otherwise. - */ - public boolean isImplementationSupported() { - return isImplementationSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support declaration and false otherwise. - * - * @return true if the language server can support declaration and false otherwise. - */ - public boolean isDeclarationSupported() { - return isDeclarationSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support definition and false otherwise. - * - * @return true if the language server can support definition and false otherwise. - */ - public boolean isDefinitionSupported() { - return isDefinitionSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support type definition and false otherwise. - * - * @return true if the language server can support type definition and false otherwise. - */ - public boolean isTypeDefinitionSupported() { - return isTypeDefinitionSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support formatting and false otherwise. - * - * @return true if the language server can support formatting and false otherwise. - */ - public boolean isDocumentFormattingSupported() { - return isDocumentFormattingSupported(getServerCapabilities()); - } - /** * Returns true if the language server can support range formatting and false otherwise. * @@ -211,207 +121,6 @@ public boolean isDocumentRangeFormattingSupported() { return isDocumentRangeFormattingSupported(getServerCapabilities()); } - /** - * Returns true if the language server can support completion and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support completion and false otherwise. - */ - public static boolean isCompletionSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - serverCapabilities.getCompletionProvider() != null; - } - - /** - * Returns true if the language server can support resolve completion and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support resolve completion and false otherwise. - */ - public static boolean isResolveCompletionSupported(@Nullable ServerCapabilities serverCapabilities) { - var completionProvider = serverCapabilities != null ? serverCapabilities.getCompletionProvider() : null; - return completionProvider != null && hasCapability(completionProvider.getResolveProvider()); - } - - /** - * Returns true if the language server can support signature help and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support signature help and false otherwise. - */ - public static boolean isSignatureHelpSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - serverCapabilities.getSignatureHelpProvider() != null; - } - - /** - * Returns true if the language server can support code lens and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support code lens and false otherwise. - */ - public static boolean isCodeLensSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - serverCapabilities.getCodeLensProvider() != null; - } - - /** - * Returns true if the language server can support resolve code lens and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support resolve code lens and false otherwise. - */ - public static boolean isResolveCodeLensSupported(@Nullable ServerCapabilities serverCapabilities) { - var codeLensProvider = serverCapabilities != null ? serverCapabilities.getCodeLensProvider() : null; - return codeLensProvider != null && hasCapability(codeLensProvider.getResolveProvider()); - } - - /** - * Returns true if the language server can support inlay hint and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support inlay hint and false otherwise. - */ - public static boolean isInlayHintSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getInlayHintProvider()); - } - - /** - * Returns true if the language server can support resolve inlay hint and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support resolve inlay hint and false otherwise. - */ - public static boolean isResolveInlayHintSupported(@Nullable ServerCapabilities serverCapabilities) { - var inlayHintProvider = serverCapabilities != null ? serverCapabilities.getInlayHintProvider() : null; - if (inlayHintProvider != null && inlayHintProvider.isRight()) { - return hasCapability(inlayHintProvider.getRight().getResolveProvider()); - } - return false; - } - - /** - * Returns true if the language server can support color and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support color and false otherwise. - */ - public static boolean isColorSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getColorProvider()); - } - - /** - * Returns true if the language server can support declaration and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support declaration and false otherwise. - */ - public static boolean isDeclarationSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getDeclarationProvider()); - } - - /** - * Returns true if the language server can support definition and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support definition and false otherwise. - */ - public static boolean isDefinitionSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getDefinitionProvider()); - } - - /** - * Returns true if the language server can support type definition and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support type definition and false otherwise. - */ - public static boolean isTypeDefinitionSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getTypeDefinitionProvider()); - } - - /** - * Returns true if the language server can support document highlight and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support document highlight and false otherwise. - */ - public static boolean isDocumentHighlightSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getDocumentHighlightProvider()); - } - - /** - * Returns true if the language server can support document link and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support document link and false otherwise. - */ - public static boolean isDocumentLinkSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - serverCapabilities.getDocumentLinkProvider() != null; - } - - /** - * Returns true if the language server can support document link and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support document link and false otherwise. - */ - public static boolean isHoverSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getHoverProvider()); - } - - /** - * Returns true if the language server can support references and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support references and false otherwise. - */ - public static boolean isReferencesSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getReferencesProvider()); - } - - /** - * Returns true if the language server can support references and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support references and false otherwise. - */ - public static boolean isImplementationSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getImplementationProvider()); - } - - /** - * Returns true if the language server can support folding range and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support folding range and false otherwise. - */ - public static boolean isFoldingRangeSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getFoldingRangeProvider()); - } - - /** - * Returns true if the language server can support formatting and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support formatting and false otherwise. - */ - public static boolean isDocumentFormattingSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getDocumentFormattingProvider()); - } - /** * Returns true if the language server can support range formatting and false otherwise. * @@ -423,62 +132,6 @@ public static boolean isDocumentRangeFormattingSupported(@Nullable ServerCapabil hasCapability(serverCapabilities.getDocumentRangeFormattingProvider()); } - /** - * Returns true if the language server can support code action and false otherwise. - * - * @return true if the language server can support code action and false otherwise. - */ - public boolean isCodeActionSupported() { - return isCodeActionSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support code action and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support code action and false otherwise. - */ - public static boolean isCodeActionSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getCodeActionProvider()); - } - - /** - * Returns true if the language server can support resolve code action and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support resolve code action and false otherwise. - */ - public static boolean isCodeActionResolveSupported(@Nullable ServerCapabilities serverCapabilities) { - Either codeActionProvider = serverCapabilities != null ? serverCapabilities.getCodeActionProvider() : null; - if (codeActionProvider != null && codeActionProvider.isRight()) { - return hasCapability(codeActionProvider.getRight().getResolveProvider()); - } - return false; - } - - /** - * Returns true if the language server can support semantic tokens and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support semantic tokens and false otherwise. - */ - public static boolean isSemanticTokensSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - serverCapabilities.getSemanticTokensProvider() != null; - } - - /** - * Returns true if the language server can support rename and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support rename and false otherwise. - */ - public static boolean isRenameSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getRenameProvider()); - } - /** * Returns true if the language server can support prepare rename and false otherwise. * @@ -564,42 +217,4 @@ public SemanticTokensColorsProvider getSemanticTokensColorsProvider() { return getClientFeatures().getSemanticTokensFeature(); } - /** - * Returns true if the language server can support document symbol and false otherwise. - * - * @return true if the language server can support document symbol and false otherwise. - */ - public static boolean isDocumentSymbolSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getDocumentSymbolProvider()); - } - - /** - * Returns true if the language server can support workspace symbol and false otherwise. - * - * @return true if the language server can support workspace symbol and false otherwise. - */ - public boolean isWorkspaceSymbolSupported() { - return isWorkspaceSymbolSupported(getServerCapabilities()); - } - - /** - * Returns true if the language server can support workspace symbol and false otherwise. - * - * @param serverCapabilities the server capabilities. - * @return true if the language server can support workspace symbol and false otherwise. - */ - public static boolean isWorkspaceSymbolSupported(@Nullable ServerCapabilities serverCapabilities) { - return serverCapabilities != null && - hasCapability(serverCapabilities.getWorkspaceSymbolProvider()); - } - - public static boolean isUsageSupported(ServerCapabilities serverCapabilities) { - return LanguageServerItem.isDeclarationSupported(serverCapabilities) || - LanguageServerItem.isTypeDefinitionSupported(serverCapabilities) || - LanguageServerItem.isDefinitionSupported(serverCapabilities) || - LanguageServerItem.isReferencesSupported(serverCapabilities) || - LanguageServerItem.isImplementationSupported(serverCapabilities); - } - } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java index 6c235c0fc..ac49f9bf2 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/LanguageServerWrapper.java @@ -12,12 +12,10 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.gson.JsonObject; -import com.intellij.lang.Language; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileEditor.FileEditorManagerListener; -import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vfs.VirtualFile; @@ -26,12 +24,13 @@ import com.intellij.psi.PsiFile; import com.intellij.util.messages.MessageBusConnection; import com.redhat.devtools.lsp4ij.client.LanguageClientImpl; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.files.operations.FileOperationsManager; import com.redhat.devtools.lsp4ij.internal.ClientCapabilitiesFactory; import com.redhat.devtools.lsp4ij.lifecycle.LanguageServerLifecycleManager; import com.redhat.devtools.lsp4ij.lifecycle.NullLanguageServerLifecycleManager; import com.redhat.devtools.lsp4ij.server.*; -import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import com.redhat.devtools.lsp4ij.server.capabilities.TextDocumentServerCapabilityRegistry; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; import org.eclipse.lsp4j.*; import org.eclipse.lsp4j.jsonrpc.Launcher; @@ -228,9 +227,7 @@ public synchronized void start() throws LanguageServerException { initParams.setInitializationOptions(provider.getInitializationOptions(rootURI)); // Add error log - provider.addLogErrorHandler(error -> { - ServerMessageHandler.logMessage(this.getServerDefinition(), new MessageParams(MessageType.Error, error), getProject()); - }); + provider.addLogErrorHandler(error -> ServerMessageHandler.logMessage(this.getServerDefinition(), new MessageParams(MessageType.Error, error), getProject())); // Starting process... updateStatus(ServerStatus.starting); @@ -312,6 +309,7 @@ public synchronized void start() throws LanguageServerException { fileOperationsManager = new FileOperationsManager(this); fileOperationsManager.setServerCapabilities(serverCapabilities); + getClientFeatures().setServerCapabilities(serverCapabilities); updateStatus(ServerStatus.started); getLanguageServerLifecycleManager().onStatusChanged(this); @@ -635,7 +633,7 @@ private CompletableFuture connect(@NotNull URI fileUri, return CompletableFuture.completedFuture(null); } - VirtualFile fileToConnect = optionalFile; + final @NotNull VirtualFile fileToConnect = optionalFile; return initializeFuture.thenComposeAsync(theVoid -> { // Here, the "initialize" future is initialized @@ -656,7 +654,7 @@ private CompletableFuture connect(@NotNull URI fileUri, return ls2; } - DocumentContentSynchronizer synchronizer = createDocumentContentSynchronizer(fileUri, document); + DocumentContentSynchronizer synchronizer = createDocumentContentSynchronizer(fileUri, fileToConnect, document); document.addDocumentListener(synchronizer); LSPVirtualFileData data = new LSPVirtualFileData(new LanguageServerItem(languageServer, this), fileToConnect, synchronizer); LanguageServerWrapper.this.connectedDocuments.put(fileUri, data); @@ -692,6 +690,7 @@ private CompletableFuture getLanguageServerWhenDidOpen(Completab @NotNull private DocumentContentSynchronizer createDocumentContentSynchronizer(@NotNull URI fileUri, + @NotNull VirtualFile file, @NotNull Document document) { Either syncOptions = initializeFuture == null ? null : this.serverCapabilities.getTextDocumentSync(); @@ -703,7 +702,7 @@ private DocumentContentSynchronizer createDocumentContentSynchronizer(@NotNull U syncKind = syncOptions.getLeft(); } } - return new DocumentContentSynchronizer(this, fileUri, document, syncKind); + return new DocumentContentSynchronizer(this, fileUri, file, document, syncKind); } public void disconnect(URI path, boolean stopIfNoOpenedFiles) { @@ -838,43 +837,6 @@ public ServerCapabilities getServerCapabilitiesSync() { return serverCapabilities; } - /** - * @return The language ID that this wrapper is dealing with if defined in the - * language mapping for the language server - */ - @Nullable - public String getLanguageId(@Nullable Language language) { - while (language != null) { - String languageId = serverDefinition.getLanguageId(language); - if (languageId != null) { - return languageId; - } - language = language.getBaseLanguage(); - } - return null; - } - - /** - * @return The language ID that this wrapper is dealing with if defined in the - * file type mapping for the language server - */ - @Nullable - public String getLanguageId(@Nullable FileType fileType) { - if (fileType == null) { - return null; - } - return serverDefinition.getLanguageId(fileType); - } - - /** - * @return The language ID that this wrapper is dealing with if defined in the - * file type mapping for the language server - */ - @Nullable - public String getLanguageId(@NotNull String filename) { - return serverDefinition.getLanguageId(filename); - } - public void registerCapability(RegistrationParams params) { initializeFuture.thenRun(() -> { params.getRegistrations().forEach(reg -> { @@ -917,47 +879,17 @@ public void registerCapability(RegistrationParams params) { } catch (Exception e) { LOGGER.error("Error while getting 'workspace/executeCommand' capability", e); } - } else if (LSPRequestConstants.TEXT_DOCUMENT_FORMATTING.equals(reg.getMethod())) { - // register 'textDocument/formatting' capability - final Either documentFormattingProvider = serverCapabilities.getDocumentFormattingProvider(); - if (documentFormattingProvider == null || documentFormattingProvider.isLeft()) { - serverCapabilities.setDocumentFormattingProvider(Boolean.TRUE); - addRegistration(reg, () -> serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider)); - } else { - serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider.getRight()); - addRegistration(reg, () -> serverCapabilities.setDocumentFormattingProvider(documentFormattingProvider)); - } - } else if (LSPRequestConstants.TEXT_DOCUMENT_RANGE_FORMATTING.equals(reg.getMethod())) { - // register 'textDocument/rangeFormatting' capability - final Either documentRangeFormattingProvider = serverCapabilities.getDocumentRangeFormattingProvider(); - if (documentRangeFormattingProvider == null || documentRangeFormattingProvider.isLeft()) { - serverCapabilities.setDocumentRangeFormattingProvider(Boolean.TRUE); - addRegistration(reg, () -> serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider)); - } else { - serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider.getRight()); - addRegistration(reg, () -> serverCapabilities.setDocumentRangeFormattingProvider(documentRangeFormattingProvider)); - } - } else if (LSPRequestConstants.TEXT_DOCUMENT_CODE_ACTION.equals(reg.getMethod())) { + } else { + String method = reg.getMethod(); try { - // Get old 'textDocument/codeAction' capability - final Either beforeRegistration = serverCapabilities.getCodeActionProvider(); - - // Register new 'textDocument/codeAction' capability - CodeActionRegistrationOptions options = JSONUtils.getLsp4jGson() - .fromJson((JsonObject) reg.getRegisterOptions(), - CodeActionRegistrationOptions.class); - CodeActionOptions codeActionOptions = new CodeActionOptions(); - codeActionOptions.setCodeActionKinds(options.getCodeActionKinds()); - codeActionOptions.setResolveProvider(options.getResolveProvider()); - // TODO: manage CodeActionRegistrationOptions#getDocumentSelector() - serverCapabilities.setCodeActionProvider(Either.forRight(codeActionOptions)); - - // Add registration handler to: - // - unregister the new 'textDocument/codeAction' capability - // - register the old 'textDocument/codeAction' capability - addRegistration(reg, () -> serverCapabilities.setCodeActionProvider(beforeRegistration)); + final TextDocumentServerCapabilityRegistry registry = getClientFeatures().getCapabilityRegistry(method); + if (registry != null) { + // register 'textDocument/*' capability + final TextDocumentRegistrationOptions options = registry.registerCapability((JsonObject) reg.getRegisterOptions()); + addRegistration(reg, () -> registry.unregisterCapability(options)); + } } catch (Exception e) { - LOGGER.error("Error while getting 'textDocument/codeAction' capability", e); + LOGGER.error("Error while getting '" + method + "' capability", e); } } }); @@ -1010,13 +942,13 @@ synchronized void registerCommands(List newCommands) { public void unregisterCapability(UnregistrationParams params) { params.getUnregisterations().forEach(reg -> { String id = reg.getId(); - Runnable unregistrator; + Runnable unregisters; synchronized (dynamicRegistrations) { - unregistrator = dynamicRegistrations.get(id); + unregisters = dynamicRegistrations.get(id); dynamicRegistrations.remove(id); } - if (unregistrator != null) { - unregistrator.run(); + if (unregisters != null) { + unregisters.run(); } }); } @@ -1193,11 +1125,12 @@ public LSPClientFeatures getClientFeatures() { } private synchronized LSPClientFeatures getOrCreateClientFeatures() { - if (clientFeatures != null) { + if (clientFeatures != null) { return clientFeatures; } LSPClientFeatures clientFeatures = getServerDefinition().createClientFeatures(); clientFeatures.setServerWrapper(this); return clientFeatures; } + } \ No newline at end of file diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPDocumentFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPDocumentFeature.java index 026931eda..22790edbe 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPDocumentFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPDocumentFeature.java @@ -43,7 +43,6 @@ public boolean isEnabled(@NotNull PsiFile file) { *

* * @param file the file. - * * @return true if the LSP feature is supported for the given file and false otherwise. */ public abstract boolean isSupported(@NotNull PsiFile file); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPFeature.java index 953546c34..f5c553555 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/AbstractLSPFeature.java @@ -14,6 +14,7 @@ import com.intellij.openapi.project.Project; import com.redhat.devtools.lsp4ij.ServerStatus; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; +import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.services.LanguageServer; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -100,4 +101,6 @@ public final void setClientFeatures(@NotNull LSPClientFeatures clientFeatures) { public void dispose() { clientFeatures = null; } + + public abstract void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java index e03eea162..8ada6870f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPClientFeatures.java @@ -12,9 +12,13 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.redhat.devtools.lsp4ij.LSPRequestConstants; import com.redhat.devtools.lsp4ij.LanguageServerWrapper; import com.redhat.devtools.lsp4ij.ServerStatus; +import com.redhat.devtools.lsp4ij.server.capabilities.TextDocumentServerCapabilityRegistry; import com.redhat.devtools.lsp4ij.server.definition.LanguageServerDefinition; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentRegistrationOptions; import org.eclipse.lsp4j.services.LanguageServer; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -32,7 +36,7 @@ public class LSPClientFeatures implements Disposable { private LSPCodeLensFeature codeLensFeature; - private LSPColorFeature colorFeature; + private LSPDocumentColorFeature documentColorFeature; private LSPCompletionFeature completionFeature; @@ -71,7 +75,7 @@ public class LSPClientFeatures implements Disposable { private LSPUsageFeature usageFeature; private LSPWorkspaceSymbolFeature workspaceSymbolFeature; - + /** * Returns the project. * @@ -187,22 +191,22 @@ public final LSPClientFeatures setCodeLensFeature(@NotNull LSPCodeLensFeature co * @return the LSP color feature. */ @NotNull - public final LSPColorFeature getColorFeature() { - if (colorFeature == null) { - setColorFeature(new LSPColorFeature()); + public final LSPDocumentColorFeature getDocumentColorFeature() { + if (documentColorFeature == null) { + setDocumentColorFeature(new LSPDocumentColorFeature()); } - return colorFeature; + return documentColorFeature; } /** * Initialize the LSP color feature. * - * @param colorFeature the LSP color feature. + * @param documentColorFeature the LSP color feature. * @return the LSP client features. */ - public final LSPClientFeatures setColorFeature(@NotNull LSPColorFeature colorFeature) { - colorFeature.setClientFeatures(this); - this.colorFeature = colorFeature; + public final LSPClientFeatures setDocumentColorFeature(@NotNull LSPDocumentColorFeature documentColorFeature) { + documentColorFeature.setClientFeatures(this); + this.documentColorFeature = documentColorFeature; return this; } @@ -713,8 +717,8 @@ public void dispose() { if (codeLensFeature != null) { codeLensFeature.dispose(); } - if (colorFeature != null) { - colorFeature.dispose(); + if (documentColorFeature != null) { + documentColorFeature.dispose(); } if (completionFeature != null) { completionFeature.dispose(); @@ -780,4 +784,161 @@ public void dispose() { workspaceSymbolFeature.dispose(); } } + + public void setServerCapabilities(@NotNull ServerCapabilities serverCapabilities) { + if (codeActionFeature != null) { + codeActionFeature.setServerCapabilities(serverCapabilities); + } + if (codeLensFeature != null) { + codeLensFeature.setServerCapabilities(serverCapabilities); + } + if (completionFeature != null) { + completionFeature.setServerCapabilities(serverCapabilities); + } + if (codeActionFeature != null) { + codeActionFeature.setServerCapabilities(serverCapabilities); + } + if (codeActionFeature != null) { + codeActionFeature.setServerCapabilities(serverCapabilities); + } + if (declarationFeature != null) { + declarationFeature.setServerCapabilities(serverCapabilities); + } + if (definitionFeature != null) { + definitionFeature.setServerCapabilities(serverCapabilities); + } + if (documentColorFeature != null) { + documentColorFeature.setServerCapabilities(serverCapabilities); + } + if (documentLinkFeature != null) { + documentLinkFeature.setServerCapabilities(serverCapabilities); + } + if (documentHighlightFeature != null) { + documentHighlightFeature.setServerCapabilities(serverCapabilities); + } + if (documentSymbolFeature != null) { + documentSymbolFeature.setServerCapabilities(serverCapabilities); + } + if (diagnosticFeature != null) { + diagnosticFeature.setServerCapabilities(serverCapabilities); + } + if (foldingRangeFeature != null) { + foldingRangeFeature.setServerCapabilities(serverCapabilities); + } + if (formattingFeature != null) { + formattingFeature.setServerCapabilities(serverCapabilities); + } + if (implementationFeature != null) { + implementationFeature.setServerCapabilities(serverCapabilities); + } + if (inlayHintFeature != null) { + inlayHintFeature.setServerCapabilities(serverCapabilities); + } + if (hoverFeature != null) { + hoverFeature.setServerCapabilities(serverCapabilities); + } + if (referencesFeature != null) { + referencesFeature.setServerCapabilities(serverCapabilities); + } + if (renameFeature != null) { + renameFeature.setServerCapabilities(serverCapabilities); + } + if (semanticTokensFeature != null) { + semanticTokensFeature.setServerCapabilities(serverCapabilities); + } + if (signatureHelpFeature != null) { + signatureHelpFeature.setServerCapabilities(serverCapabilities); + } + if (typeDefinitionFeature != null) { + typeDefinitionFeature.setServerCapabilities(serverCapabilities); + } + if (usageFeature != null) { + usageFeature.setServerCapabilities(serverCapabilities); + } + if (workspaceSymbolFeature != null) { + workspaceSymbolFeature.setServerCapabilities(serverCapabilities); + } + } + + @Nullable + public TextDocumentServerCapabilityRegistry getCapabilityRegistry(String method) { + if (LSPRequestConstants.TEXT_DOCUMENT_CODE_ACTION.equals(method)) { + // register 'textDocument/codeAction' capability + return getCodeActionFeature().getCodeActionCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_CODE_LENS.equals(method)) { + // register 'textDocument/codeLens' capability + return getCodeLensFeature().getCodeLensCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_COLOR.equals(method)) { + // register 'textDocument/documentColor' capability + return getDocumentColorFeature().getDocumentColorCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_COMPLETION.equals(method)) { + // register 'textDocument/completion' capability + return getCompletionFeature().getCompletionCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DECLARATION.equals(method)) { + // register 'textDocument/declaration' capability + return getDeclarationFeature().getDeclarationCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DEFINITION.equals(method)) { + // register 'textDocument/definition' capability + return getDefinitionFeature().getDefinitionCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT.equals(method)) { + // register 'textDocument/documentHighlight' capability + return getDocumentHighlightFeature().getDocumentHighlightCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_LINK.equals(method)) { + // register 'textDocument/documentHighLink' capability + return getDocumentLinkFeature().getDocumentLinkCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_DOCUMENT_SYMBOL.equals(method)) { + // register 'textDocument/documentSymbol' capability + return getDocumentSymbolFeature().getDocumentSymbolCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_FOLDING_RANGE.equals(method)) { + // register 'textDocument/foldingRange' capability + return getFoldingRangeFeature().getFoldingRangeCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_FORMATTING.equals(method)) { + // register 'textDocument/formatting' capability + return getFormattingFeature().getFormattingCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_RANGE_FORMATTING.equals(method)) { + // register 'textDocument/rangeFormatting' capability + return getFormattingFeature().getRangeFormattingCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_HOVER.equals(method)) { + // register 'textDocument/hover' capability + return getHoverFeature().getHoverCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_IMPLEMENTATION.equals(method)) { + // register 'textDocument/implementation' capability + return getImplementationFeature().getImplementationCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_INLAY_HINT.equals(method)) { + // register 'textDocument/inlayHint' capability + return getInlayHintFeature().getInlayHintCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_REFERENCES.equals(method)) { + // register 'textDocument/references' capability + return getReferencesFeature().getReferencesCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_RENAME.equals(method)) { + // register 'textDocument/rename' capability + return getRenameFeature().getRenameCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_SIGNATURE_HELP.equals(method)) { + // register 'textDocument/signatureHelp' capability + return getSignatureHelpFeature().getSignatureHelpCapabilityRegistry(); + } + if (LSPRequestConstants.TEXT_DOCUMENT_TYPE_DEFINITION.equals(method)) { + // register 'textDocument/typeDefinition' capability + return getTypeDefinitionFeature().getTypeDefinitionCapabilityRegistry(); + } + return null; + } + } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeActionFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeActionFeature.java index 458827a51..07db8121f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeActionFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeActionFeature.java @@ -12,11 +12,12 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LanguageServerBundle; -import com.redhat.devtools.lsp4ij.LanguageServerItem; import com.redhat.devtools.lsp4ij.internal.StringUtils; +import com.redhat.devtools.lsp4ij.server.capabilities.CodeActionCapabilityRegistry; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionKind; import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.ServerCapabilities; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,8 @@ @ApiStatus.Experimental public class LSPCodeActionFeature extends AbstractLSPDocumentFeature { + private CodeActionCapabilityRegistry codeActionCapabilityRegistry; + @Override public boolean isSupported(@NotNull PsiFile file) { return isCodeActionSupported(file); @@ -35,14 +38,24 @@ public boolean isSupported(@NotNull PsiFile file) { /** * Returns true if the file associated with a language server can support codeAction and false otherwise. * - * @param file the file. + * @param file the file. * @return true if the file associated with a language server can support codeAction and false otherwise. */ public boolean isCodeActionSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isCodeActionSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + return getCodeActionCapabilityRegistry().isCodeActionSupported(file); + } + + /** + * Returns true if the file associated with a language server can support resolve codeAction and false otherwise. + * + * @param file the file. + * @return true if the file associated with a language server can support resolve codeAction and false otherwise. + */ + public boolean isResolveCodeActionSupported(@NotNull PsiFile file) { + return getCodeActionCapabilityRegistry().isResolveCodeActionSupported(file); } + /** * Returns true if quick fixes are enabled and false otherwise. * @@ -125,4 +138,20 @@ public String getText(@NotNull Command command) { public String getFamilyName(@NotNull Command command) { return "LSP Command"; } + + public CodeActionCapabilityRegistry getCodeActionCapabilityRegistry() { + if (codeActionCapabilityRegistry == null) { + var clientFeatures = getClientFeatures(); + codeActionCapabilityRegistry = new CodeActionCapabilityRegistry(clientFeatures); + codeActionCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync()); + } + return codeActionCapabilityRegistry; + } + + @Override + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + if(codeActionCapabilityRegistry != null) { + codeActionCapabilityRegistry.setServerCapabilities(serverCapabilities); + } + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeLensFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeLensFeature.java index c86e50e18..14102efad 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeLensFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCodeLensFeature.java @@ -19,8 +19,10 @@ import com.redhat.devtools.lsp4ij.commands.CommandExecutor; import com.redhat.devtools.lsp4ij.commands.LSPCommandContext; import com.redhat.devtools.lsp4ij.internal.StringUtils; +import com.redhat.devtools.lsp4ij.server.capabilities.CodeLensCapabilityRegistry; import org.eclipse.lsp4j.CodeLens; import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.ServerCapabilities; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,6 +35,33 @@ @ApiStatus.Experimental public class LSPCodeLensFeature extends AbstractLSPDocumentFeature { + private CodeLensCapabilityRegistry codeLensCapabilityRegistry; + + @Override + public boolean isSupported(@NotNull PsiFile file) { + return isCodeLensSupported(file); + } + + /** + * Returns true if the file associated with a language server can support codelens and false otherwise. + * + * @param file the Psi file. + * @return true if the file associated with a language server can support codelens and false otherwise. + */ + public boolean isCodeLensSupported(@NotNull PsiFile file) { + return getCodeLensCapabilityRegistry().isCodeLensSupported(file); + } + + /** + * Returns true if the file associated with a language server can support resolve codelens and false otherwise. + * + * @param file the file. + * @return true if the file associated with a language server can support resolve codelens and false otherwise. + */ + public boolean isResolveCodeLensSupported(@NotNull PsiFile file) { + return getCodeLensCapabilityRegistry().isResolveCodeLensSupported(file); + } + public static class LSPCodeLensContext { private final @NotNull PsiFile psiFile; private final @NotNull LanguageServerItem languageServer; @@ -120,30 +149,19 @@ public String getText(@NotNull CodeLens codeLens) { return command.getTitle(); } - - @Override - public boolean isSupported(@NotNull PsiFile file) { - return isCodeLensSupported(file); - } - - /** - * Returns true if the file associated with a language server can support codelens and false otherwise. - * - * @param file the file. - * @return true if the file associated with a language server can support codelens and false otherwise. - */ - public boolean isCodeLensSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isCodeLensSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + public CodeLensCapabilityRegistry getCodeLensCapabilityRegistry() { + if (codeLensCapabilityRegistry == null) { + var clientFeatures = getClientFeatures(); + codeLensCapabilityRegistry = new CodeLensCapabilityRegistry(clientFeatures); + codeLensCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync()); + } + return codeLensCapabilityRegistry; } - /** - * Returns true if the file associated with a language server can support resolve codelens and false otherwise. - * - * @param file the file. - * @return true if the file associated with a language server can support resolve codelens and false otherwise. - */ - public boolean isResolveCodeLensSupported(@NotNull PsiFile file) { - return LanguageServerItem.isResolveCodeLensSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + @Override + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + if(codeLensCapabilityRegistry != null) { + codeLensCapabilityRegistry.setServerCapabilities(serverCapabilities); + } } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPColorFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPColorFeature.java deleted file mode 100644 index a25671127..000000000 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPColorFeature.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * 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 https://www.eclipse.org/legal/epl-v20.html - * - * Contributors: - * Red Hat, Inc. - initial API and implementation - ******************************************************************************/ -package com.redhat.devtools.lsp4ij.client.features; - -import com.intellij.psi.PsiFile; -import com.redhat.devtools.lsp4ij.LanguageServerItem; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -/** - * LSP color feature. - */ -@ApiStatus.Experimental -public class LSPColorFeature extends AbstractLSPDocumentFeature { - - @Override - public boolean isSupported(@NotNull PsiFile file) { - return isColorSupported(file); - } - - /** - * Returns true if the file associated with a language server can support color and false otherwise. - * - * @param file the file. - * @return true if the file associated with a language server can support color and false otherwise. - */ - public boolean isColorSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isColorSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); - } -} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java index 4647400be..79a63ae74 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPCompletionFeature.java @@ -19,10 +19,12 @@ import com.redhat.devtools.lsp4ij.LanguageServerItem; import com.redhat.devtools.lsp4ij.features.completion.CompletionPrefix; import com.redhat.devtools.lsp4ij.internal.StringUtils; +import com.redhat.devtools.lsp4ij.server.capabilities.CompletionCapabilityRegistry; import com.redhat.devtools.lsp4ij.ui.IconMapper; import org.eclipse.lsp4j.CompletionItem; import org.eclipse.lsp4j.CompletionItemKind; import org.eclipse.lsp4j.CompletionItemTag; +import org.eclipse.lsp4j.ServerCapabilities; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -35,6 +37,8 @@ @ApiStatus.Experimental public class LSPCompletionFeature extends AbstractLSPDocumentFeature { + private CompletionCapabilityRegistry completionCapabilityRegistry; + public static class LSPCompletionContext { private final @NotNull CompletionParameters parameters; @@ -84,12 +88,7 @@ public boolean isSupported(@NotNull PsiFile file) { * @return true if the file associated with a language server can support completion and false otherwise. */ public boolean isCompletionSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isCompletionSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); - } - - public boolean isCompletionTriggerCharactersSupported(@NotNull PsiFile file, String completionChar) { - return getClientFeatures().getServerWrapper().isCompletionTriggerCharactersSupported(completionChar); + return getCompletionCapabilityRegistry().isCompletionSupported(file); } /** @@ -99,8 +98,12 @@ public boolean isCompletionTriggerCharactersSupported(@NotNull PsiFile file, Str * @return true the file associated with a language server can support resolve completion and false otherwise. */ public boolean isResolveCompletionSupported(@Nullable PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isResolveCompletionSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + return getCompletionCapabilityRegistry().isResolveCompletionSupported(file); + } + + public boolean isCompletionTriggerCharactersSupported(@NotNull PsiFile file, String completionChar) { + // TODO: manage documentSelector for trigger character + return getClientFeatures().getServerWrapper().isCompletionTriggerCharactersSupported(completionChar); } /** @@ -225,5 +228,19 @@ public void addLookupItem(@NotNull CompletionPrefix completionPrefix, } } + public CompletionCapabilityRegistry getCompletionCapabilityRegistry() { + if (completionCapabilityRegistry == null) { + var clientFeatures = getClientFeatures(); + completionCapabilityRegistry = new CompletionCapabilityRegistry(clientFeatures); + completionCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync()); + } + return completionCapabilityRegistry; + } + @Override + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + if(completionCapabilityRegistry != null) { + completionCapabilityRegistry.setServerCapabilities(serverCapabilities); + } + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDeclarationFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDeclarationFeature.java index 0a1fd79fe..179da3de8 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDeclarationFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDeclarationFeature.java @@ -11,9 +11,11 @@ package com.redhat.devtools.lsp4ij.client.features; import com.intellij.psi.PsiFile; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.server.capabilities.DeclarationCapabilityRegistry; +import org.eclipse.lsp4j.ServerCapabilities; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * LSP declaration feature. @@ -21,19 +23,36 @@ @ApiStatus.Experimental public class LSPDeclarationFeature extends AbstractLSPDocumentFeature { + private DeclarationCapabilityRegistry declarationCapabilityRegistry; + @Override public boolean isSupported(@NotNull PsiFile file) { return isDeclarationSupported(file); } /** - * Returns true if the file associated with a language server can support codelens and false otherwise. + * Returns true if the file associated with a language server can support declaration and false otherwise. * * @param file the file. - * @return true if the file associated with a language server can support codelens and false otherwise. + * @return true if the file associated with a language server can support declaration and false otherwise. */ public boolean isDeclarationSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isDeclarationSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + return getDeclarationCapabilityRegistry().isDeclarationSupported(file); + } + + public DeclarationCapabilityRegistry getDeclarationCapabilityRegistry() { + if (declarationCapabilityRegistry == null) { + var clientFeatures = getClientFeatures(); + declarationCapabilityRegistry = new DeclarationCapabilityRegistry(clientFeatures); + declarationCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync()); + } + return declarationCapabilityRegistry; + } + + @Override + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + if (declarationCapabilityRegistry != null) { + declarationCapabilityRegistry.setServerCapabilities(serverCapabilities); + } } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDefinitionFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDefinitionFeature.java index 5232a11b0..6ea36abb6 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDefinitionFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDefinitionFeature.java @@ -11,9 +11,11 @@ package com.redhat.devtools.lsp4ij.client.features; import com.intellij.psi.PsiFile; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.server.capabilities.DefinitionCapabilityRegistry; +import org.eclipse.lsp4j.ServerCapabilities; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * LSP definition feature. @@ -21,19 +23,36 @@ @ApiStatus.Experimental public class LSPDefinitionFeature extends AbstractLSPDocumentFeature { + private DefinitionCapabilityRegistry definitionCapabilityRegistry; + @Override public boolean isSupported(@NotNull PsiFile file) { return isDefinitionSupported(file); } /** - * Returns true if the file associated with a language server can support codelens and false otherwise. + * Returns true if the file associated with a language server can support definition and false otherwise. * * @param file the file. - * @return true if the file associated with a language server can support codelens and false otherwise. + * @return true if the file associated with a language server can support definition and false otherwise. */ public boolean isDefinitionSupported(@NotNull PsiFile file) { - // TODO implement documentSelector to use language of the given file - return LanguageServerItem.isDefinitionSupported(getClientFeatures().getServerWrapper().getServerCapabilitiesSync()); + return getDefinitionCapabilityRegistry().isDefinitionSupported(file); + } + + public DefinitionCapabilityRegistry getDefinitionCapabilityRegistry() { + if (definitionCapabilityRegistry == null) { + var clientFeatures = getClientFeatures(); + definitionCapabilityRegistry = new DefinitionCapabilityRegistry(clientFeatures); + definitionCapabilityRegistry.setServerCapabilities(clientFeatures.getServerWrapper().getServerCapabilitiesSync()); + } + return definitionCapabilityRegistry; + } + + @Override + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + if (definitionCapabilityRegistry != null) { + definitionCapabilityRegistry.setServerCapabilities(serverCapabilities); + } } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDiagnosticFeature.java b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDiagnosticFeature.java index 3429dca27..f4f16713c 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDiagnosticFeature.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/client/features/LSPDiagnosticFeature.java @@ -249,4 +249,8 @@ public ProblemHighlightType getProblemHighlightType(@Nullable List canSupportFeature(ls.getServerCapabilitiesSync())); + .hasAny(file.getVirtualFile(), ls -> canSupportFeature(ls.getClientFeatures(), file)); } - protected abstract boolean canSupportFeature(ServerCapabilities serverCapabilities); + protected abstract boolean canSupportFeature(@NotNull LSPClientFeatures clientFeatures, @NotNull PsiFile file); @Override public final @NotNull ActionUpdateThread getActionUpdateThread() { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/LSPLazyCodeActionIntentionAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/LSPLazyCodeActionIntentionAction.java index 97bc4640c..93a72194a 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/LSPLazyCodeActionIntentionAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/codeAction/LSPLazyCodeActionIntentionAction.java @@ -33,8 +33,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.redhat.devtools.lsp4ij.LanguageServerItem.isCodeActionResolveSupported; - /** * The lazy IJ Quick fix / Intention. */ @@ -84,7 +82,7 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws var languageServer = getLanguageServer(); if (codeAction != null) { if (codeAction.getEdit() == null && codeAction.getCommand() == null - && isCodeActionResolveSupported(languageServer.getServerCapabilities())) { + && languageServer.getClientFeatures().getCodeActionFeature().isResolveCodeActionSupported(file)) { // Unresolved code action "edit" property. Resolve it. languageServer .getInitializedServer() diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java index 1e9a9a592..d578a518e 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/codeLens/LSPCodeLensSupport.java @@ -67,7 +67,7 @@ protected CompletableFuture> doLoad(CodeLensParams params, Ca // Collect list of textDocument/codeLens future for each language servers List>> codeLensPerServerFutures = languageServers .stream() - .map(languageServer -> getCodeLensesFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getCodeLensesFor(params, languageServer, file, cancellationSupport)) .toList(); // Merge list of textDocument/codelens future in one future which return the list of code lenses @@ -80,7 +80,10 @@ protected CompletableFuture> doLoad(CodeLensParams params, Ca }); } - private static CompletableFuture> getCodeLensesFor(CodeLensParams params, LanguageServerItem languageServer, CancellationSupport cancellationSupport) { + private static CompletableFuture> getCodeLensesFor(@NotNull CodeLensParams params, + @NotNull LanguageServerItem languageServer, + @NotNull PsiFile file, + @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer .getTextDocumentService() .codeLens(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_CODE_LENS) @@ -95,14 +98,14 @@ private static CompletableFuture> getCodeLensesFor(CodeLensPa .filter(LSPCodeLensSupport::isValidCodeLens) .forEach(codeLens -> { CompletableFuture resolvedCodeLensFuture = null; - if (codeLens.getCommand() == null && languageServer.isResolveCodeLensSupported()) { + var codeLensFeature = languageServer.getClientFeatures().getCodeLensFeature(); + if (codeLens.getCommand() == null && codeLensFeature.isResolveCodeLensSupported(file)) { // - the codelens has no command, and the language server supports codeLens/resolve // prepare the future which resolves the codelens. resolvedCodeLensFuture = cancellationSupport.execute(languageServer .getTextDocumentService() .resolveCodeLens(codeLens), languageServer, LSPRequestConstants.TEXT_DOCUMENT_RESOLVE_CODE_LENS); } - var codeLensFeature = languageServer.getClientFeatures().getCodeLensFeature(); if (codeLensFeature.getText(codeLens) != null || resolvedCodeLensFuture != null) { // The codelens content is filled or the codelens must be resolved data.add(new CodeLensData(codeLens, languageServer, resolvedCodeLensFuture)); diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java index 276291843..cd17d90e3 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/color/LSPColorSupport.java @@ -53,8 +53,8 @@ protected CompletableFuture> doLoad(@NotNull DocumentColorParams @NotNull CancellationSupport cancellationSupport) { return getLanguageServers(file, - f -> f.getColorFeature().isEnabled(file), - f -> f.getColorFeature().isSupported(file)) + f -> f.getDocumentColorFeature().isEnabled(file), + f -> f.getDocumentColorFeature().isSupported(file)) .thenComposeAsync(languageServers -> { // Here languageServers is the list of language servers which matches the given file // and which have color capability diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java index 0816ed084..fe1cbe589 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/declaration/LSPGoToDeclarationAction.java @@ -16,11 +16,11 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,8 +62,8 @@ protected CompletableFuture> getLocations(PsiFile psiFile, Docume } @Override - protected boolean canSupportFeature(ServerCapabilities serverCapabilities) { - return LanguageServerItem.isDeclarationSupported(serverCapabilities); + protected boolean canSupportFeature(@NotNull LSPClientFeatures clientFeatures, @NotNull PsiFile file) { + return clientFeatures.getDeclarationFeature().isDeclarationSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticAnnotator.java b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticAnnotator.java index 9fe6adbbb..8a74f5f7f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticAnnotator.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticAnnotator.java @@ -98,7 +98,7 @@ private static void createAnnotation(@NotNull Diagnostic diagnostic, List fixes = Collections.emptyList(); var codeActionFeature = clientFeatures.getCodeActionFeature(); if (codeActionFeature.isQuickFixesEnabled(file)) { - fixes = diagnosticsForServer.getQuickFixesFor(diagnostic); + fixes = diagnosticsForServer.getQuickFixesFor(diagnostic, file); } diagnosticSupport.createAnnotation(diagnostic, document, fixes, holder); } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticsForServer.java b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticsForServer.java index 6cecb2c8a..d239854cb 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticsForServer.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/diagnostics/LSPDiagnosticsForServer.java @@ -15,6 +15,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LanguageServerItem; import com.redhat.devtools.lsp4ij.LanguageServerWrapper; import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; @@ -23,6 +24,7 @@ import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.util.Ranges; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; @@ -138,10 +140,12 @@ public Set getDiagnostics() { * Returns Intellij quickfixes for the given diagnostic if there available. * * @param diagnostic the diagnostic. + * @param file * @return Intellij quickfixes for the given diagnostic if there available. */ - public List getQuickFixesFor(Diagnostic diagnostic) { - boolean codeActionSupported = isCodeActionSupported(languageServer.getServerWrapper()); + public List getQuickFixesFor(@NotNull Diagnostic diagnostic, + @NotNull PsiFile file) { + boolean codeActionSupported = isCodeActionSupported(languageServer.getServerWrapper(), file); if (!codeActionSupported || diagnostics.isEmpty()) { return Collections.emptyList(); } @@ -149,13 +153,14 @@ public List getQuickFixesFor(Diagnostic diagnostic) { return codeActions != null ? codeActions.getCodeActions() : Collections.emptyList(); } - private static boolean isCodeActionSupported(LanguageServerWrapper languageServerWrapper) { + private static boolean isCodeActionSupported(@NotNull LanguageServerWrapper languageServerWrapper, + @NotNull PsiFile file) { if (!languageServerWrapper.isActive() || languageServerWrapper.isStopping()) { // This use-case comes from when a diagnostics is published and the language server is stopped // We cannot use here languageServerWrapper.getServerCapabilities() otherwise it will restart the language server. return false; } - return LanguageServerItem.isCodeActionSupported(languageServerWrapper.getServerCapabilities()); + return languageServerWrapper.getClientFeatures().getCodeActionFeature().isCodeActionSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingAndRangeBothService.java b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingAndRangeBothService.java index 978a5e037..f0ae9a556 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingAndRangeBothService.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingAndRangeBothService.java @@ -30,7 +30,7 @@ public class LSPFormattingAndRangeBothService extends AbstractLSPFormattingServi @Override protected boolean canSupportFormatting(LSPFormattingFeature formattingFeature, PsiFile file) { - return formattingFeature.isDocumentRangeFormattingSupported(file); + return formattingFeature.isRangeFormattingSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingOnlyService.java b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingOnlyService.java index 4f3aacb41..6b1f204eb 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingOnlyService.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingOnlyService.java @@ -31,8 +31,8 @@ public class LSPFormattingOnlyService extends AbstractLSPFormattingService { @Override protected boolean canSupportFormatting(LSPFormattingFeature formattingFeature, PsiFile file) { - return formattingFeature.isDocumentFormattingSupported(file) && - !formattingFeature.isDocumentRangeFormattingSupported(file); + return formattingFeature.isFormattingSupported(file) && + !formattingFeature.isRangeFormattingSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java index 665b224f2..c29d7e1a9 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/formatting/LSPFormattingSupport.java @@ -90,9 +90,9 @@ protected CompletableFuture> doLoad(LSPFormattingParams @NotNull CancellationSupport cancellationSupport) { boolean isRangeFormatting = params.textRange() != null; Predicate filter = !isRangeFormatting ? - f -> f.getFormattingFeature().isDocumentFormattingSupported(file) : - f -> f.getFormattingFeature().isDocumentFormattingSupported(file) || - f.getFormattingFeature().isDocumentRangeFormattingSupported(file); + f -> f.getFormattingFeature().isFormattingSupported(file) : + f -> f.getFormattingFeature().isFormattingSupported(file) || + f.getFormattingFeature().isRangeFormattingSupported(file); return getLanguageServers(file, f -> f.getFormattingFeature().isEnabled(file), filter) diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java index 783f50b88..79983228f 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/implementation/LSPGoToImplementationAction.java @@ -16,11 +16,11 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,8 +62,8 @@ protected CompletableFuture> getLocations(PsiFile psiFile, Docume } @Override - protected boolean canSupportFeature(ServerCapabilities serverCapabilities) { - return LanguageServerItem.isImplementationSupported(serverCapabilities); + protected boolean canSupportFeature(@NotNull LSPClientFeatures clientFeatures, @NotNull PsiFile file) { + return clientFeatures.getImplementationFeature().isImplementationSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsProvider.java b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsProvider.java index 456c9f5ec..e6a4a7391 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsProvider.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsProvider.java @@ -178,7 +178,7 @@ private void executeCommand(@NotNull PsiFile psiFile, @NotNull InlayHint inlayHint, int index, @Nullable MouseEvent event) { - if (languageServer.isResolveInlayHintSupported()) { + if (languageServer.getClientFeatures().getInlayHintFeature().isResolveInlayHintSupported(psiFile)) { languageServer.getTextDocumentService() .resolveInlayHint(inlayHint) .thenAcceptAsync(resolvedInlayHint -> { diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java index 3fdbf6e25..1a05c195b 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/inlayhint/LSPInlayHintsSupport.java @@ -67,7 +67,7 @@ protected CompletableFuture> doLoad(InlayHintParams params, // Collect list of textDocument/inlayHint future for each language servers List>> inlayHintPerServerFutures = languageServers .stream() - .map(languageServer -> getInlayHintsFor(params, languageServer, cancellationSupport)) + .map(languageServer -> getInlayHintsFor(params, file, languageServer, cancellationSupport)) .toList(); // Merge list of textDocument/inlayHint future in one future which return the list of inlay hints @@ -75,7 +75,10 @@ protected CompletableFuture> doLoad(InlayHintParams params, }); } - private static CompletableFuture> getInlayHintsFor(InlayHintParams params, LanguageServerItem languageServer, CancellationSupport cancellationSupport) { + private static CompletableFuture> getInlayHintsFor(@NotNull InlayHintParams params, + @NotNull PsiFile file, + @NotNull LanguageServerItem languageServer, + @NotNull CancellationSupport cancellationSupport) { return cancellationSupport.execute(languageServer .getTextDocumentService() .inlayHint(params), languageServer, LSPRequestConstants.TEXT_DOCUMENT_INLAY_HINT) @@ -89,7 +92,7 @@ private static CompletableFuture> getInlayHintsFor(InlayHint .filter(Objects::nonNull) .forEach(inlayHint -> { CompletableFuture resolvedInlayHintFuture = null; - if (inlayHint.getLabel() == null && languageServer.isResolveInlayHintSupported()) { + if (inlayHint.getLabel() == null && languageServer.getClientFeatures().getInlayHintFeature().isResolveInlayHintSupported(file)) { // - the inlayHint has no label, and the language server supports inlayHint/resolve // prepare the future which resolves the inlayHint. resolvedInlayHintFuture = cancellationSupport.execute(languageServer diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java index 086afa359..ad8a69a64 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/references/LSPGoToReferenceAction.java @@ -16,11 +16,11 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -56,14 +56,14 @@ protected CompletableFuture> getLocations(PsiFile psiFile, Docume // cancel the LSP requests textDocument/references referenceSupport.cancel(); } catch (ExecutionException e) { - LOGGER.error("Error while consuming LSP 'textDocument/reference' request", e); + LOGGER.error("Error while consuming LSP 'textDocument/references' request", e); } return referencesFuture; } @Override - protected boolean canSupportFeature(ServerCapabilities serverCapabilities) { - return LanguageServerItem.isReferencesSupported(serverCapabilities); + protected boolean canSupportFeature(@NotNull LSPClientFeatures clientFeatures, @NotNull PsiFile file) { + return clientFeatures.getReferencesFeature().isReferencesSupported(file); } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameHandler.java b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameHandler.java index d06711b79..8824e02c0 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameHandler.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/rename/LSPRenameHandler.java @@ -181,7 +181,7 @@ public boolean isAvailableOnDataContext(@NotNull DataContext dataContext) { // At this step we consider that language servers are started, we just wait for 200ms // to avoid freezing the UI if (LanguageServiceAccessor.getInstance(project) - .hasAny(file.getVirtualFile(), ls -> LanguageServerItem.isRenameSupported(ls.getServerCapabilitiesSync()))) { + .hasAny(file.getVirtualFile(), ls -> ls.getClientFeatures().getRenameFeature().isRenameSupported(file))) { // There is at least one language server providing rename support for the file. try { searchingRenameHandlers = true; diff --git a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java index fa8fe8463..933872b30 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/features/typeDefinition/LSPGoToTypeDefinitionAction.java @@ -16,11 +16,11 @@ import com.intellij.psi.PsiFile; import com.redhat.devtools.lsp4ij.LSPFileSupport; import com.redhat.devtools.lsp4ij.LSPIJUtils; -import com.redhat.devtools.lsp4ij.LanguageServerItem; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; import com.redhat.devtools.lsp4ij.features.AbstractLSPGoToAction; import com.redhat.devtools.lsp4ij.usages.LSPUsageType; import org.eclipse.lsp4j.Location; -import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -62,8 +62,8 @@ protected CompletableFuture> getLocations(PsiFile psiFile, Docume } @Override - protected boolean canSupportFeature(ServerCapabilities serverCapabilities) { - return LanguageServerItem.isTypeDefinitionSupported(serverCapabilities); + protected boolean canSupportFeature(@NotNull LSPClientFeatures clientFeatures, @NotNull PsiFile file) { + return clientFeatures.getTypeDefinitionFeature().isTypeDefinitionSupported(file); } } 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 b628d7deb..be9531b93 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/internal/ClientCapabilitiesFactory.java @@ -118,13 +118,13 @@ public static ClientCapabilities create(Object experimental) { textDocumentClientCapabilities.setCodeAction(codeAction); // Code Lens support - textDocumentClientCapabilities.setCodeLens(new CodeLensCapabilities()); + textDocumentClientCapabilities.setCodeLens(new CodeLensCapabilities(Boolean.TRUE)); // Inlay Hint support - textDocumentClientCapabilities.setInlayHint(new InlayHintCapabilities()); + textDocumentClientCapabilities.setInlayHint(new InlayHintCapabilities(Boolean.TRUE)); // textDocument/colorPresentation support - textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities()); + textDocumentClientCapabilities.setColorProvider(new ColorProviderCapabilities(Boolean.TRUE)); // Completion support final var completionItemCapabilities = new CompletionItemCapabilities(Boolean.TRUE); @@ -140,10 +140,11 @@ public static ClientCapabilities create(Object experimental) { completionItemCapabilities.setLabelDetailsSupport(Boolean.TRUE); CompletionCapabilities completionCapabilities = new CompletionCapabilities(completionItemCapabilities); completionCapabilities.setCompletionList(new CompletionListCapabilities(List.of("editRange"))); + completionCapabilities.setDynamicRegistration(Boolean.TRUE); textDocumentClientCapabilities.setCompletion(completionCapabilities); // Signature help support - SignatureHelpCapabilities signatureHelpCapabilities = new SignatureHelpCapabilities(); + SignatureHelpCapabilities signatureHelpCapabilities = new SignatureHelpCapabilities(Boolean.TRUE); SignatureInformationCapabilities signatureInformationCapabilities = new SignatureInformationCapabilities(); ParameterInformationCapabilities parameterInformationCapabilities = new ParameterInformationCapabilities(); parameterInformationCapabilities.setLabelOffsetSupport(Boolean.TRUE); @@ -152,28 +153,28 @@ public static ClientCapabilities create(Object experimental) { textDocumentClientCapabilities.setSignatureHelp(signatureHelpCapabilities); // Declaration support - final var declarationCapabilities = new DeclarationCapabilities(); + final var declarationCapabilities = new DeclarationCapabilities(Boolean.TRUE); declarationCapabilities.setLinkSupport(Boolean.TRUE); textDocumentClientCapabilities.setDeclaration(declarationCapabilities); // Definition support - final var definitionCapabilities = new DefinitionCapabilities(); + final var definitionCapabilities = new DefinitionCapabilities(Boolean.TRUE); definitionCapabilities.setLinkSupport(Boolean.TRUE); textDocumentClientCapabilities.setDefinition(definitionCapabilities); // Type Definition support - final var typeDefinitionCapabilities = new TypeDefinitionCapabilities(); + final var typeDefinitionCapabilities = new TypeDefinitionCapabilities(Boolean.TRUE); typeDefinitionCapabilities.setLinkSupport(Boolean.TRUE); textDocumentClientCapabilities.setTypeDefinition(typeDefinitionCapabilities); // DocumentHighlight support - textDocumentClientCapabilities.setDocumentHighlight(new DocumentHighlightCapabilities()); + textDocumentClientCapabilities.setDocumentHighlight(new DocumentHighlightCapabilities(Boolean.TRUE)); // DocumentLink support - textDocumentClientCapabilities.setDocumentLink(new DocumentLinkCapabilities()); + textDocumentClientCapabilities.setDocumentLink(new DocumentLinkCapabilities(Boolean.TRUE)); // textDocument/documentSymbol - final var documentSymbol = new DocumentSymbolCapabilities(); + final var documentSymbol = new DocumentSymbolCapabilities(Boolean.TRUE); documentSymbol.setHierarchicalDocumentSymbolSupport(true); documentSymbol.setSymbolKind(new SymbolKindCapabilities(Arrays.asList(SymbolKind.Array, SymbolKind.Boolean, SymbolKind.Class, SymbolKind.Constant, SymbolKind.Constructor, @@ -185,37 +186,36 @@ public static ClientCapabilities create(Object experimental) { textDocumentClientCapabilities.setDocumentSymbol(documentSymbol); // FoldingRange support - textDocumentClientCapabilities.setFoldingRange(new FoldingRangeCapabilities()); - - // Formatting support - textDocumentClientCapabilities.setFormatting(new FormattingCapabilities(Boolean.TRUE)); - textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities(Boolean.TRUE)); + var foldingRangeCapabilities = new FoldingRangeCapabilities(); + foldingRangeCapabilities.setDynamicRegistration(Boolean.TRUE); + textDocumentClientCapabilities.setFoldingRange(foldingRangeCapabilities); // Hover support - final var hoverCapabilities = new HoverCapabilities(); + final var hoverCapabilities = new HoverCapabilities(Boolean.TRUE); hoverCapabilities.setContentFormat(List.of(MarkupKind.MARKDOWN, MarkupKind.PLAINTEXT)); textDocumentClientCapabilities.setHover(hoverCapabilities); // References support - textDocumentClientCapabilities.setReferences(new ReferencesCapabilities()); + textDocumentClientCapabilities.setReferences(new ReferencesCapabilities(Boolean.TRUE)); // Implementation support - var implementationCapabilities = new ImplementationCapabilities(); + var implementationCapabilities = new ImplementationCapabilities(Boolean.TRUE); implementationCapabilities.setLinkSupport(Boolean.TRUE); textDocumentClientCapabilities.setImplementation(implementationCapabilities); - // TODO: support onTypeFormatting - // textDocumentClientCapabilities.setOnTypeFormatting(null); // TODO - // TODO : support textDocument/rangeFormatting - // textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities()); + // textDocument/formatting + textDocumentClientCapabilities.setFormatting(new FormattingCapabilities(Boolean.TRUE)); + + // textDocument/rangeFormatting + textDocumentClientCapabilities.setRangeFormatting(new RangeFormattingCapabilities(Boolean.TRUE)); // textDocument/rename support - final var renameCapabilities = new RenameCapabilities(); + final var renameCapabilities = new RenameCapabilities(Boolean.TRUE); renameCapabilities.setPrepareSupport(true); textDocumentClientCapabilities.setRename(renameCapabilities); // textDocument/semanticTokens - var semanticTokensCapabilities = new SemanticTokensCapabilities(); + var semanticTokensCapabilities = new SemanticTokensCapabilities(Boolean.TRUE); semanticTokensCapabilities.setTokenTypes(List.of( SemanticTokenTypes.Namespace, SemanticTokenTypes.Type, diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeActionCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeActionCapabilityRegistry.java new file mode 100644 index 000000000..bfb42a0c5 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeActionCapabilityRegistry.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.CodeActionOptions; +import org.eclipse.lsp4j.CodeActionRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/codeAction'. + */ +public class CodeActionCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getCodeActionProvider()); + + private static final @NotNull Predicate<@NotNull ServerCapabilities> RESOLVE_SERVER_CAPABILITIES_PREDICATE = sc -> { + Either codeActionProvider = sc.getCodeActionProvider(); + if (codeActionProvider != null && codeActionProvider.isRight()) { + return hasCapability(codeActionProvider.getRight().getResolveProvider()); + } + return false; + }; + + private static final @Nullable Predicate<@NotNull CodeActionRegistrationOptions> RESOLVE_REGISTRATION_OPTIONS_PREDICATE = o -> + hasCapability(o.getResolveProvider()); + + + public CodeActionCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedCodeActionFormattingRegistrationOptions extends CodeActionRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable CodeActionRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedCodeActionFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support codeAction and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support codeAction and false otherwise. + */ + public boolean isCodeActionSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + + /** + * Returns true if the language server can support resolve codeAction and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support resolve codeAction and false otherwise. + */ + public boolean isResolveCodeActionSupported(@NotNull PsiFile file) { + return super.isSupported(file, RESOLVE_SERVER_CAPABILITIES_PREDICATE, RESOLVE_REGISTRATION_OPTIONS_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeLensCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeLensCapabilityRegistry.java new file mode 100644 index 000000000..02de0afe9 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CodeLensCapabilityRegistry.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.CodeLensRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/codeLens'. + */ +public class CodeLensCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getCodeLensProvider() != null; + + private static final @NotNull Predicate<@NotNull ServerCapabilities> RESOLVE_SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getCodeLensProvider() != null && hasCapability(sc.getCodeLensProvider().getResolveProvider()); + + private static final @Nullable Predicate<@NotNull CodeLensRegistrationOptions> RESOLVE_REGISTRATION_OPTIONS_PREDICATE = o -> + hasCapability(o.getResolveProvider()); + + public CodeLensCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedCodeLensFormattingRegistrationOptions extends CodeLensRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable CodeLensRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedCodeLensFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support codeLens and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support codeLens and false otherwise. + */ + public boolean isCodeLensSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + + /** + * Returns true if the language server can support resolve code lens and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support resolve code lens and false otherwise. + */ + public boolean isResolveCodeLensSupported(@NotNull PsiFile file) { + return super.isSupported(file, RESOLVE_SERVER_CAPABILITIES_PREDICATE, RESOLVE_REGISTRATION_OPTIONS_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CompletionCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CompletionCapabilityRegistry.java new file mode 100644 index 000000000..85e5ba16b --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/CompletionCapabilityRegistry.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.CompletionRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/completion'. + */ +public class CompletionCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getCompletionProvider() != null; + + private static final @NotNull Predicate<@NotNull ServerCapabilities> RESOLVE_SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getCompletionProvider() != null && hasCapability(sc.getCompletionProvider().getResolveProvider()); + + private static final @Nullable Predicate<@NotNull CompletionRegistrationOptions> RESOLVE_REGISTRATION_OPTIONS_PREDICATE = o -> + hasCapability(o.getResolveProvider()); + + public CompletionCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedCompletionFormattingRegistrationOptions extends CompletionRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable CompletionRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedCompletionFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support completion and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support completion and false otherwise. + */ + public boolean isCompletionSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + + /** + * Returns true if the language server can support resolve completion and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support resolve completion and false otherwise. + */ + public boolean isResolveCompletionSupported(@NotNull PsiFile file) { + return super.isSupported(file, RESOLVE_SERVER_CAPABILITIES_PREDICATE, RESOLVE_REGISTRATION_OPTIONS_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DeclarationCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DeclarationCapabilityRegistry.java new file mode 100644 index 000000000..1c30d21f7 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DeclarationCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DeclarationRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/declaration'. + */ +public class DeclarationCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getDeclarationProvider()); + + public DeclarationCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedDeclarationRegistrationOptions extends DeclarationRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable DeclarationRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedDeclarationRegistrationOptions.class); + } + + /** + * Returns true if the language server can support declaration and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support declaration and false otherwise. + */ + public boolean isDeclarationSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DefinitionCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DefinitionCapabilityRegistry.java new file mode 100644 index 000000000..1c5558989 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DefinitionCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DefinitionRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/definition'. + */ +public class DefinitionCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getDefinitionProvider()); + + public DefinitionCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedDefinitionRegistrationOptions extends DefinitionRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable DefinitionRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedDefinitionRegistrationOptions.class); + } + + /** + * Returns true if the language server can support definition and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support definition and false otherwise. + */ + public boolean isDefinitionSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentColorCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentColorCapabilityRegistry.java new file mode 100644 index 000000000..7e9153481 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentColorCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.ColorProviderOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/documentColor'. + */ +public class DocumentColorCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getColorProvider()); + + public DocumentColorCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedColorFormattingRegistrationOptions extends ColorProviderOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable ColorProviderOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedColorFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support documentColor and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support documentColor and false otherwise. + */ + public boolean isDocumentColorSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentFormattingCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentFormattingCapabilityRegistry.java new file mode 100644 index 000000000..591999f31 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentFormattingCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DocumentFormattingRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/formatting'. + */ +public class DocumentFormattingCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getDocumentFormattingProvider()); + + public DocumentFormattingCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedDocumentFormattingRegistrationOptions extends DocumentFormattingRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable DocumentFormattingRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedDocumentFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support formatting and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support formatting and false otherwise. + */ + public boolean isFormattingSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentHighlightCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentHighlightCapabilityRegistry.java new file mode 100644 index 000000000..6236e5ca9 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentHighlightCapabilityRegistry.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DocumentHighlightRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/documentHighlight'. + */ +public class DocumentHighlightCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getDocumentHighlightProvider() != null && hasCapability(sc.getDocumentHighlightProvider()); + + public DocumentHighlightCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedDocumentHighlightingFormattingRegistrationOptions extends DocumentHighlightRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + + } + + @Override + protected @Nullable DocumentHighlightRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedDocumentHighlightingFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support documentDeclaration and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support documentDeclaration and false otherwise. + */ + public boolean isDocumentHighlightSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentLinkCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentLinkCapabilityRegistry.java new file mode 100644 index 000000000..cfb6e5745 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentLinkCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DocumentLinkRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/documentLink'. + */ +public class DocumentLinkCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getDocumentLinkProvider() != null; + + public DocumentLinkCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedLinkFormattingRegistrationOptions extends DocumentLinkRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable DocumentLinkRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedLinkFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support documentLink and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support documentLink and false otherwise. + */ + public boolean isDocumentLinkSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentRangeFormattingCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentRangeFormattingCapabilityRegistry.java new file mode 100644 index 000000000..41918a376 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentRangeFormattingCapabilityRegistry.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DocumentRangeFormattingRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/rangeFormatting'. + */ +public class DocumentRangeFormattingCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getDocumentRangeFormattingProvider()); + + public DocumentRangeFormattingCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedDocumentRangeFormattingRegistrationOptions extends DocumentRangeFormattingRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + + @Override + protected @Nullable DocumentRangeFormattingRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedDocumentRangeFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support rangeFormatting and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support rangeFormatting and false otherwise. + */ + public boolean isRangeFormattingSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentSymbolCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentSymbolCapabilityRegistry.java new file mode 100644 index 000000000..9e05c14ea --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/DocumentSymbolCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.DocumentSymbolRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/documentSymbol'. + */ +public class DocumentSymbolCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getDocumentSymbolProvider()); + + public DocumentSymbolCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedSymbolFormattingRegistrationOptions extends DocumentSymbolRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable DocumentSymbolRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedSymbolFormattingRegistrationOptions.class); + } + + /** + * Returns true if the language server can support documentSymbol and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support documentSymbol and false otherwise. + */ + public boolean isDocumentSymbolSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ExtendedDocumentSelector.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ExtendedDocumentSelector.java new file mode 100644 index 000000000..8b798587f --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ExtendedDocumentSelector.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.redhat.devtools.lsp4ij.features.files.PathPatternMatcher; +import com.redhat.devtools.lsp4ij.internal.StringUtils; +import org.eclipse.lsp4j.DocumentFilter; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +/** + * Extended documentSelector. + */ +public class ExtendedDocumentSelector { + + interface DocumentFilersProvider { + List getFilters(); + } + + @NotNull + private final List filters; + + public class ExtendedDocumentFilter extends DocumentFilter { + + private PathPatternMatcher patternMatcher; + + public ExtendedDocumentFilter(DocumentFilter filter) { + super.setLanguage(filter.getLanguage()); + super.setScheme(filter.getScheme()); + super.setPattern(filter.getPattern()); + } + + @Nullable + public PathPatternMatcher getPathPattern() { + if (patternMatcher != null) { + return patternMatcher; + } + String pattern = super.getPattern(); + if (StringUtils.isEmpty(pattern)) { + return null; + } + patternMatcher = new PathPatternMatcher(pattern); + return patternMatcher; + } + } + + public ExtendedDocumentSelector(List documentSelector) { + this.filters = documentSelector != null ? + documentSelector + .stream() + .filter(f -> !(StringUtils.isEmpty(f.getLanguage()) && StringUtils.isEmpty(f.getPattern()) && StringUtils.isEmpty(f.getScheme()))) + .map(f -> new ExtendedDocumentFilter(f)) + .toList() : + Collections.emptyList(); + } + + public @NotNull List getFilters() { + return filters; + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/FoldingRangeCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/FoldingRangeCapabilityRegistry.java new file mode 100644 index 000000000..8f905cfba --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/FoldingRangeCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.FoldingRangeProviderOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/foldingRange'. + */ +public class FoldingRangeCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getFoldingRangeProvider()); + + public FoldingRangeCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedFoldingRangeProviderOptions extends FoldingRangeProviderOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable FoldingRangeProviderOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedFoldingRangeProviderOptions.class); + } + + /** + * Returns true if the language server can support foldingRange and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support foldingRange and false otherwise. + */ + public boolean isFoldingRangeSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/HoverCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/HoverCapabilityRegistry.java new file mode 100644 index 000000000..fb080a81d --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/HoverCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.HoverRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/hover'. + */ +public class HoverCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getHoverProvider()); + + public HoverCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedHoverRegistrationOptions extends HoverRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable HoverRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedHoverRegistrationOptions.class); + } + + /** + * Returns true if the language server can support hover and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support hover and false otherwise. + */ + public boolean isHoverSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ImplementationCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ImplementationCapabilityRegistry.java new file mode 100644 index 000000000..239aafc37 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ImplementationCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.ImplementationRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/implementation'. + */ +public class ImplementationCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getImplementationProvider()); + + public ImplementationCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedImplementationRegistrationOptions extends ImplementationRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable ImplementationRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedImplementationRegistrationOptions.class); + } + + /** + * Returns true if the language server can support implementation and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support implementation and false otherwise. + */ + public boolean isImplementationSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/InlayHintCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/InlayHintCapabilityRegistry.java new file mode 100644 index 000000000..a876e436a --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/InlayHintCapabilityRegistry.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and inlayHint + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.InlayHintRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/inlayHint'. + */ +public class InlayHintCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getInlayHintProvider()); + + private static final @NotNull Predicate<@NotNull ServerCapabilities> RESOLVE_SERVER_CAPABILITIES_PREDICATE = sc -> { + var inlayHintProvider =sc.getInlayHintProvider(); + if (inlayHintProvider != null && inlayHintProvider.isRight()) { + return hasCapability(inlayHintProvider.getRight().getResolveProvider()); + } + return false; + }; + + private static final @Nullable Predicate<@NotNull InlayHintRegistrationOptions> RESOLVE_REGISTRATION_OPTIONS_PREDICATE = o -> + hasCapability(o.getResolveProvider()); + + public InlayHintCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedInlayHintRegistrationOptions extends InlayHintRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable InlayHintRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedInlayHintRegistrationOptions.class); + } + + /** + * Returns true if the language server can support inlayHint and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support inlayHint and false otherwise. + */ + public boolean isInlayHintSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + + /** + * Returns true if the language server can support resolve inlayHint and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support resolve inlayHint and false otherwise. + */ + public boolean isResolveInlayHintSupported(@NotNull PsiFile file) { + return super.isSupported(file, RESOLVE_SERVER_CAPABILITIES_PREDICATE, RESOLVE_REGISTRATION_OPTIONS_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ReferencesCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ReferencesCapabilityRegistry.java new file mode 100644 index 000000000..471f61978 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/ReferencesCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and references + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.ReferenceRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/references'. + */ +public class ReferencesCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getReferencesProvider()); + + public ReferencesCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedReferenceRegistrationOptions extends ReferenceRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable ReferenceRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedReferenceRegistrationOptions.class); + } + + /** + * Returns true if the language server can support references and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support references and false otherwise. + */ + public boolean isReferencesSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/RenameCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/RenameCapabilityRegistry.java new file mode 100644 index 000000000..f77991621 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/RenameCapabilityRegistry.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and rename + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.RenameOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/rename'. + */ +public class RenameCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getRenameProvider()); + + private static final @NotNull Predicate<@NotNull ServerCapabilities> PREPARE_RENAME_SERVER_CAPABILITIES_PREDICATE = sc -> { + Either renameProvider =sc.getRenameProvider(); + if (renameProvider != null && renameProvider.isRight()) { + return hasCapability(renameProvider.getRight().getPrepareProvider()); + } + return false; + }; + + private static final @Nullable Predicate<@NotNull RenameOptions> PREPARE_RENAME_REGISTRATION_OPTIONS_PREDICATE = o -> + hasCapability(o.getPrepareProvider()); + + public RenameCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedRenameOptions extends RenameOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable RenameOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedRenameOptions.class); + } + + /** + * Returns true if the language server can support rename and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support rename and false otherwise. + */ + public boolean isRenameSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + + /** + * Returns true if the language server can support prepareRename and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support prepareRename and false otherwise. + */ + public boolean isPrepareRenameSupported(@NotNull PsiFile file) { + return super.isSupported(file, PREPARE_RENAME_SERVER_CAPABILITIES_PREDICATE, PREPARE_RENAME_REGISTRATION_OPTIONS_PREDICATE); + } +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/SignatureHelpCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/SignatureHelpCapabilityRegistry.java new file mode 100644 index 000000000..8202596c1 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/SignatureHelpCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.SignatureHelpRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/signatureHelp'. + */ +public class SignatureHelpCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + sc.getSignatureHelpProvider() != null; + + public SignatureHelpCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedSignatureHelpRegistrationOptions extends SignatureHelpRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable SignatureHelpRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedSignatureHelpRegistrationOptions.class); + } + + /** + * Returns true if the language server can support signatureHelp and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support signatureHelp and false otherwise. + */ + public boolean isSignatureHelpSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TextDocumentServerCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TextDocumentServerCapabilityRegistry.java new file mode 100644 index 000000000..1d3d59fee --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TextDocumentServerCapabilityRegistry.java @@ -0,0 +1,149 @@ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.LSPIJUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import com.redhat.devtools.lsp4ij.features.files.PathPatternMatcher; +import com.redhat.devtools.lsp4ij.internal.StringUtils; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentRegistrationOptions; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public abstract class TextDocumentServerCapabilityRegistry { + + private final @NotNull LSPClientFeatures clientFeatures; + private @Nullable ServerCapabilities serverCapabilities; + + private final List dynamicCapabilities; + + public TextDocumentServerCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + this.clientFeatures = clientFeatures; + this.dynamicCapabilities = new ArrayList<>(); + } + + public void setServerCapabilities(@Nullable ServerCapabilities serverCapabilities) { + this.serverCapabilities = serverCapabilities; + this.dynamicCapabilities.clear(); + } + + public @Nullable ServerCapabilities getServerCapabilities() { + return serverCapabilities; + } + + @Nullable + public T registerCapability(JsonObject json) { + T t = create(json); + if (t != null) { + dynamicCapabilities.add(t); + } + return t; + } + + @Nullable + protected abstract T create(JsonObject json); + + public void unregisterCapability(Object options) { + dynamicCapabilities.remove(options); + } + + protected boolean isSupported(@NotNull PsiFile file, + @NotNull Predicate<@NotNull ServerCapabilities> matchServerCapabilities) { + return isSupported(file, matchServerCapabilities, null); + } + + protected boolean isSupported(@NotNull PsiFile file, + @NotNull Predicate<@NotNull ServerCapabilities> matchServerCapabilities, + @Nullable Predicate<@NotNull T> matchOption) { + var serverCapabilities = getServerCapabilities(); + if (serverCapabilities != null && matchServerCapabilities.test(serverCapabilities)) { + return true; + } + + if (dynamicCapabilities.isEmpty()) { + return false; + } + + boolean languageIdGet = false; + String languageId = null; + URI fileUri = null; + String scheme = null; + for (var option : dynamicCapabilities) { + // Match documentSelector? + var filters = ((ExtendedDocumentSelector.DocumentFilersProvider) option).getFilters(); + if (filters.isEmpty()) { + return matchOption != null ? matchOption.test(option) : true; + } + for (var filter : filters) { + boolean hasLanguage = !StringUtils.isEmpty(filter.getLanguage()); + boolean hasScheme = !StringUtils.isEmpty(filter.getScheme()); + boolean hasPattern = !StringUtils.isEmpty(filter.getPattern()); + + boolean matchDocumentSelector = false; + // Matches language? + if (hasLanguage) { + if (!languageIdGet) { + languageId = clientFeatures.getServerDefinition().getLanguageIdOrNull(file); + languageIdGet = true; + } + matchDocumentSelector = (languageId == null && !hasScheme && !hasPattern) // to be compatible with LSP4IJ < 0.7.0, when languageId is not defined in the mapping, we consider that it matches the documentSelector + || filter.getLanguage().equals(languageId); + } + + if (!matchDocumentSelector) { + // Matches scheme? + if (hasScheme) { + if (fileUri == null) { + fileUri = LSPIJUtils.toUri(file); // TODO: move this file Uri into LSP client features to customize the file Uri + } + if (scheme == null) { + scheme = fileUri.getScheme(); + } + matchDocumentSelector = filter.getScheme().equals(scheme); + } + + if (!matchDocumentSelector) { + + // Matches pattern? + if (hasPattern) { + PathPatternMatcher patternMatcher = filter.getPathPattern(); + if (fileUri == null) { + fileUri = LSPIJUtils.toUri(file); // TODO: move this file Uri into LSP client features to customize the file Uri + } + matchDocumentSelector = patternMatcher.matches(fileUri); + } + } + } + + if (matchDocumentSelector) { + if (matchOption == null) { + return true; + } + if (matchOption.test(option)) { + return true; + } + } + } + } + return false; + } + + public static boolean hasCapability(final Either eitherCapability) { + if (eitherCapability == null) { + return false; + } + return eitherCapability.isRight() || hasCapability(eitherCapability.getLeft()); + } + + public static boolean hasCapability(Boolean capability) { + return capability != null && capability; + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TypeDefinitionCapabilityRegistry.java b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TypeDefinitionCapabilityRegistry.java new file mode 100644 index 000000000..3f102e871 --- /dev/null +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/capabilities/TypeDefinitionCapabilityRegistry.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * 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 https://www.eclipse.org/legal/epl-v20.html + * + * Contributors: + * Red Hat, Inc. - initial API and implementation + ******************************************************************************/ +package com.redhat.devtools.lsp4ij.server.capabilities; + +import com.google.gson.JsonObject; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.JSONUtils; +import com.redhat.devtools.lsp4ij.client.features.LSPClientFeatures; +import org.eclipse.lsp4j.TypeDefinitionRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.Predicate; + +/** + * Server capability registry for 'textDocument/typeDefinition'. + */ +public class TypeDefinitionCapabilityRegistry extends TextDocumentServerCapabilityRegistry { + + private static final @NotNull Predicate<@NotNull ServerCapabilities> SERVER_CAPABILITIES_PREDICATE = sc -> + hasCapability(sc.getTypeDefinitionProvider()); + + public TypeDefinitionCapabilityRegistry(@NotNull LSPClientFeatures clientFeatures) { + super(clientFeatures); + } + + class ExtendedTypeDefinitionRegistrationOptions extends TypeDefinitionRegistrationOptions implements ExtendedDocumentSelector.DocumentFilersProvider { + private transient ExtendedDocumentSelector documentSelector; + + @Override + public List getFilters() { + if (documentSelector == null) { + documentSelector = new ExtendedDocumentSelector(super.getDocumentSelector()); + } + return documentSelector.getFilters(); + } + } + + @Override + protected @Nullable TypeDefinitionRegistrationOptions create(JsonObject json) { + return JSONUtils.getLsp4jGson() + .fromJson(json, + ExtendedTypeDefinitionRegistrationOptions.class); + } + + /** + * Returns true if the language server can support typeDefinition and false otherwise. + * + * @param file the Psi file. + * @return true if the language server can support typeDefinition and false otherwise. + */ + public boolean isTypeDefinitionSupported(@NotNull PsiFile file) { + return super.isSupported(file, SERVER_CAPABILITIES_PREDICATE); + } + +} diff --git a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/LanguageServerDefinition.java b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/LanguageServerDefinition.java index 0e0a898a8..67657f3bd 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/server/definition/LanguageServerDefinition.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/server/definition/LanguageServerDefinition.java @@ -18,8 +18,12 @@ import com.intellij.lang.Language; import com.intellij.openapi.fileTypes.FileNameMatcher; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.redhat.devtools.lsp4ij.LSPIJUtils; import com.redhat.devtools.lsp4ij.LanguageServerEnablementSupport; import com.redhat.devtools.lsp4ij.LanguageServerFactory; import com.redhat.devtools.lsp4ij.features.semanticTokens.DefaultSemanticTokensColorsProvider; @@ -165,9 +169,7 @@ public void registerAssociation(List matchers, String languageI this.languageIdFileNameMatcherMappings.add(Pair.create(matchers, languageId)); } - public @Nullable String getLanguageId(Language language) { - return languageIdLanguageMappings.get(language); - } + public Map getLanguageMappings() { return languageIdLanguageMappings; @@ -181,21 +183,6 @@ public List, String>> getFilenameMatcherMappings() { return languageIdFileNameMatcherMappings; } - public @Nullable String getLanguageId(FileType fileType) { - return languageIdFileTypeMappings.get(fileType); - } - - public @Nullable String getLanguageId(String filename) { - for (var mapping : languageIdFileNameMatcherMappings) { - for (var matcher : mapping.getFirst()) { - if (matcher.acceptsCharSequence(filename)) { - return mapping.getSecond(); - } - } - } - return null; - } - public Launcher.Builder createLauncherBuilder() { return new Launcher.Builder<>(); } @@ -217,4 +204,147 @@ public SemanticTokensColorsProvider getSemanticTokensColorsProvider() { public void setSemanticTokensColorsProvider(@NotNull SemanticTokensColorsProvider semanticTokensColorsProvider) { this.semanticTokensColorsProvider = semanticTokensColorsProvider; } + + /** + * Returns the LSP language id defined in mapping otherwise null. + * + * @param file the PsiFile file. + * @return the LSP language id or null. + */ + @Nullable + public String getLanguageIdOrNull(@NotNull PsiFile file) { + return getLanguageId(file.getLanguage(), file.getFileType(), file.getName(), true); + } + + /** + * Returns the LSP language id defined in mapping otherwise the {@link Language#getID()} otherwise the {@link FileType#getName()} otherwise 'unknown'. + * + * @param file the PsiFile file. + * @return the LSP language id. + */ + @NotNull + public String getLanguageId(@NotNull PsiFile file) { + return getLanguageId(file.getLanguage(), file.getFileType(), file.getName()); + } + + /** + * Returns the LSP language id defined in mapping otherwise the {@link Language#getID()} otherwise the {@link FileType#getName()} otherwise 'unknown'. + * + * @param file the virtual file. + * @param project the project. + * @return the LSP language id. + */ + @NotNull + public String getLanguageId(@Nullable VirtualFile file, + @NotNull Project project) { + if (file == null) { + return FileTypes.UNKNOWN.getName().toLowerCase(); + } + Language language = LSPIJUtils.getFileLanguage(file, project); + return getLanguageId(language, file.getFileType(), file.getName()); + } + + /** + * Returns the LSP language id defined in mapping otherwise the {@link Language#getID()} otherwise the {@link FileType#getName()} otherwise 'unknown'. + * + * @param language the language. + * @param fileType the file type. + * @param fileName the file name. + * @return the LSP language id. + */ + @NotNull + public String getLanguageId(@Nullable Language language, + @Nullable FileType fileType, + @NotNull String fileName) { + return getLanguageId(language, fileType, fileName, false); + } + /** + * Returns the LSP language id defined in mapping otherwise the {@link Language#getID()} otherwise the {@link FileType#getName()} otherwise 'unknown'. + * + * @param language the language. + * @param fileType the file type. + * @param fileName the file name. + * @param nullIfNotFound returns null if not found. + * @return the LSP language id. + */ + @Nullable + private String getLanguageId(@Nullable Language language, + @Nullable FileType fileType, + @NotNull String fileName, + boolean nullIfNotFound) { + // 1. Try to get the LSP languageId by using language mapping + String languageId = getLanguageId(language); + if (languageId != null) { + return languageId; + } + + // 2. Try to get the LSP languageId by using the fileType mapping + languageId = getLanguageId(fileType); + if (languageId != null) { + return languageId; + } + + // 3. Try to get the LSP languageId by using the file name pattern mapping + languageId = getLanguageId(fileName); + if (languageId != null) { + return languageId; + } + + // At this step there is no mapping + if (nullIfNotFound) { + return null; + } + + // We return the language Id if it exists or file type name + // with 'lower case' to try to map the recommended languageId specified at + // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocumentItem + if (language != null) { + // The language exists, use its ID with lower case + return language.getID().toLowerCase(); + } + // Returns the existing file type or 'unknown' with lower case + return fileName.toLowerCase(); + } + + /** + * @return The language ID that this wrapper is dealing with if defined in the + * language mapping for the language server + */ + @Nullable + public String getLanguageId(@Nullable Language language) { + while (language != null) { + String languageId = languageIdLanguageMappings.get(language); + if (languageId != null) { + return languageId; + } + language = language.getBaseLanguage(); + } + return null; + } + + /** + * @return The language ID that this wrapper is dealing with if defined in the + * file type mapping for the language server + */ + @Nullable + public String getLanguageId(@Nullable FileType fileType) { + if (fileType == null) { + return null; + } + return languageIdLanguageMappings.get(fileType); + } + /** + * @return The language ID that this wrapper is dealing with if defined in the + * file type mapping for the language server + */ + public @Nullable String getLanguageId(String filename) { + for (var mapping : languageIdFileNameMatcherMappings) { + for (var matcher : mapping.getFirst()) { + if (matcher.acceptsCharSequence(filename)) { + return mapping.getSecond(); + } + } + } + return null; + } } diff --git a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java index 5b9221d51..523d52112 100644 --- a/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java +++ b/src/main/java/com/redhat/devtools/lsp4ij/usages/LSPUsageSupport.java @@ -77,8 +77,9 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara List>> allFutures = new ArrayList<>(); for (var ls : languageServers) { + var clientFeature = ls.getClientFeatures(); // Collect declarations - if (ls.isDeclarationSupported()) { + if (clientFeature.getDeclarationFeature().isDeclarationSupported(file)) { allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() @@ -88,7 +89,7 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara } // Collect definitions - if (ls.isDefinitionSupported()) { + if (clientFeature.getDefinitionFeature().isDefinitionSupported(file)) { allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() @@ -98,7 +99,7 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara } // Collect type definitions - if (ls.isTypeDefinitionSupported()) { + if (clientFeature.getTypeDefinitionFeature().isTypeDefinitionSupported(file)) { allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() @@ -108,7 +109,7 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara } // Collect references - if (ls.isReferencesSupported()) { + if (clientFeature.getReferencesFeature().isReferencesSupported(file)) { allFutures.add( cancellationSupport.execute(ls .getTextDocumentService() @@ -118,7 +119,7 @@ protected CompletableFuture> doLoad(LSPUsageSupportPara } // Collect implementation - if (ls.isImplementationSupported()) { + if (clientFeature.getImplementationFeature().isImplementationSupported(file)) { allFutures.add( cancellationSupport.execute(ls .getTextDocumentService()