Repository: sublimelsp/LSP

Issue:
When modifying a setting in an LSP-foo.sublime-settings file, LSP does not pick up the change in state
**Describe the bug**
State changes are not detected when changing settings in LSP-foo helper packages.

**To Reproduce**
Steps to reproduce the behavior:
1. Install LSP-eslint
2. Set the lint behavior to "onSave"
3. See eslint still linting on every keypress

**Expected behavior**
As soon as a setting is changed, LSP should either:

- notify workspace/didChangeConfiguration, or
- restart the language server

Because we cannot distinguish between these two cases, we should restart the language server. We cannot distinguish between the two because:

- We need to register a callback when the associated `sublime.Settings` object changed (for the LSP-foo.sublime-settings)
- We only know that something changed, not which keys were modified
- Because these files now contain both the server configuration as well as the server settings we must assume that the server configuration changed so we must restart the server.

**Environment**
- Sublime Text version: 4090
- LSP version: st4000-exploration branch
- Language servers used: LSP-eslint


Discussion:

Resolving patch:
diff --git a/plugin/core/sessions.py b/plugin/core/sessions.py
--- a/plugin/core/sessions.py
+++ b/plugin/core/sessions.py
@@ -24,6 +24,7 @@
 from .types import DocumentSelector
 from .types import method_to_capability
 from .types import ResolvedStartupConfig
+from .types import SettingsRegistration
 from .typing import Callable, Dict, Any, Optional, List, Tuple, Generator, Type, Protocol, Mapping, Union
 from .url import uri_to_filename
 from .version import __version__
@@ -540,7 +541,7 @@ def on_pre_server_command(self, command: Mapping[str, Any], done_callback: Calla
         return False
 
 
-_plugins = {}  # type: Dict[str, Type[AbstractPlugin]]
+_plugins = {}  # type: Dict[str, Tuple[Type[AbstractPlugin], SettingsRegistration]]
 
 
 def _register_plugin_impl(plugin: Type[AbstractPlugin], notify_listener: bool) -> None:
@@ -549,7 +550,8 @@ def _register_plugin_impl(plugin: Type[AbstractPlugin], notify_listener: bool) -
     try:
         settings, base_file = plugin.configuration()
         if client_configs.add_external_config(name, settings, base_file, notify_listener):
-            _plugins[name] = plugin
+            on_change = functools.partial(client_configs.update_external_config, name, settings, base_file)
+            _plugins[name] = (plugin, SettingsRegistration(settings, on_change))
     except Exception as ex:
         exception_log('Failed to register plugin "{}"'.format(name), ex)
 
@@ -618,7 +620,8 @@ def unregister_plugin(plugin: Type[AbstractPlugin]) -> None:
 
 def get_plugin(name: str) -> Optional[Type[AbstractPlugin]]:
     global _plugins
-    return _plugins.get(name, None)
+    tup = _plugins.get(name, None)
+    return tup[0] if tup else None
 
 
 class Logger(metaclass=ABCMeta):
diff --git a/plugin/core/settings.py b/plugin/core/settings.py
--- a/plugin/core/settings.py
+++ b/plugin/core/settings.py
@@ -58,6 +58,12 @@ def remove_external_config(self, name: str) -> None:
         if self.all.pop(name, None):
             self._notify_listener()
 
+    def update_external_config(self, name: str, s: sublime.Settings, file: str) -> None:
+        config = ClientConfig.from_sublime_settings(name, s, file)
+        self.external[name] = config
+        self.all[name] = config
+        self._notify_listener()
+
     def update_configs(self) -> None:
         global _settings_obj
         if _settings_obj is None:


Test patch: