Skip to content

Commit d24cad3

Browse files
Be more lenient when dealing with rust-analyzer's flycheck commands (#36782)
Flycheck commands are global and makes sense to fall back to looking up project's rust-analyzer even if the commands are run on a non-rust buffer. If multiple rust-analyzers are found in the project, avoid ambiguous commands and bail (as before). Closes #ISSUE Release Notes: - Made it possible to run rust-analyzer's flycheck actions from anywhere in the project
1 parent 153724a commit d24cad3

File tree

5 files changed

+114
-64
lines changed

5 files changed

+114
-64
lines changed

crates/diagnostics/src/diagnostics.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ impl ProjectDiagnosticsEditor {
438438
for buffer_path in diagnostics_sources.iter().cloned() {
439439
if cx
440440
.update(|cx| {
441-
fetch_tasks.push(run_flycheck(project.clone(), buffer_path, cx));
441+
fetch_tasks.push(run_flycheck(project.clone(), Some(buffer_path), cx));
442442
})
443443
.is_err()
444444
{
@@ -462,7 +462,7 @@ impl ProjectDiagnosticsEditor {
462462
.iter()
463463
.cloned()
464464
{
465-
cancel_gasks.push(cancel_flycheck(self.project.clone(), buffer_path, cx));
465+
cancel_gasks.push(cancel_flycheck(self.project.clone(), Some(buffer_path), cx));
466466
}
467467

468468
self.cargo_diagnostics_fetch.cancel_task = Some(cx.background_spawn(async move {

crates/editor/src/rust_analyzer_ext.rs

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ fn is_rust_language(language: &Language) -> bool {
2626
}
2727

2828
pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) {
29+
if editor.read(cx).project().is_some_and(|project| {
30+
project
31+
.read(cx)
32+
.language_server_statuses(cx)
33+
.any(|(_, status)| status.name == RUST_ANALYZER_NAME)
34+
}) {
35+
register_action(editor, window, cancel_flycheck_action);
36+
register_action(editor, window, run_flycheck_action);
37+
register_action(editor, window, clear_flycheck_action);
38+
}
39+
2940
if editor
3041
.read(cx)
3142
.buffer()
@@ -38,9 +49,6 @@ pub fn apply_related_actions(editor: &Entity<Editor>, window: &mut Window, cx: &
3849
register_action(editor, window, go_to_parent_module);
3950
register_action(editor, window, expand_macro_recursively);
4051
register_action(editor, window, open_docs);
41-
register_action(editor, window, cancel_flycheck_action);
42-
register_action(editor, window, run_flycheck_action);
43-
register_action(editor, window, clear_flycheck_action);
4452
}
4553
}
4654

@@ -309,7 +317,7 @@ fn cancel_flycheck_action(
309317
let Some(project) = &editor.project else {
310318
return;
311319
};
312-
let Some(buffer_id) = editor
320+
let buffer_id = editor
313321
.selections
314322
.disjoint_anchors()
315323
.iter()
@@ -321,10 +329,7 @@ fn cancel_flycheck_action(
321329
.read(cx)
322330
.entry_id(cx)?;
323331
project.path_for_entry(entry_id, cx)
324-
})
325-
else {
326-
return;
327-
};
332+
});
328333
cancel_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
329334
}
330335

@@ -337,7 +342,7 @@ fn run_flycheck_action(
337342
let Some(project) = &editor.project else {
338343
return;
339344
};
340-
let Some(buffer_id) = editor
345+
let buffer_id = editor
341346
.selections
342347
.disjoint_anchors()
343348
.iter()
@@ -349,10 +354,7 @@ fn run_flycheck_action(
349354
.read(cx)
350355
.entry_id(cx)?;
351356
project.path_for_entry(entry_id, cx)
352-
})
353-
else {
354-
return;
355-
};
357+
});
356358
run_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
357359
}
358360

@@ -365,7 +367,7 @@ fn clear_flycheck_action(
365367
let Some(project) = &editor.project else {
366368
return;
367369
};
368-
let Some(buffer_id) = editor
370+
let buffer_id = editor
369371
.selections
370372
.disjoint_anchors()
371373
.iter()
@@ -377,9 +379,6 @@ fn clear_flycheck_action(
377379
.read(cx)
378380
.entry_id(cx)?;
379381
project.path_for_entry(entry_id, cx)
380-
})
381-
else {
382-
return;
383-
};
382+
});
384383
clear_flycheck(project.clone(), buffer_id, cx).detach_and_log_err(cx);
385384
}

crates/project/src/lsp_store.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9029,13 +9029,22 @@ impl LspStore {
90299029
lsp_store.update(&mut cx, |lsp_store, cx| {
90309030
if let Some(server) = lsp_store.language_server_for_id(server_id) {
90319031
let text_document = if envelope.payload.current_file_only {
9032-
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
9033-
lsp_store
9034-
.buffer_store()
9035-
.read(cx)
9036-
.get(buffer_id)
9037-
.and_then(|buffer| Some(buffer.read(cx).file()?.as_local()?.abs_path(cx)))
9038-
.map(|path| make_text_document_identifier(&path))
9032+
let buffer_id = envelope
9033+
.payload
9034+
.buffer_id
9035+
.map(|id| BufferId::new(id))
9036+
.transpose()?;
9037+
buffer_id
9038+
.and_then(|buffer_id| {
9039+
lsp_store
9040+
.buffer_store()
9041+
.read(cx)
9042+
.get(buffer_id)
9043+
.and_then(|buffer| {
9044+
Some(buffer.read(cx).file()?.as_local()?.abs_path(cx))
9045+
})
9046+
.map(|path| make_text_document_identifier(&path))
9047+
})
90399048
.transpose()?
90409049
} else {
90419050
None

crates/project/src/lsp_store/rust_analyzer_ext.rs

Lines changed: 76 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use ::serde::{Deserialize, Serialize};
22
use anyhow::Context as _;
3-
use gpui::{App, Entity, Task, WeakEntity};
4-
use language::ServerHealth;
5-
use lsp::{LanguageServer, LanguageServerName};
3+
use gpui::{App, AsyncApp, Entity, Task, WeakEntity};
4+
use language::{Buffer, ServerHealth};
5+
use lsp::{LanguageServer, LanguageServerId, LanguageServerName};
66
use rpc::proto;
77

88
use crate::{LspStore, LspStoreEvent, Project, ProjectPath, lsp_store};
@@ -83,31 +83,32 @@ pub fn register_notifications(lsp_store: WeakEntity<LspStore>, language_server:
8383

8484
pub fn cancel_flycheck(
8585
project: Entity<Project>,
86-
buffer_path: ProjectPath,
86+
buffer_path: Option<ProjectPath>,
8787
cx: &mut App,
8888
) -> Task<anyhow::Result<()>> {
8989
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
9090
let lsp_store = project.read(cx).lsp_store();
91-
let buffer = project.update(cx, |project, cx| {
92-
project.buffer_store().update(cx, |buffer_store, cx| {
93-
buffer_store.open_buffer(buffer_path, cx)
91+
let buffer = buffer_path.map(|buffer_path| {
92+
project.update(cx, |project, cx| {
93+
project.buffer_store().update(cx, |buffer_store, cx| {
94+
buffer_store.open_buffer(buffer_path, cx)
95+
})
9496
})
9597
});
9698

9799
cx.spawn(async move |cx| {
98-
let buffer = buffer.await?;
99-
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
100-
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
101-
})?
100+
let buffer = match buffer {
101+
Some(buffer) => Some(buffer.await?),
102+
None => None,
103+
};
104+
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
102105
else {
103106
return Ok(());
104107
};
105-
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
106108

107109
if let Some((client, project_id)) = upstream_client {
108110
let request = proto::LspExtCancelFlycheck {
109111
project_id,
110-
buffer_id,
111112
language_server_id: rust_analyzer_server.to_proto(),
112113
};
113114
client
@@ -130,28 +131,33 @@ pub fn cancel_flycheck(
130131

131132
pub fn run_flycheck(
132133
project: Entity<Project>,
133-
buffer_path: ProjectPath,
134+
buffer_path: Option<ProjectPath>,
134135
cx: &mut App,
135136
) -> Task<anyhow::Result<()>> {
136137
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
137138
let lsp_store = project.read(cx).lsp_store();
138-
let buffer = project.update(cx, |project, cx| {
139-
project.buffer_store().update(cx, |buffer_store, cx| {
140-
buffer_store.open_buffer(buffer_path, cx)
139+
let buffer = buffer_path.map(|buffer_path| {
140+
project.update(cx, |project, cx| {
141+
project.buffer_store().update(cx, |buffer_store, cx| {
142+
buffer_store.open_buffer(buffer_path, cx)
143+
})
141144
})
142145
});
143146

144147
cx.spawn(async move |cx| {
145-
let buffer = buffer.await?;
146-
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
147-
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
148-
})?
148+
let buffer = match buffer {
149+
Some(buffer) => Some(buffer.await?),
150+
None => None,
151+
};
152+
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
149153
else {
150154
return Ok(());
151155
};
152-
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
153156

154157
if let Some((client, project_id)) = upstream_client {
158+
let buffer_id = buffer
159+
.map(|buffer| buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto()))
160+
.transpose()?;
155161
let request = proto::LspExtRunFlycheck {
156162
project_id,
157163
buffer_id,
@@ -182,31 +188,32 @@ pub fn run_flycheck(
182188

183189
pub fn clear_flycheck(
184190
project: Entity<Project>,
185-
buffer_path: ProjectPath,
191+
buffer_path: Option<ProjectPath>,
186192
cx: &mut App,
187193
) -> Task<anyhow::Result<()>> {
188194
let upstream_client = project.read(cx).lsp_store().read(cx).upstream_client();
189195
let lsp_store = project.read(cx).lsp_store();
190-
let buffer = project.update(cx, |project, cx| {
191-
project.buffer_store().update(cx, |buffer_store, cx| {
192-
buffer_store.open_buffer(buffer_path, cx)
196+
let buffer = buffer_path.map(|buffer_path| {
197+
project.update(cx, |project, cx| {
198+
project.buffer_store().update(cx, |buffer_store, cx| {
199+
buffer_store.open_buffer(buffer_path, cx)
200+
})
193201
})
194202
});
195203

196204
cx.spawn(async move |cx| {
197-
let buffer = buffer.await?;
198-
let Some(rust_analyzer_server) = project.read_with(cx, |project, cx| {
199-
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
200-
})?
205+
let buffer = match buffer {
206+
Some(buffer) => Some(buffer.await?),
207+
None => None,
208+
};
209+
let Some(rust_analyzer_server) = find_rust_analyzer_server(&project, buffer.as_ref(), cx)
201210
else {
202211
return Ok(());
203212
};
204-
let buffer_id = buffer.read_with(cx, |buffer, _| buffer.remote_id().to_proto())?;
205213

206214
if let Some((client, project_id)) = upstream_client {
207215
let request = proto::LspExtClearFlycheck {
208216
project_id,
209-
buffer_id,
210217
language_server_id: rust_analyzer_server.to_proto(),
211218
};
212219
client
@@ -226,3 +233,40 @@ pub fn clear_flycheck(
226233
anyhow::Ok(())
227234
})
228235
}
236+
237+
fn find_rust_analyzer_server(
238+
project: &Entity<Project>,
239+
buffer: Option<&Entity<Buffer>>,
240+
cx: &mut AsyncApp,
241+
) -> Option<LanguageServerId> {
242+
project
243+
.read_with(cx, |project, cx| {
244+
buffer
245+
.and_then(|buffer| {
246+
project.language_server_id_for_name(buffer.read(cx), &RUST_ANALYZER_NAME, cx)
247+
})
248+
// If no rust-analyzer found for the current buffer (e.g. `settings.json`), fall back to the project lookup
249+
// and use project's rust-analyzer if it's the only one.
250+
.or_else(|| {
251+
let rust_analyzer_servers = project
252+
.lsp_store()
253+
.read(cx)
254+
.language_server_statuses
255+
.iter()
256+
.filter_map(|(server_id, server_status)| {
257+
if server_status.name == RUST_ANALYZER_NAME {
258+
Some(*server_id)
259+
} else {
260+
None
261+
}
262+
})
263+
.collect::<Vec<_>>();
264+
if rust_analyzer_servers.len() == 1 {
265+
rust_analyzer_servers.first().copied()
266+
} else {
267+
None
268+
}
269+
})
270+
})
271+
.ok()?
272+
}

crates/proto/proto/lsp.proto

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -834,21 +834,19 @@ message LspRunnable {
834834

835835
message LspExtCancelFlycheck {
836836
uint64 project_id = 1;
837-
uint64 buffer_id = 2;
838-
uint64 language_server_id = 3;
837+
uint64 language_server_id = 2;
839838
}
840839

841840
message LspExtRunFlycheck {
842841
uint64 project_id = 1;
843-
uint64 buffer_id = 2;
842+
optional uint64 buffer_id = 2;
844843
uint64 language_server_id = 3;
845844
bool current_file_only = 4;
846845
}
847846

848847
message LspExtClearFlycheck {
849848
uint64 project_id = 1;
850-
uint64 buffer_id = 2;
851-
uint64 language_server_id = 3;
849+
uint64 language_server_id = 2;
852850
}
853851

854852
message LspDiagnosticRelatedInformation {

0 commit comments

Comments
 (0)