From 770d53c7669f08691e1b2558ad7c2ec669619493 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Sat, 21 Sep 2024 23:54:19 +0200 Subject: [PATCH 1/5] feat: add GlobalLspListener --- plugin/__init__.py | 6 ++++++ plugin/core/windows.py | 45 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/plugin/__init__.py b/plugin/__init__.py index 3f009dd68..cd206c665 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 @@ -24,12 +26,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', @@ -39,6 +43,7 @@ 'FileWatcherEvent', 'FileWatcherEventType', 'FileWatcherProtocol', + 'GlobalLspListener', 'LspTextCommand', 'LspWindowCommand', 'MarkdownLangMap', @@ -54,5 +59,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 2c678756f..4f0d1c823 100644 --- a/plugin/core/windows.py +++ b/plugin/core/windows.py @@ -34,6 +34,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 @@ -70,11 +72,29 @@ def set_diagnostics_count(view: sublime.View, errors: int, warnings: int) -> Non pass +class GlobalLspListener(metaclass=ABCMeta): + + @abstractmethod + def on_session_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + raise NotImplementedError() + + @abstractmethod + def on_session_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() @@ -198,6 +218,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_initialized_async(listener, session) def sessions(self, view: sublime.View, capability: str | None = None) -> Generator[Session, None, None]: inside_workspace = self._workspace.contains(view) @@ -387,6 +409,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_shutdown_async(listener, session) if exit_code != 0 or exception: config = session.config restart = self._config_manager.record_crash(config.name, exit_code, exception) @@ -518,12 +541,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: +class WindowRegistry(GlobalLspListener): def __init__(self) -> None: self._enabled = False self._windows: dict[int, WindowManager] = {} + self._global_listeners: set[GlobalLspListener] = set() client_configs.set_listener(self._on_client_config_updated) + 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 _on_client_config_updated(self, config_name: str | None = None) -> None: for wm in self._windows.values(): wm.get_config_manager().update(config_name) @@ -551,7 +581,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 @@ -566,6 +596,15 @@ def discard(self, window: sublime.Window) -> None: if wm: sublime.set_timeout_async(wm.destroy) + # --- Implements GlobalLspListener --------------------------------------------------------------------------------- + + def on_session_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + for listener in self._global_listeners: + listener.on_session_initialized_async(view_listener, session) + + def on_session_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: + for listener in self._global_listeners: + listener.on_session_shutdown_async(view_listener, session) class RequestTimeTracker: def __init__(self) -> None: From 0b82ef9e7763b8ea948fa8a7ebbf731eebf3c58b Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Sun, 29 Sep 2024 22:06:09 +0200 Subject: [PATCH 2/5] blank --- plugin/core/windows.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/core/windows.py b/plugin/core/windows.py index 4f0d1c823..f9d698902 100644 --- a/plugin/core/windows.py +++ b/plugin/core/windows.py @@ -606,6 +606,7 @@ def on_session_shutdown_async(self, view_listener: AbstractViewListener, session for listener in self._global_listeners: listener.on_session_shutdown_async(view_listener, session) + class RequestTimeTracker: def __init__(self) -> None: self._start_times: dict[int, float] = {} From 32b6fdf26a78c90da46fd274e4b73d4c38bd5ffd Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Mon, 30 Sep 2024 11:09:22 +0200 Subject: [PATCH 3/5] remove unnecessary use of generators for session retrieval --- plugin/core/registry.py | 6 +++--- plugin/core/sessions.py | 6 +++--- plugin/documents.py | 22 ++++++++++++---------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/plugin/core/registry.py b/plugin/core/registry.py index 2a1e5a2a3..5ee33b6c2 100644 --- a/plugin/core/registry.py +++ b/plugin/core/registry.py @@ -12,7 +12,7 @@ from .windows import WindowManager from .windows import WindowRegistry from functools import partial -from typing import Any, Generator, Iterable +from typing import Any, Generator import operator import sublime import sublime_plugin @@ -21,14 +21,14 @@ windows = WindowRegistry() -def best_session(view: sublime.View, sessions: Iterable[Session], point: int | None = None) -> Session | None: +def best_session(view: sublime.View, sessions: list[Session], point: int | None = None) -> Session | None: if point is None: try: point = view.sel()[0].b except IndexError: return None try: - return max(sessions, key=lambda s: view.score_selector(point, s.config.priority_selector)) # type: ignore + return max(sessions, key=lambda s: view.score_selector(point, s.config.priority_selector)) except ValueError: return None diff --git a/plugin/core/sessions.py b/plugin/core/sessions.py index ccb30b429..694dadfb1 100644 --- a/plugin/core/sessions.py +++ b/plugin/core/sessions.py @@ -701,15 +701,15 @@ def session_async(self, capability: str, point: int | None = None) -> Session | raise NotImplementedError() @abstractmethod - def sessions_async(self, capability: str | None = None) -> Generator[Session, None, None]: + def sessions_async(self, capability: str | None = None) -> list[Session]: raise NotImplementedError() @abstractmethod - def session_buffers_async(self, capability: str | None = None) -> Generator[SessionBufferProtocol, None, None]: + def session_buffers_async(self, capability: str | None = None) -> list[SessionBufferProtocol]: raise NotImplementedError() @abstractmethod - def session_views_async(self) -> Generator[SessionViewProtocol, None, None]: + def session_views_async(self) -> list[SessionViewProtocol]: raise NotImplementedError() @abstractmethod diff --git a/plugin/documents.py b/plugin/documents.py index 901d81fda..4ae268c83 100644 --- a/plugin/documents.py +++ b/plugin/documents.py @@ -319,13 +319,14 @@ def _update_diagnostic_in_status_bar_async(self) -> None: return self.view.erase_status(self.ACTIVE_DIAGNOSTIC) - def session_buffers_async(self, capability: str | None = None) -> Generator[SessionBuffer, None, None]: - for sv in self.session_views_async(): - if capability is None or sv.has_capability_async(capability): - yield sv.session_buffer + def session_buffers_async(self, capability: str | None = None) -> list[SessionBuffer]: + return [ + sv.session_buffer for sv in self.session_views_async() + if capability is None or sv.has_capability_async(capability) + ] - def session_views_async(self) -> Generator[SessionView, None, None]: - yield from self._session_views.values() + def session_views_async(self) -> list[SessionView]: + return list(self._session_views.values()) def on_text_changed_async(self, change_count: int, changes: Iterable[sublime.TextChange]) -> None: if self.view.is_primary(): @@ -857,10 +858,11 @@ def _on_initial_folding_ranges(self, kinds: list[str], response: list[FoldingRan def session_async(self, capability: str, point: int | None = None) -> Session | None: return best_session(self.view, self.sessions_async(capability), point) - def sessions_async(self, capability: str | None = None) -> Generator[Session, None, None]: - for sb in self.session_buffers_async(): - if capability is None or sb.has_capability(capability): - yield sb.session + def sessions_async(self, capability: str | None = None) -> list[Session]: + return [ + sb.session for sb in self.session_buffers_async() + if capability is None or sb.has_capability(capability) + ] def session_by_name(self, name: str | None = None) -> Session | None: for sb in self.session_buffers_async(): From 013aaf05b12ffac99167e7a35f497f460f6b3631 Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Fri, 4 Oct 2024 10:00:41 +0200 Subject: [PATCH 4/5] doesn't belong --- plugin/core/registry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/core/registry.py b/plugin/core/registry.py index 5ee33b6c2..5c3a63321 100644 --- a/plugin/core/registry.py +++ b/plugin/core/registry.py @@ -12,7 +12,7 @@ from .windows import WindowManager from .windows import WindowRegistry from functools import partial -from typing import Any, Generator +from typing import Any, Generator, Iterable import operator import sublime import sublime_plugin @@ -21,7 +21,7 @@ windows = WindowRegistry() -def best_session(view: sublime.View, sessions: list[Session], point: int | None = None) -> Session | None: +def best_session(view: sublime.View, sessions: Iterable[Session], point: int | None = None) -> Session | None: if point is None: try: point = view.sel()[0].b From b43942f3debf9a31418241734eb2429015f3984c Mon Sep 17 00:00:00 2001 From: Rafal Chlodnicki Date: Mon, 14 Oct 2024 13:37:38 +0200 Subject: [PATCH 5/5] better name --- plugin/core/windows.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin/core/windows.py b/plugin/core/windows.py index 8b960d9ef..a441cf252 100644 --- a/plugin/core/windows.py +++ b/plugin/core/windows.py @@ -76,11 +76,11 @@ def set_diagnostics_count(view: sublime.View, errors: int, warnings: int) -> Non class GlobalLspListener(metaclass=ABCMeta): @abstractmethod - def on_session_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + def on_session_view_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: raise NotImplementedError() @abstractmethod - def on_session_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: + def on_session_view_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: raise NotImplementedError() @@ -220,7 +220,7 @@ def _publish_sessions_to_listener_async(self, listener: AbstractViewListener) -> message = f"failed to register session {session.config.name} to listener {listener}" exception_log(message, ex) return - self._global_listener.on_session_initialized_async(listener, session) + 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) @@ -410,7 +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_shutdown_async(listener, 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) @@ -610,13 +610,13 @@ def on_userprefs_updated(self) -> None: # --- Implements GlobalLspListener --------------------------------------------------------------------------------- - def on_session_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: + def on_session_view_initialized_async(self, view_listener: AbstractViewListener, session: Session) -> None: for listener in self._global_listeners: - listener.on_session_initialized_async(view_listener, session) + listener.on_session_view_initialized_async(view_listener, session) - def on_session_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: + def on_session_view_shutdown_async(self, view_listener: AbstractViewListener, session: Session) -> None: for listener in self._global_listeners: - listener.on_session_shutdown_async(view_listener, session) + listener.on_session_view_shutdown_async(view_listener, session) class RequestTimeTracker: