aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs54
-rw-r--r--crates/rust-analyzer/src/handlers.rs5
-rw-r--r--crates/rust-analyzer/src/main_loop.rs61
3 files changed, 50 insertions, 70 deletions
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 290609e7f..f3cdb842b 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -1,14 +1,15 @@
1//! Book keeping for keeping diagnostics easily in sync with the client. 1//! Book keeping for keeping diagnostics easily in sync with the client.
2pub(crate) mod to_proto; 2pub(crate) mod to_proto;
3 3
4use std::{collections::HashMap, sync::Arc}; 4use std::{collections::HashMap, mem, sync::Arc};
5 5
6use lsp_types::{Diagnostic, Range}; 6use lsp_types::{Diagnostic, Range};
7use ra_ide::FileId; 7use ra_ide::FileId;
8use rustc_hash::FxHashSet;
8 9
9use crate::lsp_ext; 10use crate::lsp_ext;
10 11
11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 12pub(crate) type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
12 13
13#[derive(Debug, Default, Clone)] 14#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsConfig { 15pub struct DiagnosticsConfig {
@@ -17,32 +18,26 @@ pub struct DiagnosticsConfig {
17} 18}
18 19
19#[derive(Debug, Default, Clone)] 20#[derive(Debug, Default, Clone)]
20pub struct DiagnosticCollection { 21pub(crate) struct DiagnosticCollection {
21 pub native: HashMap<FileId, Vec<Diagnostic>>, 22 pub(crate) native: HashMap<FileId, Vec<Diagnostic>>,
22 pub check: HashMap<FileId, Vec<Diagnostic>>, 23 pub(crate) check: HashMap<FileId, Vec<Diagnostic>>,
23 pub check_fixes: CheckFixes, 24 pub(crate) check_fixes: CheckFixes,
25 changes: FxHashSet<FileId>,
24} 26}
25 27
26#[derive(Debug, Clone)] 28#[derive(Debug, Clone)]
27pub struct Fix { 29pub(crate) struct Fix {
28 pub range: Range, 30 pub(crate) range: Range,
29 pub action: lsp_ext::CodeAction, 31 pub(crate) action: lsp_ext::CodeAction,
30}
31
32#[derive(Debug)]
33pub enum DiagnosticTask {
34 ClearCheck,
35 AddCheck(FileId, Diagnostic, Vec<lsp_ext::CodeAction>),
36 SetNative(FileId, Vec<Diagnostic>),
37} 32}
38 33
39impl DiagnosticCollection { 34impl DiagnosticCollection {
40 pub fn clear_check(&mut self) -> Vec<FileId> { 35 pub(crate) fn clear_check(&mut self) {
41 Arc::make_mut(&mut self.check_fixes).clear(); 36 Arc::make_mut(&mut self.check_fixes).clear();
42 self.check.drain().map(|(key, _value)| key).collect() 37 self.changes.extend(self.check.drain().map(|(key, _value)| key))
43 } 38 }
44 39
45 pub fn add_check_diagnostic( 40 pub(crate) fn add_check_diagnostic(
46 &mut self, 41 &mut self,
47 file_id: FileId, 42 file_id: FileId,
48 diagnostic: Diagnostic, 43 diagnostic: Diagnostic,
@@ -61,30 +56,25 @@ impl DiagnosticCollection {
61 .or_default() 56 .or_default()
62 .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action })); 57 .extend(fixes.into_iter().map(|action| Fix { range: diagnostic.range, action }));
63 diagnostics.push(diagnostic); 58 diagnostics.push(diagnostic);
59 self.changes.insert(file_id);
64 } 60 }
65 61
66 pub fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) { 62 pub(crate) fn set_native_diagnostics(&mut self, file_id: FileId, diagnostics: Vec<Diagnostic>) {
67 self.native.insert(file_id, diagnostics); 63 self.native.insert(file_id, diagnostics);
64 self.changes.insert(file_id);
68 } 65 }
69 66
70 pub fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> { 67 pub(crate) fn diagnostics_for(&self, file_id: FileId) -> impl Iterator<Item = &Diagnostic> {
71 let native = self.native.get(&file_id).into_iter().flatten(); 68 let native = self.native.get(&file_id).into_iter().flatten();
72 let check = self.check.get(&file_id).into_iter().flatten(); 69 let check = self.check.get(&file_id).into_iter().flatten();
73 native.chain(check) 70 native.chain(check)
74 } 71 }
75 72
76 pub fn handle_task(&mut self, task: DiagnosticTask) -> Vec<FileId> { 73 pub(crate) fn take_changes(&mut self) -> Option<FxHashSet<FileId>> {
77 match task { 74 if self.changes.is_empty() {
78 DiagnosticTask::ClearCheck => self.clear_check(), 75 return None;
79 DiagnosticTask::AddCheck(file_id, diagnostic, fixes) => {
80 self.add_check_diagnostic(file_id, diagnostic, fixes);
81 vec![file_id]
82 }
83 DiagnosticTask::SetNative(file_id, diagnostics) => {
84 self.set_native_diagnostics(file_id, diagnostics);
85 vec![file_id]
86 }
87 } 76 }
77 Some(mem::take(&mut self.changes))
88 } 78 }
89} 79}
90 80
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index b2ff9a157..12b494496 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -31,7 +31,6 @@ use stdx::{format_to, split_delim};
31use crate::{ 31use crate::{
32 cargo_target_spec::CargoTargetSpec, 32 cargo_target_spec::CargoTargetSpec,
33 config::RustfmtConfig, 33 config::RustfmtConfig,
34 diagnostics::DiagnosticTask,
35 from_json, from_proto, 34 from_json, from_proto,
36 global_state::GlobalStateSnapshot, 35 global_state::GlobalStateSnapshot,
37 lsp_ext::{self, InlayHint, InlayHintsParams}, 36 lsp_ext::{self, InlayHint, InlayHintsParams},
@@ -950,7 +949,7 @@ pub(crate) fn handle_ssr(
950pub(crate) fn publish_diagnostics( 949pub(crate) fn publish_diagnostics(
951 snap: &GlobalStateSnapshot, 950 snap: &GlobalStateSnapshot,
952 file_id: FileId, 951 file_id: FileId,
953) -> Result<DiagnosticTask> { 952) -> Result<Vec<Diagnostic>> {
954 let _p = profile("publish_diagnostics"); 953 let _p = profile("publish_diagnostics");
955 let line_index = snap.analysis.file_line_index(file_id)?; 954 let line_index = snap.analysis.file_line_index(file_id)?;
956 let diagnostics: Vec<Diagnostic> = snap 955 let diagnostics: Vec<Diagnostic> = snap
@@ -967,7 +966,7 @@ pub(crate) fn publish_diagnostics(
967 tags: None, 966 tags: None,
968 }) 967 })
969 .collect(); 968 .collect();
970 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 969 Ok(diagnostics)
971} 970}
972 971
973pub(crate) fn handle_inlay_hints( 972pub(crate) fn handle_inlay_hints(
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 76dea3e22..1bd9d6389 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -15,7 +15,6 @@ use ra_project_model::{PackageRoot, ProjectWorkspace};
15 15
16use crate::{ 16use crate::{
17 config::{Config, FilesWatcher, LinkedProject}, 17 config::{Config, FilesWatcher, LinkedProject},
18 diagnostics::DiagnosticTask,
19 dispatch::{NotificationDispatcher, RequestDispatcher}, 18 dispatch::{NotificationDispatcher, RequestDispatcher},
20 from_proto, 19 from_proto,
21 global_state::{file_id_to_url, GlobalState, Status}, 20 global_state::{file_id_to_url, GlobalState, Status},
@@ -132,6 +131,13 @@ enum Event {
132 Flycheck(flycheck::Message), 131 Flycheck(flycheck::Message),
133} 132}
134 133
134#[derive(Debug)]
135pub(crate) enum Task {
136 Response(Response),
137 Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>),
138 Unit,
139}
140
135impl fmt::Debug for Event { 141impl fmt::Debug for Event {
136 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
137 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| { 143 let debug_verbose_not = |not: &Notification, f: &mut fmt::Formatter| {
@@ -219,8 +225,10 @@ impl GlobalState {
219 Event::Task(task) => { 225 Event::Task(task) => {
220 match task { 226 match task {
221 Task::Response(response) => self.respond(response), 227 Task::Response(response) => self.respond(response),
222 Task::Diagnostics(tasks) => { 228 Task::Diagnostics(diagnostics_per_file) => {
223 tasks.into_iter().for_each(|task| on_diagnostic_task(task, self)) 229 for (file_id, diagnostics) in diagnostics_per_file {
230 self.diagnostics.set_native_diagnostics(file_id, diagnostics)
231 }
224 } 232 }
225 Task::Unit => (), 233 Task::Unit => (),
226 } 234 }
@@ -257,9 +265,7 @@ impl GlobalState {
257 } 265 }
258 }, 266 },
259 Event::Flycheck(task) => match task { 267 Event::Flycheck(task) => match task {
260 flycheck::Message::ClearDiagnostics => { 268 flycheck::Message::ClearDiagnostics => self.diagnostics.clear_check(),
261 on_diagnostic_task(DiagnosticTask::ClearCheck, self)
262 }
263 269
264 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { 270 flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
265 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 271 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
@@ -279,15 +285,7 @@ impl GlobalState {
279 return Ok(()); 285 return Ok(());
280 } 286 }
281 }; 287 };
282 288 self.diagnostics.add_check_diagnostic(file_id, diag.diagnostic, diag.fixes)
283 on_diagnostic_task(
284 DiagnosticTask::AddCheck(
285 file_id,
286 diag.diagnostic,
287 diag.fixes.into_iter().map(|it| it.into()).collect(),
288 ),
289 self,
290 )
291 } 289 }
292 } 290 }
293 291
@@ -322,9 +320,20 @@ impl GlobalState {
322 self.update_file_notifications_on_threadpool(subscriptions); 320 self.update_file_notifications_on_threadpool(subscriptions);
323 } 321 }
324 322
323 if let Some(diagnostic_changes) = self.diagnostics.take_changes() {
324 for file_id in diagnostic_changes {
325 let url = file_id_to_url(&self.vfs.read().0, file_id);
326 let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect();
327 let params =
328 lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None };
329 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
330 self.send(not.into());
331 }
332 }
333
325 let loop_duration = loop_start.elapsed(); 334 let loop_duration = loop_start.elapsed();
326 if loop_duration > Duration::from_millis(100) { 335 if loop_duration > Duration::from_millis(100) {
327 log::error!("overly long loop turn: {:?}", loop_duration); 336 log::warn!("overly long loop turn: {:?}", loop_duration);
328 if env::var("RA_PROFILE").is_ok() { 337 if env::var("RA_PROFILE").is_ok() {
329 self.show_message( 338 self.show_message(
330 lsp_types::MessageType::Error, 339 lsp_types::MessageType::Error,
@@ -516,6 +525,7 @@ impl GlobalState {
516 () 525 ()
517 }) 526 })
518 .ok() 527 .ok()
528 .map(|diags| (file_id, diags))
519 }) 529 })
520 .collect::<Vec<_>>(); 530 .collect::<Vec<_>>();
521 Task::Diagnostics(diagnostics) 531 Task::Diagnostics(diagnostics)
@@ -532,29 +542,10 @@ impl GlobalState {
532 } 542 }
533} 543}
534 544
535#[derive(Debug)]
536pub(crate) enum Task {
537 Response(Response),
538 Diagnostics(()),
539 Unit,
540}
541
542pub(crate) type ReqHandler = fn(&mut GlobalState, Response); 545pub(crate) type ReqHandler = fn(&mut GlobalState, Response);
543pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>; 546pub(crate) type ReqQueue = lsp_server::ReqQueue<(String, Instant), ReqHandler>;
544const DO_NOTHING: ReqHandler = |_, _| (); 547const DO_NOTHING: ReqHandler = |_, _| ();
545 548
546fn on_diagnostic_task(task: DiagnosticTask, global_state: &mut GlobalState) {
547 let subscriptions = global_state.diagnostics.handle_task(task);
548
549 for file_id in subscriptions {
550 let url = file_id_to_url(&global_state.vfs.read().0, file_id);
551 let diagnostics = global_state.diagnostics.diagnostics_for(file_id).cloned().collect();
552 let params = lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None };
553 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
554 global_state.send(not.into());
555 }
556}
557
558#[derive(Eq, PartialEq)] 549#[derive(Eq, PartialEq)]
559enum Progress { 550enum Progress {
560 Begin, 551 Begin,