diff --git a/.vscode/launch.json b/.vscode/launch.json index 82687021c..f7d8f6439 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,15 +1,15 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "java", - "name": "Debug (Attach)", - "request": "attach", - "hostName": "localhost", - "port": 1054 - } - ] -} \ No newline at end of file + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "java", + "name": "Debug (Attach)", + "request": "attach", + "hostName": "localhost", + "port": 1054 + } + ] + } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e1212fe55..83a82a884 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,6 @@ "**/.project": true, "**/.settings": true }, - "java.saveActions.organizeImports": true, "editor.formatOnSave": true, "editor.renderWhitespace": "boundary", //Use consistent indentation diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLLanguageServer.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLLanguageServer.java index be2f1f984..b78e107be 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLLanguageServer.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLLanguageServer.java @@ -7,12 +7,12 @@ * * Contributors: * Angelo Zerr - initial API and implementation + * Red Hat Inc. - Dynamic Server capabilities */ package org.eclipse.lsp4xml; import static org.eclipse.lsp4j.jsonrpc.CompletableFutures.computeAsync; -import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -20,13 +20,11 @@ import java.util.concurrent.TimeUnit; import java.util.logging.Logger; -import org.eclipse.lsp4j.CompletionOptions; -import org.eclipse.lsp4j.DocumentLinkOptions; import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.InitializedParams; import org.eclipse.lsp4j.ServerCapabilities; import org.eclipse.lsp4j.TextDocumentPositionParams; -import org.eclipse.lsp4j.TextDocumentSyncKind; import org.eclipse.lsp4j.services.LanguageClient; import org.eclipse.lsp4j.services.LanguageServer; import org.eclipse.lsp4j.services.TextDocumentService; @@ -40,6 +38,8 @@ import org.eclipse.lsp4xml.settings.LogsSettings; import org.eclipse.lsp4xml.settings.XMLClientSettings; import org.eclipse.lsp4xml.settings.XMLFormattingOptions; +import org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesInitializer; +import org.eclipse.lsp4xml.settings.capabilities.XMLCapabilityManager; /** * XML language server. @@ -55,6 +55,7 @@ public class XMLLanguageServer implements LanguageServer, ProcessLanguageServer, private LanguageClient languageClient; private final ScheduledExecutorService delayer; private Integer parentProcessId; + public XMLCapabilityManager capabilityManager; public XMLLanguageServer() { xmlLanguageService = new XMLLanguageService(); @@ -66,24 +67,30 @@ public XMLLanguageServer() { @Override public CompletableFuture initialize(InitializeParams params) { LOGGER.info("Initializing LSP4XML server"); - updateSettings(InitializationOptionsSettings.getSettings(params)); - xmlTextDocumentService.updateClientCapabilities(params.getCapabilities()); this.parentProcessId = params.getProcessId(); - ServerCapabilities capabilities = new ServerCapabilities(); - capabilities - .setTextDocumentSync(xmlTextDocumentService.isIncrementalSupport() ? TextDocumentSyncKind.Incremental - : TextDocumentSyncKind.Full); - capabilities.setDocumentSymbolProvider(true); - capabilities.setDocumentHighlightProvider(true); - capabilities.setCompletionProvider(new CompletionOptions(false, Arrays.asList(".", ":", "<", "\"", "=", "/"))); - capabilities.setDocumentFormattingProvider(true); - capabilities.setDocumentRangeFormattingProvider(true); - capabilities.setHoverProvider(true); - capabilities.setRenameProvider(true); - capabilities.setFoldingRangeProvider(true); - capabilities.setDocumentLinkProvider(new DocumentLinkOptions(true)); - capabilities.setCodeActionProvider(true); - return CompletableFuture.completedFuture(new InitializeResult(capabilities)); + + capabilityManager.setClientCapabilities(params.getCapabilities()); + updateSettings(InitializationOptionsSettings.getSettings(params)); + + xmlTextDocumentService.updateClientCapabilities(capabilityManager.getClientCapabilities().capabilities); + ServerCapabilities nonDynamicServerCapabilities = ServerCapabilitiesInitializer.getNonDynamicServerCapabilities( + capabilityManager.getClientCapabilities(), xmlTextDocumentService.isIncrementalSupport()); + + return CompletableFuture.completedFuture(new InitializeResult(nonDynamicServerCapabilities)); + } + + /* + * Registers all capabilities that do not support client side preferences to + * turn on/off + * + * (non-Javadoc) + * + * @see org.eclipse.lsp4j.services.LanguageServer#initialized(org.eclipse.lsp4j. + * InitializedParams) + */ + @Override + public void initialized(InitializedParams params) { + capabilityManager.initializeCapabilities(); } /** @@ -96,6 +103,7 @@ public void updateSettings(Object initializationOptionsSettings) { return; } // Update client settings + XMLClientSettings clientSettings = XMLClientSettings.getSettings(initializationOptionsSettings); if (clientSettings != null) { // Update logs settings @@ -143,6 +151,7 @@ public WorkspaceService getWorkspaceService() { public void setClient(LanguageClient languageClient) { this.languageClient = languageClient; + capabilityManager = new XMLCapabilityManager(this.languageClient, xmlTextDocumentService); } public LanguageClient getLanguageClient() { diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java index da7e221be..0f7d07739 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLTextDocumentService.java @@ -66,6 +66,7 @@ import org.eclipse.lsp4xml.services.XMLLanguageService; import org.eclipse.lsp4xml.services.extensions.CompletionSettings; import org.eclipse.lsp4xml.settings.XMLExperimentalCapabilities; +import org.eclipse.lsp4xml.settings.capabilities.ClientCapabilitiesWrapper; import org.eclipse.lsp4xml.settings.XMLFormattingOptions; import org.eclipse.lsp4xml.utils.JSONUtility; @@ -329,4 +330,8 @@ public boolean isIncrementalSupport() { return documents.isIncremental(); } + public XMLFormattingOptions getSharedFormattingOptions() { + return this.sharedFormattingOptions; + } + } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLWorkspaceService.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLWorkspaceService.java index e90b8864f..57bcaa4b0 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLWorkspaceService.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/XMLWorkspaceService.java @@ -39,6 +39,7 @@ public CompletableFuture> symbol(WorkspaceSymb @Override public void didChangeConfiguration(DidChangeConfigurationParams params) { xmlLanguageServer.updateSettings(params.getSettings()); + xmlLanguageServer.capabilityManager.syncDynamicCapabilitiesWithPreferences(); } @Override diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/contentmodel/participants/XMLSchemaErrorCode.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/contentmodel/participants/XMLSchemaErrorCode.java index 007413bd7..25076fa4a 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/contentmodel/participants/XMLSchemaErrorCode.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/contentmodel/participants/XMLSchemaErrorCode.java @@ -125,6 +125,7 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj } return XMLPositionUtility.selectText(offset, document);*/ } + default: } return null; } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/XMLParser.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/XMLParser.java index 48abacdde..b6e0e156f 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/XMLParser.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/dom/XMLParser.java @@ -280,7 +280,7 @@ public XMLDocument parse(TextDocument document) { curr.addChild(textNode); break; } - + default: } token = scanner.scan(); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java index 1395f0e2d..56217f83f 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLCompletions.java @@ -139,7 +139,9 @@ public CompletionList doComplete(XMLDocument xmlDocument, Position position, Com case WithinContent: collectInsideContent(completionRequest, completionResponse); return completionResponse; + default: } + } break; case EndTagOpen: diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFoldings.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFoldings.java index 476015a03..2d26d39d0 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFoldings.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/services/XMLFoldings.java @@ -139,6 +139,7 @@ public List getFoldingRanges(TextDocument document, FoldingRangeCa } break; } + default: } token = scanner.scan(); } diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/XMLFormattingOptions.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/XMLFormattingOptions.java index e96c8859e..7ef303149 100644 --- a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/XMLFormattingOptions.java +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/XMLFormattingOptions.java @@ -26,6 +26,7 @@ public class XMLFormattingOptions extends FormattingOptions { private static final String FORMAT_COMMENTS = "formatComments"; private static final String JOIN_COMMENT_LINES = "joinCommentLines"; private static final String JOIN_CONTENT_LINES = "joinContentLines"; + private static final String ENABLED = "enabled"; public XMLFormattingOptions() { this(false); @@ -43,6 +44,7 @@ private void initializeDefaultSettings() { this.setFormatComments(true); this.setJoinCommentLines(false); this.setJoinContentLines(false); + this.setEnabled(true); } public XMLFormattingOptions(int tabSize, boolean insertSpaces) { @@ -118,6 +120,19 @@ public void setJoinContentLines(final boolean joinContentLines) { this.putBoolean(XMLFormattingOptions.JOIN_CONTENT_LINES, Boolean.valueOf(joinContentLines)); } + public boolean isEnabled() { + final Boolean value = this.getBoolean(XMLFormattingOptions.ENABLED); + if ((value != null)) { + return (value).booleanValue(); + } else { + return false; + } + } + + public void setEnabled(final boolean enabled) { + this.putBoolean(XMLFormattingOptions.ENABLED, Boolean.valueOf(enabled)); + } + public XMLFormattingOptions merge(FormattingOptions formattingOptions) { formattingOptions.entrySet().stream().forEach(entry -> // this.putIfAbsent(entry.getKey(), entry.getValue()) // diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java new file mode 100644 index 000000000..ba719bd2d --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ClientCapabilitiesWrapper.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2018 Red Hat, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + */ + +package org.eclipse.lsp4xml.settings.capabilities; + +import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.DynamicRegistrationCapabilities; +import org.eclipse.lsp4j.TextDocumentClientCapabilities; + +/** + * Determines if a client supports a specific capability dynamically + */ +public class ClientCapabilitiesWrapper { + private boolean v3Supported; + + public ClientCapabilities capabilities; + + public ClientCapabilitiesWrapper() { + this.capabilities = new ClientCapabilities(); + this.v3Supported = false; + } + public ClientCapabilitiesWrapper(ClientCapabilities capabilities) { + this.capabilities = capabilities; + this.v3Supported = capabilities != null ? capabilities.getTextDocument() != null : false; + } + + /** + * IMPORTANT + * + * This should be up to date with all Server supported capabilities + * + */ + + public boolean isCompletionDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getCompletion()); + } + + public boolean isLinkDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDocumentLink()); + } + + public boolean isRangeFoldingDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getFoldingRange()); + } + + public boolean isDocumentSyncDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getSynchronization()); + } + + public boolean isFormattingDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getFormatting()); + } + + public boolean isRangeFormattingDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getRangeFormatting()); + } + + public boolean isRenameDynamicRegistrationSupported() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getRename()); + } + + public boolean isDocumentSymbolDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDocumentSymbol()); + } + + public boolean isCodeActionDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getCodeAction()); + } + + public boolean isHoverDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getHover()); + } + + public boolean isDocumentHighlightDynamicRegistered() { + return v3Supported && isDynamicRegistrationSupported(getTextDocument().getDocumentHighlight()); + } + + private boolean isDynamicRegistrationSupported(DynamicRegistrationCapabilities capability) { + return capability != null && capability.getDynamicRegistration().booleanValue(); + } + + public TextDocumentClientCapabilities getTextDocument() { + return this.capabilities.getTextDocument(); + } + +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesConstants.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesConstants.java new file mode 100644 index 000000000..a02d59700 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesConstants.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2018 Red Hat, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + */ + +package org.eclipse.lsp4xml.settings.capabilities; + +import java.util.Arrays; +import java.util.UUID; + +import org.eclipse.lsp4j.CompletionOptions; +import org.eclipse.lsp4j.DocumentLinkOptions; +import org.eclipse.lsp4j.TextDocumentSyncKind; + +/** + * Server Capabilities Constants + */ +public class ServerCapabilitiesConstants { + + private ServerCapabilitiesConstants() { + } + + public static final String TEXT_DOCUMENT_FORMATTING = "textDocument/formatting"; + public static final String TEXT_DOCUMENT_RANGE_FORMATTING = "textDocument/rangeFormatting"; + public static final String TEXT_DOCUMENT_ON_TYPE_FORMATTING = "textDocument/onTypeFormatting"; + public static final String TEXT_DOCUMENT_CODE_LENS = "textDocument/codeLens"; + public static final String TEXT_DOCUMENT_SIGNATURE_HELP = "textDocument/signatureHelp"; + public static final String TEXT_DOCUMENT_RENAME = "textDocument/rename"; + public static final String TEXT_DOCUMENT_COMPLETION = "textDocument/completion"; + public static final String TEXT_DOCUMENT_SYNC = "textDocument/synchronization"; + public static final String TEXT_DOCUMENT_LINK = "textDocument/documentLink"; + public static final String TEXT_DOCUMENT_FOLDING_RANGE = "textDocument/foldingRange"; + public static final String TEXT_DOCUMENT_DOCUMENT_SYMBOL = "textDocument/documentSymbol"; + public static final String TEXT_DOCUMENT_CODE_ACTION = "textDocument/codeAction"; + public static final String TEXT_DOCUMENT_DEFINITION = "textDocument/definition"; + public static final String TEXT_DOCUMENT_TYPEDEFINITION = "textDocument/typeDefinition"; + public static final String TEXT_DOCUMENT_HOVER = "textDocument/hover"; + public static final String TEXT_DOCUMENT_REFERENCES = "textDocument/references"; + public static final String TEXT_DOCUMENT_HIGHLIGHT = "textDocument/documentHighlight"; + + public static final String WORKSPACE_CHANGE_FOLDERS = "workspace/didChangeWorkspaceFolders"; + public static final String WORKSPACE_EXECUTE_COMMAND = "workspace/executeCommand"; + public static final String WORKSPACE_SYMBOL = "workspace/symbol"; + public static final String WORKSPACE_WATCHED_FILES = "workspace/didChangeWatchedFiles"; + + public static final String FORMATTING_ID = UUID.randomUUID().toString(); + public static final String COMPLETION_ID = UUID.randomUUID().toString(); + public static final String SYNC_ID = UUID.randomUUID().toString(); + public static final String FOLDING_RANGE_ID = UUID.randomUUID().toString(); + public static final String LINK_ID = UUID.randomUUID().toString(); + public static final String FORMATTING_ON_TYPE_ID = UUID.randomUUID().toString(); + public static final String FORMATTING_RANGE_ID = UUID.randomUUID().toString(); + public static final String CODE_LENS_ID = UUID.randomUUID().toString(); + public static final String SIGNATURE_HELP_ID = UUID.randomUUID().toString(); + public static final String RENAME_ID = UUID.randomUUID().toString(); + public static final String EXECUTE_COMMAND_ID = UUID.randomUUID().toString(); + public static final String WORKSPACE_SYMBOL_ID = UUID.randomUUID().toString(); + public static final String DOCUMENT_SYMBOL_ID = UUID.randomUUID().toString(); + public static final String CODE_ACTION_ID = UUID.randomUUID().toString(); + public static final String DEFINITION_ID = UUID.randomUUID().toString(); + public static final String TYPEDEFINITION_ID = UUID.randomUUID().toString(); + public static final String HOVER_ID = UUID.randomUUID().toString(); + public static final String REFERENCES_ID = UUID.randomUUID().toString(); + public static final String DOCUMENT_HIGHLIGHT_ID = UUID.randomUUID().toString(); + public static final String WORKSPACE_CHANGE_FOLDERS_ID = UUID.randomUUID().toString(); + public static final String WORKSPACE_WATCHED_FILES_ID = UUID.randomUUID().toString(); + + public static final CompletionOptions DEFAULT_COMPLETION_OPTIONS = new CompletionOptions(false, Arrays.asList(".", ":", "<", "\"", "=", "/")); + public static final TextDocumentSyncKind DEFAULT_SYNC_OPTION = TextDocumentSyncKind.Full; + public static final DocumentLinkOptions DEFAULT_LINK_OPTIONS = new DocumentLinkOptions(true); +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java new file mode 100644 index 000000000..2670c01de --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/ServerCapabilitiesInitializer.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2018 Red Hat, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + */ + +package org.eclipse.lsp4xml.settings.capabilities; + +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_COMPLETION_OPTIONS; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_LINK_OPTIONS; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_SYNC_OPTION; + +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentSyncKind; + +/** + * All default capabilities of this server + */ +public class ServerCapabilitiesInitializer { + + private ServerCapabilitiesInitializer() { + } + + /** + * Returns all default server capabilities that aren't dynamic + * + * @param clientCapabilities + * @return ServerCapabilities object + */ + public static ServerCapabilities getNonDynamicServerCapabilities(ClientCapabilitiesWrapper clientCapabilities, + boolean isIncremental) { + ServerCapabilities serverCapabilities = new ServerCapabilities(); + + serverCapabilities.setTextDocumentSync(DEFAULT_SYNC_OPTION); + + serverCapabilities + .setTextDocumentSync(isIncremental ? TextDocumentSyncKind.Incremental : TextDocumentSyncKind.Full); + + serverCapabilities.setDocumentSymbolProvider(!clientCapabilities.isDocumentSymbolDynamicRegistered()); + serverCapabilities.setDocumentHighlightProvider(!clientCapabilities.isDocumentHighlightDynamicRegistered()); + serverCapabilities.setCodeActionProvider(!clientCapabilities.isCodeActionDynamicRegistered()); + serverCapabilities + .setDocumentFormattingProvider(!clientCapabilities.isFormattingDynamicRegistrationSupported()); + serverCapabilities.setDocumentRangeFormattingProvider( + !clientCapabilities.isRangeFormattingDynamicRegistrationSupported()); + serverCapabilities.setHoverProvider(!clientCapabilities.isHoverDynamicRegistered()); + serverCapabilities.setRenameProvider(!clientCapabilities.isRenameDynamicRegistrationSupported()); + serverCapabilities.setFoldingRangeProvider(!clientCapabilities.isRangeFoldingDynamicRegistrationSupported()); + + if (!clientCapabilities.isLinkDynamicRegistrationSupported()) { + serverCapabilities.setDocumentLinkProvider(DEFAULT_LINK_OPTIONS); + } + if (!clientCapabilities.isCompletionDynamicRegistrationSupported()) { + serverCapabilities.setCompletionProvider(DEFAULT_COMPLETION_OPTIONS); + } + return serverCapabilities; + } +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java new file mode 100644 index 000000000..6900e2df9 --- /dev/null +++ b/org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilityManager.java @@ -0,0 +1,166 @@ +/** + * Copyright (c) 2018 Red Hat, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + */ +package org.eclipse.lsp4xml.settings.capabilities; + +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.CODE_ACTION_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.COMPLETION_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_COMPLETION_OPTIONS; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DEFAULT_LINK_OPTIONS; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DOCUMENT_HIGHLIGHT_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.DOCUMENT_SYMBOL_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.FOLDING_RANGE_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.HOVER_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.LINK_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.RENAME_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_CODE_ACTION; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_COMPLETION; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_DOCUMENT_SYMBOL; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_FOLDING_RANGE; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HIGHLIGHT; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_HOVER; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_LINK; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.TEXT_DOCUMENT_RENAME; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.FORMATTING_ID; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.FORMATTING_RANGE_ID; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.Registration; +import org.eclipse.lsp4j.RegistrationParams; +import org.eclipse.lsp4j.Unregistration; +import org.eclipse.lsp4j.UnregistrationParams; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4xml.XMLTextDocumentService; +import org.eclipse.lsp4xml.settings.XMLFormattingOptions; + +/** + * Manager for capability related tasks + */ +public class XMLCapabilityManager { + + private ClientCapabilitiesWrapper clientWrapper; + private Set registeredCapabilities = new HashSet<>(3); + private LanguageClient languageClient; + private XMLTextDocumentService textDocumentService; + + public XMLCapabilityManager(LanguageClient languageClient, XMLTextDocumentService textDocumentService) { + this.languageClient = languageClient; + this.textDocumentService = textDocumentService; + } + + /** + * Creates and sets a {@link ClientCapabilitiesWrapper} instance formed from + * clientCapabilities + * + * @param clientCapabilities + */ + public void setClientCapabilities(ClientCapabilities clientCapabilities) { + this.clientWrapper = new ClientCapabilitiesWrapper(clientCapabilities); + } + + public ClientCapabilitiesWrapper getClientCapabilities() { + if (this.clientWrapper == null) { + this.clientWrapper = new ClientCapabilitiesWrapper(); + } + return this.clientWrapper; + } + + public void toggleCapability(boolean enabled, String id, String capability, Object options) { + if (enabled) { + registerCapability(id, capability, options); + } else { + unregisterCapability(id, capability); + } + } + + public void unregisterCapability(String id, String method) { + if (registeredCapabilities.remove(id)) { + Unregistration unregistration = new Unregistration(id, method); + UnregistrationParams unregistrationParams = new UnregistrationParams( + Collections.singletonList(unregistration)); + languageClient.unregisterCapability(unregistrationParams); + } + } + + public void registerCapability(String id, String method) { + registerCapability(id, method, null); + } + + public void registerCapability(String id, String method, Object options) { + if (registeredCapabilities.add(id)) { + Registration registration = new Registration(id, method, options); + RegistrationParams registrationParams = new RegistrationParams(Collections.singletonList(registration)); + languageClient.registerCapability(registrationParams); + } + } + + /** + * Registers all dynamic capabilities that the server does not support client + * side preferences turning on/off + */ + public void initializeCapabilities() { + if (this.getClientCapabilities().isCodeActionDynamicRegistered()) { + registerCapability(CODE_ACTION_ID, TEXT_DOCUMENT_CODE_ACTION); + } + if (this.getClientCapabilities().isCompletionDynamicRegistrationSupported()) { + registerCapability(COMPLETION_ID, TEXT_DOCUMENT_COMPLETION, DEFAULT_COMPLETION_OPTIONS); + } + if (this.getClientCapabilities().isDocumentHighlightDynamicRegistered()) { + registerCapability(DOCUMENT_HIGHLIGHT_ID, TEXT_DOCUMENT_HIGHLIGHT); + } + if (this.getClientCapabilities().isDocumentSymbolDynamicRegistered()) { + registerCapability(DOCUMENT_SYMBOL_ID, TEXT_DOCUMENT_DOCUMENT_SYMBOL); + } + if (this.getClientCapabilities().isRangeFoldingDynamicRegistrationSupported()) { + registerCapability(FOLDING_RANGE_ID, TEXT_DOCUMENT_FOLDING_RANGE); + } + if (this.getClientCapabilities().isHoverDynamicRegistered()) { + registerCapability(HOVER_ID, TEXT_DOCUMENT_HOVER); + } + if (this.getClientCapabilities().isLinkDynamicRegistrationSupported()) { + registerCapability(LINK_ID, TEXT_DOCUMENT_LINK, DEFAULT_LINK_OPTIONS); + } + if (this.getClientCapabilities().isRenameDynamicRegistrationSupported()) { + registerCapability(RENAME_ID, TEXT_DOCUMENT_RENAME); + } + + syncDynamicCapabilitiesWithPreferences(); + } + + /** + * Registers all capabilities that this server can support client side + * preferences to turn on/off + * + * If a capability is not dynamic, it's handled by + * {@link ServerCapabilitiesInitializer} + */ + public void syncDynamicCapabilitiesWithPreferences() { + XMLFormattingOptions formattingPreferences = this.textDocumentService.getSharedFormattingOptions(); + + if (this.getClientCapabilities().isFormattingDynamicRegistrationSupported()) { + toggleCapability(formattingPreferences.isEnabled(), FORMATTING_ID, + ServerCapabilitiesConstants.TEXT_DOCUMENT_FORMATTING, null); + } + + if (this.getClientCapabilities().isRangeFormattingDynamicRegistrationSupported()) { + toggleCapability(formattingPreferences.isEnabled(), FORMATTING_RANGE_ID, + ServerCapabilitiesConstants.TEXT_DOCUMENT_RANGE_FORMATTING, null); + } + } + + public Set getRegisteredCapabilities() { + return this.registeredCapabilities; + } + +} \ No newline at end of file diff --git a/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilitiesTest.java b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilitiesTest.java new file mode 100644 index 000000000..1f9564a6c --- /dev/null +++ b/org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/settings/capabilities/XMLCapabilitiesTest.java @@ -0,0 +1,246 @@ +/** + * Copyright (c) 2018 Red Hat, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Red Hat Inc. - initial API and implementation + */ + +package org.eclipse.lsp4xml.settings.capabilities; + +import static org.junit.Assert.assertEquals; + +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.ClientCapabilities; +import org.eclipse.lsp4j.CodeActionCapabilities; +import org.eclipse.lsp4j.CompletionCapabilities; +import org.eclipse.lsp4j.DocumentHighlightCapabilities; +import org.eclipse.lsp4j.DocumentLinkCapabilities; +import org.eclipse.lsp4j.DocumentSymbolCapabilities; +import org.eclipse.lsp4j.FoldingRangeCapabilities; +import org.eclipse.lsp4j.FormattingCapabilities; +import org.eclipse.lsp4j.HoverCapabilities; +import org.eclipse.lsp4j.MessageActionItem; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.RangeFormattingCapabilities; +import org.eclipse.lsp4j.RegistrationParams; +import org.eclipse.lsp4j.RenameCapabilities; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.ShowMessageRequestParams; +import org.eclipse.lsp4j.TextDocumentClientCapabilities; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4xml.XMLTextDocumentService; +import org.eclipse.lsp4xml.settings.XMLFormattingOptions; +import org.junit.Before; +import org.junit.Test; + +import org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesInitializer; +import static org.eclipse.lsp4xml.settings.capabilities.ServerCapabilitiesConstants.*; + +/** + * XMLCapabilityManagerTest + */ +public class XMLCapabilitiesTest { + + private LanguageClient languageClient = new LanguageClientMock(); + private XMLCapabilityManager manager; + private ClientCapabilities clientCapabilities; + private TextDocumentClientCapabilities textDocument; + private XMLTextDocumentService textDocumentService; + private Set capabilityIDs; + + @Before + public void startup() { + XMLFormattingOptions formattingOptions = new XMLFormattingOptions(); + formattingOptions.setEnabled(true); + + textDocumentService = new XMLTextDocumentService(null); + textDocumentService.setSharedFormattingOptions(formattingOptions); + + textDocument = new TextDocumentClientCapabilities(); + manager = new XMLCapabilityManager(languageClient, textDocumentService); + clientCapabilities = new ClientCapabilities(); + capabilityIDs = null; + + } + + @Test + public void testAllDynamicCapabilities() { + setAllCapabilities(true); + setAndInitializeCapabilities(); + + assertEquals(10, capabilityIDs.size()); + + ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer + .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); + assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(false, serverCapabilities.getHoverProvider()); + assertEquals(false, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(false, serverCapabilities.getRenameProvider()); + assertEquals(false, serverCapabilities.getFoldingRangeProvider().getLeft()); + assertEquals(false, serverCapabilities.getCodeActionProvider()); + assertEquals(null, serverCapabilities.getCompletionProvider()); + assertEquals(null, serverCapabilities.getDocumentLinkProvider()); + } + + @Test + public void testNoDynamicCapabilities() { + setAllCapabilities(false); + setAndInitializeCapabilities(); + + assertEquals(0, capabilityIDs.size()); + + ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer + .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); + assertEquals(true, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(true, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(true, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(true, serverCapabilities.getHoverProvider()); + assertEquals(true, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(true, serverCapabilities.getRenameProvider()); + assertEquals(true, serverCapabilities.getFoldingRangeProvider().getLeft()); + assertEquals(true, serverCapabilities.getCodeActionProvider()); + assertEquals(DEFAULT_COMPLETION_OPTIONS, serverCapabilities.getCompletionProvider()); + assertEquals(DEFAULT_LINK_OPTIONS, serverCapabilities.getDocumentLinkProvider()); + } + + @Test + public void testBothCapabilityTypes() { + // Dynamic capabilities + textDocument.setRangeFormatting(new RangeFormattingCapabilities(true)); + textDocument.setFormatting(new FormattingCapabilities(true)); + CompletionCapabilities completion = new CompletionCapabilities(); + completion.setDynamicRegistration(true); + textDocument.setCompletion(completion); + textDocument.setDocumentSymbol(new DocumentSymbolCapabilities(true)); + + // Non dynamic capabilities + textDocument.setHover(new HoverCapabilities(false)); + textDocument.setDocumentHighlight(new DocumentHighlightCapabilities(false)); + textDocument.setRename(new RenameCapabilities(false)); + FoldingRangeCapabilities folding = new FoldingRangeCapabilities(); + folding.setDynamicRegistration(false); + textDocument.setFoldingRange(folding); + textDocument.setDocumentLink(new DocumentLinkCapabilities(false)); + textDocument.setCodeAction(new CodeActionCapabilities(false)); + + setAndInitializeCapabilities(); + + assertEquals(4, capabilityIDs.size()); + assertEquals(true, capabilityIDs.contains(FORMATTING_ID)); + assertEquals(true, capabilityIDs.contains(FORMATTING_RANGE_ID)); + assertEquals(true, capabilityIDs.contains(COMPLETION_ID)); + assertEquals(true, capabilityIDs.contains(DOCUMENT_SYMBOL_ID)); + + ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer + .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); + assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentSymbolProvider()); + assertEquals(true, serverCapabilities.getHoverProvider()); + assertEquals(true, serverCapabilities.getDocumentHighlightProvider()); + assertEquals(true, serverCapabilities.getRenameProvider()); + assertEquals(true, serverCapabilities.getFoldingRangeProvider().getLeft()); + assertEquals(true, serverCapabilities.getCodeActionProvider()); + assertEquals(null, serverCapabilities.getCompletionProvider()); + assertEquals(DEFAULT_LINK_OPTIONS, serverCapabilities.getDocumentLinkProvider()); + } + + @Test + public void testDynamicFormattingWithPreferenceFalse() { + textDocumentService.getSharedFormattingOptions().setEnabled(false); + // Non Dynamic capabilities + textDocument.setRangeFormatting(new RangeFormattingCapabilities(true)); + textDocument.setFormatting(new FormattingCapabilities(true)); + + setAndInitializeCapabilities(); + + Set capabilityIDs = manager.getRegisteredCapabilities(); + assertEquals(0, capabilityIDs.size()); + + ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer + .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); + assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + } + + @Test + public void testDynamicFormattingWithPreferenceTrue() { + textDocumentService.getSharedFormattingOptions().setEnabled(true); + // Dynamic capabilities + textDocument.setRangeFormatting(new RangeFormattingCapabilities(true)); + textDocument.setFormatting(new FormattingCapabilities(true)); + + setAndInitializeCapabilities(); + + Set capabilityIDs = manager.getRegisteredCapabilities(); + assertEquals(2, capabilityIDs.size()); + assertEquals(true, capabilityIDs.contains(FORMATTING_ID)); + assertEquals(true, capabilityIDs.contains(FORMATTING_RANGE_ID)); + + ServerCapabilities serverCapabilities = ServerCapabilitiesInitializer + .getNonDynamicServerCapabilities(manager.getClientCapabilities(), false); + assertEquals(false, serverCapabilities.getDocumentRangeFormattingProvider()); + assertEquals(false, serverCapabilities.getDocumentFormattingProvider()); + } + + private void setAllCapabilities(boolean areAllDynamic) { + textDocument.setRangeFormatting(new RangeFormattingCapabilities(areAllDynamic)); + textDocument.setFormatting(new FormattingCapabilities(areAllDynamic)); + CompletionCapabilities completion = new CompletionCapabilities(); + completion.setDynamicRegistration(areAllDynamic); + textDocument.setCompletion(completion); + textDocument.setDocumentSymbol(new DocumentSymbolCapabilities(areAllDynamic)); + textDocument.setHover(new HoverCapabilities(areAllDynamic)); + textDocument.setDocumentHighlight(new DocumentHighlightCapabilities(areAllDynamic)); + textDocument.setRename(new RenameCapabilities(areAllDynamic)); + FoldingRangeCapabilities folding = new FoldingRangeCapabilities(); + folding.setDynamicRegistration(areAllDynamic); + textDocument.setFoldingRange(folding); + textDocument.setDocumentLink(new DocumentLinkCapabilities(areAllDynamic)); + textDocument.setCodeAction(new CodeActionCapabilities(areAllDynamic)); + } + + private void setAndInitializeCapabilities() { + clientCapabilities.setTextDocument(textDocument); + manager.setClientCapabilities(clientCapabilities); + manager.initializeCapabilities(); + capabilityIDs = manager.getRegisteredCapabilities(); + } + + class LanguageClientMock implements LanguageClient { + @Override + public void telemetryEvent(Object object) { + } + + @Override + public void publishDiagnostics(PublishDiagnosticsParams diagnostics) { + } + + @Override + public void showMessage(MessageParams messageParams) { + } + + @Override + public CompletableFuture showMessageRequest(ShowMessageRequestParams requestParams) { + return null; + } + + @Override + public void logMessage(MessageParams message) { + } + + @Override + public CompletableFuture registerCapability(RegistrationParams params) { + return null; + } + } +} \ No newline at end of file