diff --git a/plugin/code_actions.py b/plugin/code_actions.py index a8c72b4e8..e800f9773 100644 --- a/plugin/code_actions.py +++ b/plugin/code_actions.py @@ -130,6 +130,8 @@ def request_factory(sb: SessionBufferProtocol) -> Request | None: if diag_sb == sb: diagnostics = diags break + # Pull for diagnostics to ensure that server computes them before receiving code action request. + sb.do_document_diagnostic_async(view) params = text_document_code_action_params(view, region, diagnostics, matching_kinds, manual=False) return Request.codeAction(params, view) diff --git a/plugin/session_buffer.py b/plugin/session_buffer.py index 959bfad39..28b7e6b96 100644 --- a/plugin/session_buffer.py +++ b/plugin/session_buffer.py @@ -81,6 +81,15 @@ def update(self, version: int, changes: Iterable[sublime.TextChange]) -> None: self.changes.extend(changes) +class PendingDocumentDiagnosticRequest: + + __slots__ = ('version', 'request_id') + + def __init__(self, version: int, request_id: int) -> None: + self.version = version + self.request_id = request_id + + class SemanticTokensData: __slots__ = ( @@ -122,7 +131,7 @@ def __init__(self, session_view: SessionViewProtocol, buffer_id: int, uri: Docum self.diagnostics_flags = 0 self._diagnostics_are_visible = False self.document_diagnostic_needs_refresh = False - self._document_diagnostic_pending_response: int | None = None + self._document_diagnostic_pending_request: PendingDocumentDiagnosticRequest | None = None self._last_synced_version = 0 self._last_text_change_time = 0.0 self._diagnostics_debouncer_async = DebouncerNonThreadSafe(async_thread=True) @@ -471,30 +480,32 @@ def update_document_link(self, new_link: DocumentLink) -> None: def do_document_diagnostic_async(self, view: sublime.View, version: int | None = None) -> None: mgr = self.session.manager() - if not mgr: + if not mgr or not self.has_capability("diagnosticProvider"): return if mgr.should_ignore_diagnostics(self._last_known_uri, self.session.config): return if version is None: version = view.change_count() - if self.has_capability("diagnosticProvider"): - if self._document_diagnostic_pending_response: - self.session.cancel_request(self._document_diagnostic_pending_response) - params: DocumentDiagnosticParams = {'textDocument': text_document_identifier(view)} - identifier = self.get_capability("diagnosticProvider.identifier") - if identifier: - params['identifier'] = identifier - result_id = self.session.diagnostics_result_ids.get(self._last_known_uri) - if result_id is not None: - params['previousResultId'] = result_id - self._document_diagnostic_pending_response = self.session.send_request_async( - Request.documentDiagnostic(params, view), - partial(self._on_document_diagnostic_async, version), - partial(self._on_document_diagnostic_error_async, version) - ) + if self._document_diagnostic_pending_request: + if self._document_diagnostic_pending_request.version == version: + return + self.session.cancel_request(self._document_diagnostic_pending_request.request_id) + params: DocumentDiagnosticParams = {'textDocument': text_document_identifier(view)} + identifier = self.get_capability("diagnosticProvider.identifier") + if identifier: + params['identifier'] = identifier + result_id = self.session.diagnostics_result_ids.get(self._last_known_uri) + if result_id is not None: + params['previousResultId'] = result_id + request_id = self.session.send_request_async( + Request.documentDiagnostic(params, view), + partial(self._on_document_diagnostic_async, version), + partial(self._on_document_diagnostic_error_async, version) + ) + self._document_diagnostic_pending_request = PendingDocumentDiagnosticRequest(version, request_id) def _on_document_diagnostic_async(self, version: int, response: DocumentDiagnosticReport) -> None: - self._document_diagnostic_pending_response = None + self._document_diagnostic_pending_request = None self._if_view_unchanged(self._apply_document_diagnostic_async, version)(response) def _apply_document_diagnostic_async( @@ -511,7 +522,7 @@ def _apply_document_diagnostic_async( None, cast(DocumentDiagnosticReport, diagnostic_report)) def _on_document_diagnostic_error_async(self, version: int, error: ResponseError) -> None: - self._document_diagnostic_pending_response = None + self._document_diagnostic_pending_request = None if error['code'] == LSPErrorCodes.ServerCancelled: data = error.get('data') if is_diagnostic_server_cancellation_data(data) and data['retriggerRequest']: