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;
}