Skip to content

Commit

Permalink
Merge branch 'main' into pep-526
Browse files Browse the repository at this point in the history
  • Loading branch information
predragnikolic committed Apr 20, 2024
2 parents 1aa807a + 4b02dbd commit 8193e17
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 49 deletions.
9 changes: 6 additions & 3 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1814,9 +1814,12 @@ def _set_focused_sheet(self, sheet: Optional[sublime.Sheet]) -> None:
self.window.focus_sheet(sheet)

def decode_semantic_token(
self, token_type_encoded: int, token_modifiers_encoded: int) -> Tuple[str, List[str], Optional[str]]:
types_legend = tuple(cast(List[str], self.get_capability('semanticTokensProvider.legend.tokenTypes')))
modifiers_legend = tuple(cast(List[str], self.get_capability('semanticTokensProvider.legend.tokenModifiers')))
self,
types_legend: Tuple[str, ...],
modifiers_legend: Tuple[str, ...],
token_type_encoded: int,
token_modifiers_encoded: int
) -> Tuple[str, List[str], Optional[str]]:
return decode_semantic_token(
types_legend, modifiers_legend, self._semantic_tokens_map, token_type_encoded, token_modifiers_encoded)

Expand Down
50 changes: 29 additions & 21 deletions plugin/session_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ def _check_did_open(self, view: sublime.View) -> None:
self._do_document_link_async(view, version)
self.session.notify_plugin_on_session_buffer_change(self)

def _check_did_close(self) -> None:
def _check_did_close(self, view: sublime.View) -> None:
if self.opened and self.should_notify_did_close():
self.purge_changes_async(view, suppress_requests=True)
self.session.send_notification(did_close(uri=self._last_known_uri))
self.opened = False

Expand Down Expand Up @@ -202,9 +203,9 @@ def remove_session_view(self, sv: SessionViewProtocol) -> None:
self._clear_semantic_token_regions(sv.view)
self.session_views.remove(sv)
if len(self.session_views) == 0:
self._on_before_destroy()
self._on_before_destroy(sv.view)

def _on_before_destroy(self) -> None:
def _on_before_destroy(self, view: sublime.View) -> None:
self.remove_all_inlay_hints()
if self.has_capability("diagnosticProvider") and self.session.config.diagnostics_mode == "open_files":
self.session.m_textDocument_publishDiagnostics({'uri': self._last_known_uri, 'diagnostics': []})
Expand All @@ -216,7 +217,7 @@ def _on_before_destroy(self) -> None:
# in unregistering ourselves from the session.
if not self.session.exiting:
# Only send textDocument/didClose when we are the only view left (i.e. there are no other clones).
self._check_did_close()
self._check_did_close(view)
self.session.unregister_session_buffer_async(self)

def register_capability_async(
Expand Down Expand Up @@ -308,15 +309,15 @@ def on_revert_async(self, view: sublime.View) -> None:

on_reload_async = on_revert_async

def purge_changes_async(self, view: sublime.View) -> None:
def purge_changes_async(self, view: sublime.View, suppress_requests: bool = False) -> None:
if self._pending_changes is None:
return
sync_kind = self.text_sync_kind()
if sync_kind == TextDocumentSyncKind.None_:
return
if sync_kind == TextDocumentSyncKind.Full:
changes = None
version = view.change_count()
version = view.change_count() or self._pending_changes.version
else:
changes = self._pending_changes.changes
version = self._pending_changes.version
Expand All @@ -329,23 +330,28 @@ def purge_changes_async(self, view: sublime.View) -> None:
finally:
self._pending_changes = None
self.session.notify_plugin_on_session_buffer_change(self)
sublime.set_timeout_async(lambda: self._on_after_change_async(view, version))
sublime.set_timeout_async(lambda: self._on_after_change_async(view, version, suppress_requests))

def _on_after_change_async(self, view: sublime.View, version: int) -> None:
def _on_after_change_async(self, view: sublime.View, version: int, suppress_requests: bool = False) -> None:
if self._is_saving:
self._has_changed_during_save = True
return
self._do_color_boxes_async(view, version)
self.do_document_diagnostic_async(view, version)
if self.session.config.diagnostics_mode == "workspace" and \
not self.session.workspace_diagnostics_pending_response and \
self.session.has_capability('diagnosticProvider.workspaceDiagnostics'):
self._workspace_diagnostics_debouncer_async.debounce(
self.session.do_workspace_diagnostics_async, timeout_ms=WORKSPACE_DIAGNOSTICS_TIMEOUT)
self.do_semantic_tokens_async(view)
if userprefs().link_highlight_style in ("underline", "none"):
self._do_document_link_async(view, version)
self.do_inlay_hints_async(view)
if suppress_requests:
return
try:
self._do_color_boxes_async(view, version)
self.do_document_diagnostic_async(view, version)
if self.session.config.diagnostics_mode == "workspace" and \
not self.session.workspace_diagnostics_pending_response and \
self.session.has_capability('diagnosticProvider.workspaceDiagnostics'):
self._workspace_diagnostics_debouncer_async.debounce(
self.session.do_workspace_diagnostics_async, timeout_ms=WORKSPACE_DIAGNOSTICS_TIMEOUT)
self.do_semantic_tokens_async(view)
if userprefs().link_highlight_style in ("underline", "none"):
self._do_document_link_async(view, version)
self.do_inlay_hints_async(view)
except MissingUriError:
pass

def on_pre_save_async(self, view: sublime.View) -> None:
self._is_saving = True
Expand All @@ -357,7 +363,7 @@ def on_pre_save_async(self, view: sublime.View) -> None:
def on_post_save_async(self, view: sublime.View, new_uri: DocumentUri) -> None:
self._is_saving = False
if new_uri != self._last_known_uri:
self._check_did_close()
self._check_did_close(view)
self._last_known_uri = new_uri
self._check_did_open(view)
else:
Expand Down Expand Up @@ -630,6 +636,8 @@ def _draw_semantic_tokens_async(self) -> None:
scope_regions: Dict[int, Tuple[str, List[sublime.Region]]] = dict()
prev_line = 0
prev_col_utf16 = 0
types_legend = tuple(cast(List[str], self.get_capability('semanticTokensProvider.legend.tokenTypes')))
modifiers_legend = tuple(cast(List[str], self.get_capability('semanticTokensProvider.legend.tokenModifiers')))
for idx in range(0, len(self.semantic_tokens.data), 5):
delta_line = self.semantic_tokens.data[idx]
delta_start_utf16 = self.semantic_tokens.data[idx + 1]
Expand All @@ -644,7 +652,7 @@ def _draw_semantic_tokens_async(self) -> None:
prev_line = line
prev_col_utf16 = col_utf16
token_type, token_modifiers, scope = self.session.decode_semantic_token(
token_type_encoded, token_modifiers_encoded)
types_legend, modifiers_legend, token_type_encoded, token_modifiers_encoded)
if scope is None:
# We can still use the meta scope and draw highlighting regions for custom token types if there is a
# color scheme rule for this particular token type.
Expand Down
73 changes: 48 additions & 25 deletions tests/test_single_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,31 +84,6 @@ def test_did_close(self) -> 'Generator':
self.view.close()
yield from self.await_message("textDocument/didClose")

def test_did_change(self) -> 'Generator':
assert self.view
self.maxDiff = None
self.insert_characters("A")
yield from self.await_message("textDocument/didChange")
# multiple changes are batched into one didChange notification
self.insert_characters("B\n")
self.insert_characters("🙂\n")
self.insert_characters("D")
promise = YieldPromise()
yield from self.await_message("textDocument/didChange", promise)
self.assertEqual(promise.result(), {
'contentChanges': [
{'rangeLength': 0, 'range': {'start': {'line': 0, 'character': 1}, 'end': {'line': 0, 'character': 1}}, 'text': 'B'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 0, 'character': 2}, 'end': {'line': 0, 'character': 2}}, 'text': '\n'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 1, 'character': 0}, 'end': {'line': 1, 'character': 0}}, 'text': '🙂'}, # noqa
# Note that this is character offset (2) is correct (UTF-16).
{'rangeLength': 0, 'range': {'start': {'line': 1, 'character': 2}, 'end': {'line': 1, 'character': 2}}, 'text': '\n'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 2, 'character': 0}, 'end': {'line': 2, 'character': 0}}, 'text': 'D'}], # noqa
'textDocument': {
'version': self.view.change_count(),
'uri': filename_to_uri(TEST_FILE_PATH)
}
})

def test_sends_save_with_purge(self) -> 'Generator':
assert self.view
self.view.settings().set("lsp_format_on_save", False)
Expand Down Expand Up @@ -371,6 +346,54 @@ def test_progress(self) -> 'Generator':
self.assertEqual(result, {"general": "kenobi"})


class SingleDocumentTestCase2(TextDocumentTestCase):

def test_did_change(self) -> 'Generator':
assert self.view
self.maxDiff = None
self.insert_characters("A")
yield from self.await_message("textDocument/didChange")
# multiple changes are batched into one didChange notification
self.insert_characters("B\n")
self.insert_characters("🙂\n")
self.insert_characters("D")
promise = YieldPromise()
yield from self.await_message("textDocument/didChange", promise)
self.assertEqual(promise.result(), {
'contentChanges': [
{'rangeLength': 0, 'range': {'start': {'line': 0, 'character': 1}, 'end': {'line': 0, 'character': 1}}, 'text': 'B'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 0, 'character': 2}, 'end': {'line': 0, 'character': 2}}, 'text': '\n'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 1, 'character': 0}, 'end': {'line': 1, 'character': 0}}, 'text': '🙂'}, # noqa
# Note that this is character offset (2) is correct (UTF-16).
{'rangeLength': 0, 'range': {'start': {'line': 1, 'character': 2}, 'end': {'line': 1, 'character': 2}}, 'text': '\n'}, # noqa
{'rangeLength': 0, 'range': {'start': {'line': 2, 'character': 0}, 'end': {'line': 2, 'character': 0}}, 'text': 'D'}], # noqa
'textDocument': {
'version': self.view.change_count(),
'uri': filename_to_uri(TEST_FILE_PATH)
}
})


class SingleDocumentTestCase3(TextDocumentTestCase):

@classmethod
def get_test_name(cls) -> str:
return "testfile2"

def test_did_change_before_did_close(self) -> 'Generator':
assert self.view
self.view.window().run_command("chain", {
"commands": [
["insert", {"characters": "TEST"}],
["save", {"async": False}],
["close", {}]
]
})
yield from self.await_message('textDocument/didChange')
# yield from self.await_message('textDocument/didSave') # TODO why is this not sent?
yield from self.await_message('textDocument/didClose')


class WillSaveWaitUntilTestCase(TextDocumentTestCase):

@classmethod
Expand Down
Empty file added tests/testfile2.txt
Empty file.

0 comments on commit 8193e17

Please sign in to comment.