diff --git a/plugin/__init__.py b/plugin/__init__.py index fe0213c23..3d1103421 100644 --- a/plugin/__init__.py +++ b/plugin/__init__.py @@ -9,8 +9,10 @@ from .core.protocol import Notification from .core.protocol import Request from .core.protocol import Response +from .core.registry import AbstractViewListener from .core.registry import LspTextCommand from .core.registry import LspWindowCommand +from .core.registry import windows from .core.sessions import AbstractPlugin from .core.sessions import register_plugin from .core.sessions import Session @@ -25,12 +27,14 @@ from .core.version import __version__ from .core.views import MarkdownLangMap from .core.views import uri_from_view +from .core.windows import GlobalLspListener from .core.workspace import WorkspaceFolder # This is the public API for LSP-* packages __all__ = [ '__version__', 'AbstractPlugin', + 'AbstractViewListener', 'apply_text_edits', 'ClientConfig', 'css', @@ -41,6 +45,7 @@ 'FileWatcherEvent', 'FileWatcherEventType', 'FileWatcherProtocol', + 'GlobalLspListener', 'LspTextCommand', 'LspWindowCommand', 'MarkdownLangMap', @@ -56,5 +61,6 @@ 'unregister_plugin', 'uri_from_view', 'uri_to_filename', # deprecated + 'windows', 'WorkspaceFolder', ] diff --git a/plugin/core/windows.py b/plugin/core/windows.py index 27cd45786..a441cf252 100644 --- a/plugin/core/windows.py +++ b/plugin/core/windows.py @@ -35,6 +35,8 @@ from .views import make_link from .workspace import ProjectFolders from .workspace import sorted_workspace_folders +from abc import ABCMeta +from abc import abstractmethod from collections import deque from collections import OrderedDict from datetime import datetime @@ -71,11 +73,29 @@ def set_diagnostics_count(view: sublime.View, errors: int, warnings: int) -> Non pass +class GlobalLspListener(metaclass=ABCMeta): + + @abstractmethod + def on_session_view_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + raise NotImplementedError() + + @abstractmethod + def on_session_view_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: + raise NotImplementedError() + + class WindowManager(Manager, WindowConfigChangeListener): - def __init__(self, window: sublime.Window, workspace: ProjectFolders, config_manager: WindowConfigManager) -> None: + def __init__( + self, + window: sublime.Window, + workspace: ProjectFolders, + config_manager: WindowConfigManager, + global_listener: GlobalLspListener, + ) -> None: self._window = window self._config_manager = config_manager + self._global_listener = global_listener self._sessions: set[Session] = set() self._workspace = workspace self._pending_listeners: deque[AbstractViewListener] = deque() @@ -199,6 +219,8 @@ def _publish_sessions_to_listener_async(self, listener: AbstractViewListener) -> except Exception as ex: message = f"failed to register session {session.config.name} to listener {listener}" exception_log(message, ex) + return + self._global_listener.on_session_view_initialized_async(listener, session) def sessions(self, view: sublime.View, capability: str | None = None) -> Generator[Session, None, None]: inside_workspace = self._workspace.contains(view) @@ -388,6 +410,7 @@ def on_post_exit_async(self, session: Session, exit_code: int, exception: Except self._sessions.discard(session) for listener in self._listeners: listener.on_session_shutdown_async(session) + self._global_listener.on_session_view_shutdown_async(listener, session) if exit_code != 0 or exception: config = session.config restart = self._config_manager.record_crash(config.name, exit_code, exception) @@ -519,12 +542,19 @@ def on_configs_changed(self, config_name: str | None = None) -> None: sublime.set_timeout_async(lambda: self.restart_sessions_async(config_name)) -class WindowRegistry(LspSettingsChangeListener): +class WindowRegistry(LspSettingsChangeListener, GlobalLspListener): def __init__(self) -> None: self._enabled = False self._windows: dict[int, WindowManager] = {} + self._global_listeners: set[GlobalLspListener] = set() client_configs.set_listener(self) + def add_global_listener(self, listener: GlobalLspListener) -> None: + self._global_listeners.add(listener) + + def remove_global_listener(self, listener: GlobalLspListener) -> None: + self._global_listeners.discard(listener) + def enable(self) -> None: self._enabled = True # Initialize manually at plugin_loaded as we'll miss out on "on_new_window_async" events. @@ -548,7 +578,7 @@ def lookup(self, window: sublime.Window | None) -> WindowManager | None: return wm workspace = ProjectFolders(window) window_config_manager = WindowConfigManager(window, client_configs.all) - manager = WindowManager(window, workspace, window_config_manager) + manager = WindowManager(window, workspace, window_config_manager, self) self._windows[window.id()] = manager return manager @@ -578,6 +608,16 @@ def on_client_config_updated(self, config_name: str | None = None) -> None: def on_userprefs_updated(self) -> None: sublime.set_timeout_async(self._on_userprefs_updated_async) + # --- Implements GlobalLspListener --------------------------------------------------------------------------------- + + def on_session_view_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + for listener in self._global_listeners: + listener.on_session_view_initialized_async(view_listener, session) + + def on_session_view_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: + for listener in self._global_listeners: + listener.on_session_view_shutdown_async(view_listener, session) + class RequestTimeTracker: def __init__(self) -> None: