Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix types for protocols to add type safety #1903

Merged
merged 8 commits into from
Dec 13, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,11 @@
from .views import get_uri_and_range_from_location
from .views import SYMBOL_KINDS
from .views import to_encoded_filename
from .windows import AbstractViewListener
from .workspace import is_subpath_of
from abc import ABCMeta
from abc import abstractmethod
from weakref import ref
from weakref import WeakSet
import functools
import mdpopups
Expand Down Expand Up @@ -340,10 +342,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) -> ref[AbstractViewListener]:
...

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

def get_uri(self) -> Optional[str]:
...
Expand Down Expand Up @@ -384,8 +397,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
3 changes: 1 addition & 2 deletions plugin/documents.py
Original file line number Diff line number Diff line change
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
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
54 changes: 35 additions & 19 deletions plugin/session_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,47 +37,63 @@ class SessionView:
_session_buffers = WeakValueDictionary() # type: WeakValueDictionary[Tuple[int, int], SessionBuffer]

def __init__(self, listener: AbstractViewListener, session: Session, uri: DocumentUri) -> None:
self.view = listener.view
Copy link
Member Author

@rchl rchl Nov 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid changing too much code, decided to use the "underscore" property in __init__ and access through the getter elsewhere. This is maybe not super consistent but makes the diff smaller.

Let me know what you prefer. Maybe just assign to _view and use the getter from there on.

self.session = session
self._view = listener.view
self._session = session
self.active_requests = {} # type: Dict[int, Request]
self.listener = ref(listener)
self._listener = ref(listener)
self.progress = {} # type: Dict[int, ViewProgressReporter]
self._code_lenses = CodeLensView(self.view)
settings = self.view.settings()
buffer_id = self.view.buffer_id()
self._code_lenses = CodeLensView(self._view)
settings = self._view.settings()
buffer_id = self._view.buffer_id()
key = (id(session), buffer_id)
session_buffer = self._session_buffers.get(key)
if session_buffer is None:
session_buffer = SessionBuffer(self, buffer_id, uri)
self._session_buffers[key] = session_buffer
else:
session_buffer.add_session_view(self)
self.session_buffer = session_buffer
self._session_buffer = session_buffer
session.register_session_view_async(self)
session.config.set_view_status(self.view, "")
if self.session.has_capability(self.HOVER_PROVIDER_KEY):
session.config.set_view_status(self._view, "")
if self._session.has_capability(self.HOVER_PROVIDER_KEY):
self._increment_hover_count()
self._clear_auto_complete_triggers(settings)
self._setup_auto_complete_triggers(settings)

def on_before_remove(self) -> None:
settings = self.view.settings() # type: sublime.Settings
settings = self._view.settings() # type: sublime.Settings
self._clear_auto_complete_triggers(settings)
self._code_lenses.clear_view()
if self.session.has_capability(self.HOVER_PROVIDER_KEY):
if self._session.has_capability(self.HOVER_PROVIDER_KEY):
self._decrement_hover_count()
# If the session is exiting then there's no point in sending textDocument/didClose and there's also no point
# in unregistering ourselves from the session.
if not self.session.exiting:
if not self._session.exiting:
for request_id, request in self.active_requests.items():
if request.view and request.view.id() == self.view.id():
self.session.send_notification(Notification("$/cancelRequest", {"id": request_id}))
self.session.unregister_session_view_async(self)
self.session.config.erase_view_status(self.view)
if request.view and request.view.id() == self._view.id():
self._session.send_notification(Notification("$/cancelRequest", {"id": request_id}))
self._session.unregister_session_view_async(self)
self._session.config.erase_view_status(self._view)
for severity in reversed(range(1, len(DIAGNOSTIC_SEVERITY) + 1)):
self.view.erase_regions(self.diagnostics_key(severity, False))
self.view.erase_regions(self.diagnostics_key(severity, True))
self.session_buffer.remove_session_view(self)
self._view.erase_regions(self.diagnostics_key(severity, False))
self._view.erase_regions(self.diagnostics_key(severity, True))
self._session_buffer.remove_session_view(self)

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

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

@property
def listener(self) -> ref[AbstractViewListener]:
return self._listener

@property
def session_buffer(self) -> SessionBuffer:
return self._session_buffer

def _is_listener_alive(self) -> bool:
return bool(self.listener())
Expand Down
5 changes: 3 additions & 2 deletions plugin/tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
from .core.transports import TransportCallbacks
from .core.types import Capabilities
from .core.types import ClientConfig
from .core.typing import Any, Callable, Dict, List, Optional, Tuple
from .core.typing import Any, Callable, cast, Dict, List, Optional, Tuple
from .core.version import __version__
from .core.views import extract_variables
from .core.views import make_command_link
from .session_buffer import SessionBuffer
from base64 import b64decode
from base64 import b64encode
from subprocess import list2cmdline
Expand Down Expand Up @@ -457,7 +458,7 @@ def print_capabilities(capabilities: Capabilities) -> str:
p("## Global capabilities\n")
p(print_capabilities(sv.session.capabilities) + "\n")
p("## View-specific capabilities\n")
p(print_capabilities(sv.session_buffer.capabilities) + "\n")
p(print_capabilities(cast(SessionBuffer, sv.session_buffer).capabilities) + "\n")


class ServerTestRunner(TransportCallbacks):
Expand Down