From bbc050b023d543b99663666d686724a33772d17f Mon Sep 17 00:00:00 2001 From: Adam Knapp Date: Wed, 13 Sep 2023 11:14:23 +0200 Subject: [PATCH] Adding Did Change Configuration feature Signed-off-by: Adam Knapp --- org.eclipse.lsp4e/schema/languageServer.exsd | 10 +++ .../eclipse/lsp4e/IConfigurationHandler.java | 74 +++++++++++++++++++ .../eclipse/lsp4e/LanguageServerWrapper.java | 19 +++++ .../lsp4e/LanguageServersRegistry.java | 18 +++++ .../lsp4e/internal/SupportedFeatures.java | 2 + 5 files changed, 123 insertions(+) create mode 100644 org.eclipse.lsp4e/src/org/eclipse/lsp4e/IConfigurationHandler.java diff --git a/org.eclipse.lsp4e/schema/languageServer.exsd b/org.eclipse.lsp4e/schema/languageServer.exsd index ad3dfe7e8..0116f5e12 100644 --- a/org.eclipse.lsp4e/schema/languageServer.exsd +++ b/org.eclipse.lsp4e/schema/languageServer.exsd @@ -151,6 +151,16 @@ If set to a number bigger than zero, the server will run until the timeout is re + + + + An optional configuration handler that enables to send and update the configuration of the language server. The initial configuration is sent after receiving the initialized notification message from the language server. + + + + + + diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IConfigurationHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IConfigurationHandler.java new file mode 100644 index 000000000..ae735629e --- /dev/null +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IConfigurationHandler.java @@ -0,0 +1,74 @@ +package org.eclipse.lsp4e; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * Configuration handler that enables to send and update + * the configuration of the language server. + * It also contains a default implementation that can be instantiated. + */ +public interface IConfigurationHandler { + + /** + * Returns a {@link Map} containing all preference names and their values. + * Usually used during the initialization of the language server to send + * all the configuration at once. + * @return + */ + @SuppressWarnings("null") + default @NonNull Map getConfiguration() { + return Collections.emptyMap(); + } + + /** + * Returns a {@link Map} containing all preference names and their values. + * Used to send multiple configuration change to the language server at once. + * @return + */ + @SuppressWarnings("null") + default @NonNull Map getConfiguration(final List prefList) { + return Collections.emptyMap(); + } + + /** + * Returns a {@link Map} containing the requested preference name and its value, + * usually used by a preference change listener. + * The Map will be converted to JSON by LSP4J before the transmission. + * @param preferenceName name of preference + * @return {@link Map} containing the requested preference name and its value + */ + @SuppressWarnings("null") + default @NonNull Map getConfiguration(final String prefName) { + return Collections.emptyMap(); + } + + /** + * Returns the preference store containing the configuration of the language server. + * This must be overridden by the plugin developer, usually with the actual + * {@code Activator.getDefault().getPreferenceStore()} + * @see AbstractUIPlugin#getPreferenceStore() + * @return the preference store + */ + public @NonNull IPreferenceStore getPreferenceStore(); + + /** + * Default implementation that can be instantiated + */ + final static class DefaultConfigurationHandler implements IConfigurationHandler { + public DefaultConfigurationHandler() { + // Do nothing + } + + @SuppressWarnings("null") + @Override + public @NonNull IPreferenceStore getPreferenceStore() { + return LanguageServerPlugin.getDefault().getPreferenceStore(); + } + } +} diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java index 3cc7dd266..6bb7f0da0 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServerWrapper.java @@ -65,6 +65,8 @@ import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.lsp4e.LanguageServersRegistry.LanguageServerDefinition; import org.eclipse.lsp4e.internal.FileBufferListenerAdapter; import org.eclipse.lsp4e.internal.SupportedFeatures; @@ -74,6 +76,7 @@ import org.eclipse.lsp4j.ClientCapabilities; import org.eclipse.lsp4j.ClientInfo; import org.eclipse.lsp4j.CodeActionOptions; +import org.eclipse.lsp4j.DidChangeConfigurationParams; import org.eclipse.lsp4j.DidChangeWorkspaceFoldersParams; import org.eclipse.lsp4j.DocumentFormattingOptions; import org.eclipse.lsp4j.DocumentRangeFormattingOptions; @@ -142,6 +145,15 @@ public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) { }; + private final IPropertyChangeListener propertyChangeListener = new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + final IConfigurationHandler configHandler = serverDefinition.getConfigurationHandler(); + languageServer.getWorkspaceService().didChangeConfiguration( + new DidChangeConfigurationParams(configHandler.getConfiguration(event.getProperty()))); + } + }; + @NonNull public final LanguageServerDefinition serverDefinition; @Nullable @@ -307,6 +319,11 @@ public synchronized void start() throws IOException { this.initiallySupportsWorkspaceFolders = supportsWorkspaceFolders(serverCapabilities); }).thenRun(() -> { this.languageServer.initialized(new InitializedParams()); + }).thenRun(() -> { + final IConfigurationHandler configHandler = serverDefinition.getConfigurationHandler(); + this.languageServer.getWorkspaceService().didChangeConfiguration( + new DidChangeConfigurationParams(configHandler.getConfiguration())); + configHandler.getPreferenceStore().addPropertyChangeListener(propertyChangeListener); }).thenRun(() -> { final Map toReconnect = filesToReconnect; initializeFuture.thenRunAsync(() -> { @@ -495,6 +512,8 @@ public synchronized void stop() { this.languageServer = null; FileBuffers.getTextFileBufferManager().removeFileBufferListener(fileBufferListener); + this.serverDefinition.getConfigurationHandler() + .getPreferenceStore().removePropertyChangeListener(propertyChangeListener); } public @Nullable CompletableFuture<@NonNull LanguageServerWrapper> connect(IDocument document, @NonNull IFile file) diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java index c77e92df5..2076c47d4 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java @@ -89,6 +89,7 @@ public class LanguageServersRegistry { private static final String LABEL_ATTRIBUTE = "label"; //$NON-NLS-1$ private static final String ENABLED_WHEN_ATTRIBUTE = "enabledWhen"; //$NON-NLS-1$ private static final String ENABLED_WHEN_DESC = "description"; //$NON-NLS-1$ + private static final String CONFIGURATION_HANDLER_ATTRIBUTE = "configurationHandler"; //$NON-NLS-1$ public abstract static class LanguageServerDefinition { public final @NonNull String id; @@ -123,6 +124,10 @@ public Launcher.Builder createLauncherBuilder() { return new Launcher.Builder<>(); } + public IConfigurationHandler getConfigurationHandler() { + return new IConfigurationHandler.DefaultConfigurationHandler(); + } + } static class ExtensionLanguageServerDefinition extends LanguageServerDefinition { @@ -224,6 +229,19 @@ public Launcher.Builder createLauncherBuilder() { return super.createLauncherBuilder(); } + @Override + public IConfigurationHandler getConfigurationHandler() { + final String configHandler = extension.getAttribute(CONFIGURATION_HANDLER_ATTRIBUTE); + if (configHandler != null && !configHandler.isEmpty()) { + try { + return (IConfigurationHandler) extension.createExecutableExtension(CONFIGURATION_HANDLER_ATTRIBUTE); + } catch (CoreException e) { + LanguageServerPlugin.logError(e); + } + } + return super.getConfigurationHandler(); + } + } static class LaunchConfigurationLanguageServerDefinition extends LanguageServerDefinition { diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java index 7b387d898..578aa364a 100644 --- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java +++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/internal/SupportedFeatures.java @@ -30,6 +30,7 @@ import org.eclipse.lsp4j.CompletionItemResolveSupportCapabilities; import org.eclipse.lsp4j.CompletionListCapabilities; import org.eclipse.lsp4j.DefinitionCapabilities; +import org.eclipse.lsp4j.DidChangeConfigurationCapabilities; import org.eclipse.lsp4j.DocumentHighlightCapabilities; import org.eclipse.lsp4j.DocumentLinkCapabilities; import org.eclipse.lsp4j.DocumentSymbolCapabilities; @@ -142,6 +143,7 @@ public class SupportedFeatures { workspaceClientCapabilities.setWorkspaceEdit(editCapabilities); CodeLensWorkspaceCapabilities codeLensWorkspaceCapabilities = new CodeLensWorkspaceCapabilities(true); workspaceClientCapabilities.setCodeLens(codeLensWorkspaceCapabilities); + workspaceClientCapabilities.setDidChangeConfiguration(new DidChangeConfigurationCapabilities(Boolean.FALSE)); return workspaceClientCapabilities; }