Skip to content

Commit 585c6c7

Browse files
committed
Cancel ongoing pull diagnostic events
1 parent 16766b8 commit 585c6c7

File tree

2 files changed

+61
-69
lines changed

2 files changed

+61
-69
lines changed

helix-lsp/src/client.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ pub struct Client {
6262
initialize_notify: Arc<Notify>,
6363
/// workspace folders added while the server is still initializing
6464
req_timeout: u64,
65+
ongoing_work: Mutex<Vec<(lsp::TextDocumentIdentifier, lsp::ProgressToken)>>,
6566
}
6667

6768
impl Client {
@@ -234,6 +235,7 @@ impl Client {
234235
root_uri,
235236
workspace_folders: Mutex::new(workspace_folders),
236237
initialize_notify: initialize_notify.clone(),
238+
ongoing_work: Mutex::new(Default::default()),
237239
};
238240

239241
Ok((client, server_rx, initialize_notify))
@@ -1227,13 +1229,39 @@ impl Client {
12271229
Some(self.call::<lsp::request::RangeFormatting>(params))
12281230
}
12291231

1232+
pub fn mark_work_as_done(&self, token: lsp::ProgressToken) {
1233+
self.ongoing_work.lock().retain(|x| x.1 != token);
1234+
}
1235+
12301236
pub fn text_document_diagnostic(
12311237
&self,
12321238
text_document: lsp::TextDocumentIdentifier,
12331239
previous_result_id: Option<String>,
1234-
) -> Option<impl Future<Output = Result<lsp::DocumentDiagnosticReportResult>>> {
1240+
) -> Option<(
1241+
impl Future<Output = Result<lsp::DocumentDiagnosticReportResult>>,
1242+
lsp::ProgressToken,
1243+
)> {
12351244
let capabilities = self.capabilities();
12361245

1246+
let ongoing_work = {
1247+
let ongoing_work_lock = self.ongoing_work.lock();
1248+
1249+
ongoing_work_lock
1250+
.iter()
1251+
.filter(|x| x.0 == text_document)
1252+
.cloned()
1253+
.collect::<Vec<_>>()
1254+
};
1255+
1256+
if !ongoing_work.is_empty() {
1257+
for id in ongoing_work.iter().map(|x| x.1.clone()) {
1258+
self.notify::<lsp::notification::Cancel>(lsp::CancelParams { id: id.clone() });
1259+
self.mark_work_as_done(id);
1260+
}
1261+
1262+
return None;
1263+
}
1264+
12371265
// Return early if the server does not support pull diagnostic.
12381266
let identifier = match capabilities.diagnostic_provider.as_ref()? {
12391267
lsp::DiagnosticServerCapabilities::Options(cap) => cap.identifier.clone(),
@@ -1242,15 +1270,30 @@ impl Client {
12421270
}
12431271
};
12441272

1273+
let work_done_token = match self.next_request_id() {
1274+
jsonrpc::Id::Null => lsp::ProgressToken::Number(1),
1275+
jsonrpc::Id::Num(num) => lsp::ProgressToken::Number(num as i32),
1276+
jsonrpc::Id::Str(str) => lsp::ProgressToken::String(str),
1277+
};
1278+
12451279
let params = lsp::DocumentDiagnosticParams {
1246-
text_document,
1280+
text_document: text_document.clone(),
12471281
identifier,
12481282
previous_result_id,
1249-
work_done_progress_params: lsp::WorkDoneProgressParams::default(),
1283+
work_done_progress_params: lsp::WorkDoneProgressParams {
1284+
work_done_token: Some(work_done_token.clone()),
1285+
},
12501286
partial_result_params: lsp::PartialResultParams::default(),
12511287
};
12521288

1253-
Some(self.call::<lsp::request::DocumentDiagnosticRequest>(params))
1289+
self.ongoing_work
1290+
.lock()
1291+
.push((text_document, work_done_token.clone()));
1292+
1293+
Some((
1294+
self.call::<lsp::request::DocumentDiagnosticRequest>(params),
1295+
work_done_token,
1296+
))
12541297
}
12551298

12561299
pub fn text_document_document_highlight(

helix-term/src/handlers/diagnostics.rs

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::time::Duration;
2+
use tokio::time::Instant;
23

34
use helix_core::diagnostic::DiagnosticProvider;
45
use helix_core::syntax::LanguageServerFeature;
@@ -13,7 +14,6 @@ use helix_view::handlers::diagnostics::DiagnosticEvent;
1314
use helix_view::handlers::lsp::PullDiagnosticsEvent;
1415
use helix_view::handlers::Handlers;
1516
use helix_view::{DocumentId, Editor};
16-
use tokio::time::Instant;
1717

1818
use crate::events::OnModeSwitch;
1919
use crate::job;
@@ -74,87 +74,30 @@ pub(super) fn register_hooks(handlers: &Handlers) {
7474
}
7575

7676
#[derive(Debug)]
77-
pub(super) struct PullDiagnosticsHandler {
78-
no_inter_file_dependency_timeout: Option<tokio::time::Instant>,
79-
}
77+
pub(super) struct PullDiagnosticsHandler {}
8078

8179
impl PullDiagnosticsHandler {
82-
pub fn new() -> PullDiagnosticsHandler {
83-
PullDiagnosticsHandler {
84-
no_inter_file_dependency_timeout: None,
85-
}
80+
pub fn new() -> Self {
81+
PullDiagnosticsHandler {}
8682
}
8783
}
8884

89-
const TIMEOUT: Duration = Duration::from_millis(500);
90-
const TIMEOUT_NO_INTER_FILE_DEPENDENCY: Duration = Duration::from_millis(125);
91-
9285
impl helix_event::AsyncHook for PullDiagnosticsHandler {
9386
type Event = PullDiagnosticsEvent;
9487

9588
fn handle_event(
9689
&mut self,
97-
event: Self::Event,
98-
timeout: Option<tokio::time::Instant>,
90+
_event: Self::Event,
91+
_timeout: Option<tokio::time::Instant>,
9992
) -> Option<tokio::time::Instant> {
100-
if timeout.is_none() {
101-
dispatch_pull_diagnostic_for_document(event.document_id, false);
102-
self.no_inter_file_dependency_timeout = Some(Instant::now());
103-
}
104-
105-
if self
106-
.no_inter_file_dependency_timeout
107-
.is_some_and(|nifd_timeout| {
108-
nifd_timeout.duration_since(Instant::now()) > TIMEOUT_NO_INTER_FILE_DEPENDENCY
109-
})
110-
{
111-
dispatch_pull_diagnostic_for_document(event.document_id, true);
112-
self.no_inter_file_dependency_timeout = Some(Instant::now());
113-
};
114-
115-
Some(Instant::now() + TIMEOUT)
93+
Some(Instant::now() + Duration::from_millis(150))
11694
}
11795

11896
fn finish_debounce(&mut self) {
11997
dispatch_pull_diagnostic_for_open_documents();
12098
}
12199
}
122100

123-
fn dispatch_pull_diagnostic_for_document(
124-
document_id: DocumentId,
125-
exclude_language_servers_without_inter_file_dependency: bool,
126-
) {
127-
job::dispatch_blocking(move |editor, _| {
128-
let Some(doc) = editor.document(document_id) else {
129-
return;
130-
};
131-
132-
let language_servers = doc
133-
.language_servers_with_feature(LanguageServerFeature::PullDiagnostics)
134-
.filter(|ls| ls.is_initialized())
135-
.filter(|ls| {
136-
if !exclude_language_servers_without_inter_file_dependency {
137-
return true;
138-
};
139-
ls.capabilities()
140-
.diagnostic_provider
141-
.as_ref()
142-
.is_some_and(|dp| match dp {
143-
lsp::DiagnosticServerCapabilities::Options(options) => {
144-
options.inter_file_dependencies
145-
}
146-
lsp::DiagnosticServerCapabilities::RegistrationOptions(options) => {
147-
options.diagnostic_options.inter_file_dependencies
148-
}
149-
})
150-
});
151-
152-
for language_server in language_servers {
153-
pull_diagnostics_for_document(doc, language_server);
154-
}
155-
})
156-
}
157-
158101
fn dispatch_pull_diagnostic_for_open_documents() {
159102
job::dispatch_blocking(move |editor, _| {
160103
let documents = editor.documents.values();
@@ -204,9 +147,14 @@ pub fn pull_diagnostics_for_document(
204147
let document_id = doc.id();
205148

206149
tokio::spawn(async move {
207-
match future.await {
150+
match future.0.await {
208151
Ok(result) => {
209152
job::dispatch(move |editor, _| {
153+
if let Some(language_server) = editor.language_server_by_id(language_server_id)
154+
{
155+
language_server.mark_work_as_done(future.1);
156+
};
157+
210158
handle_pull_diagnostics_response(editor, result, provider, uri, document_id)
211159
})
212160
.await
@@ -230,6 +178,7 @@ pub fn pull_diagnostics_for_document(
230178
editor.document(document_id),
231179
editor.language_server_by_id(language_server_id),
232180
) {
181+
language_server.mark_work_as_done(future.1);
233182
pull_diagnostics_for_document(doc, language_server);
234183
}
235184
})

0 commit comments

Comments
 (0)