Skip to content

Commit

Permalink
fix types for protocols to add type safety (#1903)
Browse files Browse the repository at this point in the history
  • Loading branch information
rchl authored Dec 13, 2021
1 parent 02d0f32 commit f26536b
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 120 deletions.
2 changes: 1 addition & 1 deletion plugin/core/registry.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .configurations import ConfigManager
from .sessions import AbstractViewListener
from .sessions import Session
from .settings import client_configs
from .typing import Optional, Any, Generator, Iterable
from .windows import AbstractViewListener
from .windows import WindowRegistry
import sublime
import sublime_plugin
Expand Down
118 changes: 111 additions & 7 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .protocol import Command
from .protocol import CompletionItemTag
from .protocol import Diagnostic
from .protocol import DiagnosticSeverity
from .protocol import DiagnosticTag
from .protocol import DidChangeWatchedFilesRegistrationOptions
from .protocol import DocumentUri
Expand All @@ -45,7 +46,7 @@
from .types import method_to_capability
from .types import SettingsRegistration
from .types import sublime_pattern_to_glob
from .typing import Callable, cast, Dict, Any, Optional, List, Tuple, Generator, Type, Protocol, Mapping, Union
from .typing import Callable, cast, Dict, Any, Optional, List, Tuple, Generator, Iterable, Type, Protocol, Sequence, Mapping, Union # noqa: E501
from .url import filename_to_uri
from .url import parse_uri
from .version import __version__
Expand Down Expand Up @@ -340,10 +341,21 @@ def get_initialize_params(variables: Dict[str, str], workspace_folders: List[Wor

class SessionViewProtocol(Protocol):

session = None # type: Session
view = None # type: sublime.View
listener = None # type: Any
session_buffer = None # type: Any
@property
def session(self) -> 'Session':
...

@property
def view(self) -> sublime.View:
...

@property
def listener(self) -> 'weakref.ref[AbstractViewListener]':
...

@property
def session_buffer(self) -> 'SessionBufferProtocol':
...

def get_uri(self) -> Optional[str]:
...
Expand Down Expand Up @@ -384,8 +396,13 @@ def get_resolved_code_lenses_for_region(self, region: sublime.Region) -> Generat

class SessionBufferProtocol(Protocol):

session = None # type: Session
session_views = None # type: WeakSet[SessionViewProtocol]
@property
def session(self) -> 'Session':
...

@property
def session_views(self) -> 'WeakSet[SessionViewProtocol]':
...

def get_uri(self) -> Optional[str]:
...
Expand Down Expand Up @@ -417,6 +434,93 @@ def on_diagnostics_async(self, raw_diagnostics: List[Diagnostic], version: Optio
...


class AbstractViewListener(metaclass=ABCMeta):

TOTAL_ERRORS_AND_WARNINGS_STATUS_KEY = "lsp_total_errors_and_warnings"

view = None # type: sublime.View

@abstractmethod
def session_async(self, capability_path: str, point: Optional[int] = None) -> Optional['Session']:
raise NotImplementedError()

@abstractmethod
def sessions_async(self, capability_path: Optional[str] = None) -> Generator['Session', None, None]:
raise NotImplementedError()

@abstractmethod
def session_views_async(self) -> Iterable['SessionViewProtocol']:
raise NotImplementedError()

@abstractmethod
def on_session_initialized_async(self, session: 'Session') -> None:
raise NotImplementedError()

@abstractmethod
def on_session_shutdown_async(self, session: 'Session') -> None:
raise NotImplementedError()

@abstractmethod
def diagnostics_async(
self
) -> Iterable[Tuple['SessionBufferProtocol', Sequence[Tuple[Diagnostic, sublime.Region]]]]:
raise NotImplementedError()

@abstractmethod
def diagnostics_intersecting_region_async(
self,
region: sublime.Region
) -> Tuple[Sequence[Tuple['SessionBufferProtocol', Sequence[Diagnostic]]], sublime.Region]:
raise NotImplementedError()

@abstractmethod
def diagnostics_touching_point_async(
self,
pt: int,
max_diagnostic_severity_level: int = DiagnosticSeverity.Hint
) -> Tuple[Sequence[Tuple['SessionBufferProtocol', Sequence[Diagnostic]]], sublime.Region]:
raise NotImplementedError()

def diagnostics_intersecting_async(
self,
region_or_point: Union[sublime.Region, int]
) -> Tuple[Sequence[Tuple['SessionBufferProtocol', Sequence[Diagnostic]]], sublime.Region]:
if isinstance(region_or_point, int):
return self.diagnostics_touching_point_async(region_or_point)
elif region_or_point.empty():
return self.diagnostics_touching_point_async(region_or_point.a)
else:
return self.diagnostics_intersecting_region_async(region_or_point)

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

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

@abstractmethod
def get_language_id(self) -> str:
raise NotImplementedError()

@abstractmethod
def get_uri(self) -> str:
raise NotImplementedError()

@abstractmethod
def do_signature_help_async(self, manual: bool) -> None:
raise NotImplementedError()

@abstractmethod
def navigate_signature_help(self, forward: bool) -> None:
raise NotImplementedError()

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


class AbstractPlugin(metaclass=ABCMeta):
"""
Inherit from this class to handle non-standard requests and notifications.
Expand Down
94 changes: 2 additions & 92 deletions plugin/core/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,25 @@
from .message_request_handler import MessageRequestHandler
from .panels import log_server_message
from .promise import Promise
from .protocol import Diagnostic
from .protocol import DiagnosticSeverity
from .protocol import DocumentUri
from .protocol import Error
from .protocol import Location
from .sessions import AbstractViewListener
from .sessions import get_plugin
from .sessions import Logger
from .sessions import Manager
from .sessions import Session
from .sessions import SessionBufferProtocol
from .sessions import SessionViewProtocol
from .settings import userprefs
from .transports import create_transport
from .types import ClientConfig
from .types import matches_pattern
from .typing import Optional, Any, Dict, Deque, List, Generator, Tuple, Iterable, Sequence, Union
from .typing import Optional, Any, Dict, Deque, List, Generator, Tuple
from .url import parse_uri
from .views import extract_variables
from .views import format_diagnostic_for_panel
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 OrderedDict
from collections import deque
from subprocess import CalledProcessError
Expand All @@ -48,91 +43,6 @@
_NO_DIAGNOSTICS_PLACEHOLDER = " No diagnostics. Well done!"


class AbstractViewListener(metaclass=ABCMeta):

TOTAL_ERRORS_AND_WARNINGS_STATUS_KEY = "lsp_total_errors_and_warnings"

view = None # type: sublime.View

@abstractmethod
def session_async(self, capability_path: str, point: Optional[int] = None) -> Optional[Session]:
raise NotImplementedError()

@abstractmethod
def sessions_async(self, capability_path: Optional[str] = None) -> Generator[Session, None, None]:
raise NotImplementedError()

@abstractmethod
def session_views_async(self) -> Iterable[SessionViewProtocol]:
raise NotImplementedError()

@abstractmethod
def on_session_initialized_async(self, session: Session) -> None:
raise NotImplementedError()

@abstractmethod
def on_session_shutdown_async(self, session: Session) -> None:
raise NotImplementedError()

@abstractmethod
def diagnostics_async(self) -> Iterable[Tuple[SessionBufferProtocol, Sequence[Tuple[Diagnostic, sublime.Region]]]]:
raise NotImplementedError()

@abstractmethod
def diagnostics_intersecting_region_async(
self,
region: sublime.Region
) -> Tuple[Sequence[Tuple[SessionBufferProtocol, Sequence[Diagnostic]]], sublime.Region]:
raise NotImplementedError()

@abstractmethod
def diagnostics_touching_point_async(
self,
pt: int,
max_diagnostic_severity_level: int = DiagnosticSeverity.Hint
) -> Tuple[Sequence[Tuple[SessionBufferProtocol, Sequence[Diagnostic]]], sublime.Region]:
raise NotImplementedError()

def diagnostics_intersecting_async(
self,
region_or_point: Union[sublime.Region, int]
) -> Tuple[Sequence[Tuple[SessionBufferProtocol, Sequence[Diagnostic]]], sublime.Region]:
if isinstance(region_or_point, int):
return self.diagnostics_touching_point_async(region_or_point)
elif region_or_point.empty():
return self.diagnostics_touching_point_async(region_or_point.a)
else:
return self.diagnostics_intersecting_region_async(region_or_point)

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

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

@abstractmethod
def get_language_id(self) -> str:
raise NotImplementedError()

@abstractmethod
def get_uri(self) -> str:
raise NotImplementedError()

@abstractmethod
def do_signature_help_async(self, manual: bool) -> None:
raise NotImplementedError()

@abstractmethod
def navigate_signature_help(self, forward: bool) -> None:
raise NotImplementedError()

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


def extract_message(params: Any) -> str:
return params.get("message", "???") if isinstance(params, dict) else "???"

Expand Down
5 changes: 2 additions & 3 deletions plugin/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .core.protocol import SignatureHelp
from .core.registry import best_session
from .core.registry import windows
from .core.sessions import AbstractViewListener
from .core.sessions import Session
from .core.settings import userprefs
from .core.signature_help import SigHelp
Expand All @@ -32,7 +33,6 @@
from .core.views import show_lsp_popup
from .core.views import text_document_position_params
from .core.views import update_lsp_popup
from .core.windows import AbstractViewListener
from .core.windows import WindowManager
from .session_buffer import SessionBuffer
from .session_view import SessionView
Expand Down Expand Up @@ -136,8 +136,6 @@ class DocumentSyncListener(sublime_plugin.ViewEventListener, AbstractViewListene
highlights_debounce_time = FEATURES_TIMEOUT
code_lenses_debounce_time = FEATURES_TIMEOUT

_uri = None # type: str

@classmethod
def applies_to_primary_view_only(cls) -> bool:
return False
Expand All @@ -152,6 +150,7 @@ def on_change() -> None:
if this is not None:
this._on_settings_object_changed()

self._uri = '' # assumed to never be falsey
self._current_syntax = self.view.settings().get("syntax")
existing_uri = view.settings().get("lsp_uri")
if isinstance(existing_uri, str):
Expand Down
2 changes: 1 addition & 1 deletion plugin/hover.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .core.protocol import TextDocumentPositionParams
from .core.registry import LspTextCommand
from .core.registry import windows
from .core.sessions import AbstractViewListener
from .core.sessions import Session
from .core.sessions import SessionBufferProtocol
from .core.settings import userprefs
Expand All @@ -28,7 +29,6 @@
from .core.views import text_document_range_params
from .core.views import unpack_href_location
from .core.views import update_lsp_popup
from .core.windows import AbstractViewListener
from urllib.parse import unquote, urlparse
import functools
import re
Expand Down
17 changes: 13 additions & 4 deletions plugin/session_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from .core.protocol import Request
from .core.protocol import TextDocumentSyncKindFull
from .core.protocol import TextDocumentSyncKindNone
from .core.sessions import Session
from .core.sessions import SessionViewProtocol
from .core.settings import userprefs
from .core.types import Capabilities
Expand Down Expand Up @@ -68,9 +69,9 @@ def __init__(self, session_view: SessionViewProtocol, buffer_id: int, uri: Docum
self.opened = False
# Every SessionBuffer has its own personal capabilities due to "dynamic registration".
self.capabilities = Capabilities()
self.session = session_view.session
self.session_views = WeakSet() # type: WeakSet[SessionViewProtocol]
self.session_views.add(session_view)
self._session = session_view.session
self._session_views = WeakSet() # type: WeakSet[SessionViewProtocol]
self._session_views.add(session_view)
self.last_known_uri = uri
self.id = buffer_id
self.pending_changes = None # type: Optional[PendingChanges]
Expand All @@ -86,7 +87,7 @@ def __init__(self, session_view: SessionViewProtocol, buffer_id: int, uri: Docum
self.diagnostics_debouncer = Debouncer()
self.color_phantoms = sublime.PhantomSet(view, "lsp_color")
self._check_did_open(view)
self.session.register_session_buffer_async(self)
self._session.register_session_buffer_async(self)

def __del__(self) -> None:
mgr = self.session.manager()
Expand All @@ -100,6 +101,14 @@ def __del__(self) -> None:
self._check_did_close()
self.session.unregister_session_buffer_async(self)

@property
def session(self) -> Session:
return self._session

@property
def session_views(self) -> 'WeakSet[SessionViewProtocol]':
return self._session_views

def _check_did_open(self, view: sublime.View) -> None:
if not self.opened and self.should_notify_did_open():
language_id = self.get_language_id()
Expand Down
Loading

0 comments on commit f26536b

Please sign in to comment.