Skip to content

Commit

Permalink
Merge branch 'main' into fix/remove-generators
Browse files Browse the repository at this point in the history
  • Loading branch information
rchl authored Oct 2, 2024
2 parents 32b6fdf + 4eb4390 commit 60210de
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
python-version: '3.8'
- run: sudo apt update
- run: sudo apt install --no-install-recommends -y x11-xserver-utils
- run: pip3 install mypy==1.7.1 flake8==5.0.4 pyright==1.1.381 --user
- run: pip3 install mypy==1.7.1 flake8==5.0.4 pyright==1.1.381 orjson==3.10.7 --user
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
- run: mypy stubs
- run: flake8 plugin tests
Expand Down
1 change: 1 addition & 0 deletions dependencies.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
">=4096": [
"bracex",
"mdpopups",
"orjson",
"typing_extensions",
"wcmatch"
]
Expand Down
2 changes: 2 additions & 0 deletions plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from .core.sessions import SessionBufferProtocol
from .core.sessions import unregister_plugin
from .core.types import ClientConfig
from .core.types import DebouncerNonThreadSafe
from .core.types import matches_pattern
from .core.url import filename_to_uri
from .core.url import parse_uri
Expand All @@ -33,6 +34,7 @@
'apply_text_edits',
'ClientConfig',
'css',
'DebouncerNonThreadSafe',
'DottedDict',
'filename_to_uri',
'FileWatcher',
Expand Down
21 changes: 16 additions & 5 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
from .url import parse_uri
from .url import unparse_uri
from .version import __version__
from .views import DiagnosticSeverityData
from .views import extract_variables
from .views import get_storage_path
from .views import get_uri_and_range_from_location
Expand Down Expand Up @@ -577,9 +576,7 @@ def has_capability_async(self, capability_path: str) -> bool:
def shutdown_async(self) -> None:
...

def present_diagnostics_async(
self, is_view_visible: bool, data_per_severity: dict[tuple[int, bool], DiagnosticSeverityData]
) -> None:
def present_diagnostics_async(self, is_view_visible: bool) -> None:
...

def on_request_started_async(self, request_id: int, request: Request) -> None:
Expand All @@ -606,6 +603,9 @@ def set_code_lenses_pending_refresh(self, needs_refresh: bool = True) -> None:
def reset_show_definitions(self) -> None:
...

def on_userprefs_changed_async(self) -> None:
...


class SessionBufferProtocol(Protocol):

Expand Down Expand Up @@ -653,6 +653,9 @@ def get_capability(self, capability_path: str) -> Any | None:
def has_capability(self, capability_path: str) -> bool:
...

def on_userprefs_changed_async(self) -> None:
...

def on_diagnostics_async(
self, raw_diagnostics: list[Diagnostic], version: int | None, visible_session_views: set[SessionViewProtocol]
) -> None:
Expand Down Expand Up @@ -1344,8 +1347,11 @@ def set_config_status_async(self, message: str) -> None:
:param message: The message
"""
self.config_status_message = message.strip()
self._redraw_config_status_async()

def _redraw_config_status_async(self) -> None:
for sv in self.session_views_async():
self.config.set_view_status(sv.view, message)
self.config.set_view_status(sv.view, self.config_status_message)

def set_window_status_async(self, key: str, message: str) -> None:
self._status_messages[key] = message
Expand Down Expand Up @@ -1480,6 +1486,11 @@ def on_file_event_async(self, events: list[FileWatcherEvent]) -> None:

# --- misc methods -------------------------------------------------------------------------------------------------

def on_userprefs_changed_async(self) -> None:
self._redraw_config_status_async()
for sb in self.session_buffers_async():
sb.on_userprefs_changed_async()

def markdown_language_id_to_st_syntax_map(self) -> MarkdownLangMap | None:
return self._plugin.markdown_language_id_to_st_syntax_map() if self._plugin is not None else None

Expand Down
53 changes: 39 additions & 14 deletions plugin/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,49 @@
from .types import read_dict_setting
from .types import Settings
from .types import SettingsRegistration
from typing import Any, Callable
from abc import ABCMeta
from abc import abstractmethod
from typing import Any
import json
import os
import sublime


class LspSettingsChangeListener(metaclass=ABCMeta):

@abstractmethod
def on_client_config_updated(self, config_name: str | None = None) -> None:
raise NotImplementedError()

@abstractmethod
def on_userprefs_updated(self) -> None:
raise NotImplementedError()


class ClientConfigs:

def __init__(self) -> None:
self.all: dict[str, ClientConfig] = {}
self.external: dict[str, ClientConfig] = {}
self._listener: Callable[[str | None], None] | None = None
self._listener: LspSettingsChangeListener | None = None
self._clients_hash: int | None = None

def _notify_clients_listener(self, config_name: str | None = None) -> None:
if self._listener:
self._listener.on_client_config_updated(config_name)

def _notify_listener(self, config_name: str | None = None) -> None:
if callable(self._listener):
self._listener(config_name)
def _notify_userprefs_listener(self) -> None:
if self._listener:
self._listener.on_userprefs_updated()

def add_for_testing(self, config: ClientConfig) -> None:
assert config.name not in self.all
self.all[config.name] = config
self._notify_listener()
self._notify_clients_listener()

def remove_for_testing(self, config: ClientConfig) -> None:
self.all.pop(config.name)
self._notify_listener()
self._notify_clients_listener()

def add_external_config(self, name: str, s: sublime.Settings, file: str, notify_listener: bool) -> bool:
if name in self.external:
Expand All @@ -49,13 +68,13 @@ def add_external_config(self, name: str, s: sublime.Settings, file: str, notify_
# That causes many calls to WindowConfigManager.match_view, which is relatively speaking an expensive
# operation. To ensure that this dance is done only once, we delay notifying the WindowConfigManager until
# all plugins have done their `register_plugin` call.
debounced(lambda: self._notify_listener(name), 200, lambda: len(self.external) == size)
debounced(lambda: self._notify_clients_listener(name), 200, lambda: len(self.external) == size)
return True

def remove_external_config(self, name: str) -> None:
self.external.pop(name, None)
if self.all.pop(name, None):
self._notify_listener()
self._notify_clients_listener()

def update_external_config(self, name: str, s: sublime.Settings, file: str) -> None:
try:
Expand All @@ -66,20 +85,26 @@ def update_external_config(self, name: str, s: sublime.Settings, file: str) -> N
return
self.external[name] = config
self.all[name] = config
self._notify_listener(name)
self._notify_clients_listener(name)

def update_configs(self) -> None:
global _settings_obj
if _settings_obj is None:
return
clients_dict = read_dict_setting(_settings_obj, "clients", {})
_clients_hash = hash(json.dumps(clients_dict, sort_keys=True))
if _clients_hash == self._clients_hash:
self._notify_userprefs_listener()
return
self._clients_hash = _clients_hash
clients = DottedDict(read_dict_setting(_settings_obj, "default_clients", {}))
clients.update(read_dict_setting(_settings_obj, "clients", {}))
clients.update(clients_dict)
self.all.clear()
self.all.update({name: ClientConfig.from_dict(name, d) for name, d in clients.get().items()})
self.all.update(self.external)
debug("enabled configs:", ", ".join(sorted(c.name for c in self.all.values() if c.enabled)))
debug("disabled configs:", ", ".join(sorted(c.name for c in self.all.values() if not c.enabled)))
self._notify_listener()
self._notify_clients_listener()

def _set_enabled(self, config_name: str, is_enabled: bool) -> None:
from .sessions import get_plugin
Expand All @@ -104,8 +129,8 @@ def enable(self, config_name: str) -> None:
def disable(self, config_name: str) -> None:
self._set_enabled(config_name, False)

def set_listener(self, recipient: Callable[[str | None], None]) -> None:
self._listener = recipient
def set_listener(self, listener: LspSettingsChangeListener) -> None:
self._listener = listener


_settings_obj: sublime.Settings | None = None
Expand Down
8 changes: 8 additions & 0 deletions plugin/core/transports.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
import time
import weakref

try:
import orjson
except ImportError:
orjson = None

T = TypeVar('T')
T_contra = TypeVar('T_contra', contravariant=True)
Expand Down Expand Up @@ -80,6 +84,8 @@ def read_data(self, reader: IO[bytes]) -> dict[str, Any] | None:

@staticmethod
def _encode(data: dict[str, Any]) -> bytes:
if orjson:
return orjson.dumps(data)
return json.dumps(
data,
ensure_ascii=False,
Expand All @@ -90,6 +96,8 @@ def _encode(data: dict[str, Any]) -> bytes:

@staticmethod
def _decode(message: bytes) -> dict[str, Any]:
if orjson:
return orjson.loads(message)
return json.loads(message.decode('utf-8'))


Expand Down
2 changes: 2 additions & 0 deletions plugin/core/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,8 @@ def set_view_status(self, view: sublime.View, message: str) -> None:
if sublime.load_settings("LSP.sublime-settings").get("show_view_status"):
status = f"{self.name} ({message})" if message else self.name
view.set_status(self.status_key, status)
else:
self.erase_view_status(view)

def erase_view_status(self, view: sublime.View) -> None:
view.erase_status(self.status_key)
Expand Down
24 changes: 18 additions & 6 deletions plugin/core/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from .sessions import Manager
from .sessions import Session
from .settings import client_configs
from .settings import LspSettingsChangeListener
from .settings import userprefs
from .transports import create_transport
from .types import ClientConfig
Expand Down Expand Up @@ -518,15 +519,11 @@ 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(LspSettingsChangeListener):
def __init__(self) -> None:
self._enabled = False
self._windows: dict[int, WindowManager] = {}
client_configs.set_listener(self._on_client_config_updated)

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)
client_configs.set_listener(self)

def enable(self) -> None:
self._enabled = True
Expand Down Expand Up @@ -566,6 +563,21 @@ def discard(self, window: sublime.Window) -> None:
if wm:
sublime.set_timeout_async(wm.destroy)

def _on_userprefs_updated_async(self) -> None:
for wm in self._windows.values():
wm.on_diagnostics_updated()
for session in wm.get_sessions():
session.on_userprefs_changed_async()

# --- Implements LspSettingsChangeListener -------------------------------------------------------------------------

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)

def on_userprefs_updated(self) -> None:
sublime.set_timeout_async(self._on_userprefs_updated_async)


class RequestTimeTracker:
def __init__(self) -> None:
Expand Down
17 changes: 8 additions & 9 deletions plugin/inlay_hint.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,23 @@
class LspToggleInlayHintsCommand(LspWindowCommand):
capability = 'inlayHintProvider'

def __init__(self, window: sublime.Window) -> None:
super().__init__(window)
window.settings().set('lsp_show_inlay_hints', userprefs().show_inlay_hints)

def run(self, enable: bool | None = None) -> None:
window_settings = self.window.settings()
if not isinstance(enable, bool):
enable = not self.are_enabled(self.window)
self.window.settings().set('lsp_show_inlay_hints', enable)
enable = not bool(window_settings.get('lsp_show_inlay_hints'))
window_settings.set('lsp_show_inlay_hints', enable)
status = 'on' if enable else 'off'
sublime.status_message(f'Inlay Hints are {status}')
for session in self.sessions():
for sv in session.session_views_async():
sv.session_buffer.do_inlay_hints_async(sv.view)

def is_checked(self) -> bool:
return self.are_enabled(self.window)

@classmethod
def are_enabled(cls, window: sublime.Window | None) -> bool:
if not window:
return userprefs().show_inlay_hints
return bool(window.settings().get('lsp_show_inlay_hints', userprefs().show_inlay_hints))
return bool(self.window.settings().get('lsp_show_inlay_hints'))


class LspInlayHintClickCommand(LspTextCommand):
Expand Down
Loading

0 comments on commit 60210de

Please sign in to comment.