diff options
Diffstat (limited to 'crates/ra_cargo_watch/src/lib.rs')
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 110 |
1 files changed, 17 insertions, 93 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index a718a5e52..f07c34549 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -4,22 +4,20 @@ | |||
4 | use cargo_metadata::Message; | 4 | use cargo_metadata::Message; |
5 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | 5 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; |
6 | use lsp_types::{ | 6 | use lsp_types::{ |
7 | Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, | 7 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, |
8 | WorkDoneProgressReport, | 8 | WorkDoneProgressEnd, WorkDoneProgressReport, |
9 | }; | 9 | }; |
10 | use std::{ | 10 | use std::{ |
11 | collections::HashMap, | ||
12 | io::{BufRead, BufReader}, | 11 | io::{BufRead, BufReader}, |
13 | path::PathBuf, | 12 | path::PathBuf, |
14 | process::{Command, Stdio}, | 13 | process::{Command, Stdio}, |
15 | sync::Arc, | ||
16 | thread::JoinHandle, | 14 | thread::JoinHandle, |
17 | time::Instant, | 15 | time::Instant, |
18 | }; | 16 | }; |
19 | 17 | ||
20 | mod conv; | 18 | mod conv; |
21 | 19 | ||
22 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic, SuggestedFix}; | 20 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; |
23 | 21 | ||
24 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 22 | pub use crate::conv::url_from_path_with_drive_lowercasing; |
25 | 23 | ||
@@ -38,7 +36,6 @@ pub struct CheckOptions { | |||
38 | #[derive(Debug)] | 36 | #[derive(Debug)] |
39 | pub struct CheckWatcher { | 37 | pub struct CheckWatcher { |
40 | pub task_recv: Receiver<CheckTask>, | 38 | pub task_recv: Receiver<CheckTask>, |
41 | pub state: Arc<CheckState>, | ||
42 | cmd_send: Option<Sender<CheckCommand>>, | 39 | cmd_send: Option<Sender<CheckCommand>>, |
43 | handle: Option<JoinHandle<()>>, | 40 | handle: Option<JoinHandle<()>>, |
44 | } | 41 | } |
@@ -46,7 +43,6 @@ pub struct CheckWatcher { | |||
46 | impl CheckWatcher { | 43 | impl CheckWatcher { |
47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { | 44 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { |
48 | let options = options.clone(); | 45 | let options = options.clone(); |
49 | let state = Arc::new(CheckState::new()); | ||
50 | 46 | ||
51 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 47 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 48 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
@@ -54,13 +50,12 @@ impl CheckWatcher { | |||
54 | let mut check = CheckWatcherThread::new(options, workspace_root); | 50 | let mut check = CheckWatcherThread::new(options, workspace_root); |
55 | check.run(&task_send, &cmd_recv); | 51 | check.run(&task_send, &cmd_recv); |
56 | }); | 52 | }); |
57 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), state } | 53 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle) } |
58 | } | 54 | } |
59 | 55 | ||
60 | /// Returns a CheckWatcher that doesn't actually do anything | 56 | /// Returns a CheckWatcher that doesn't actually do anything |
61 | pub fn dummy() -> CheckWatcher { | 57 | pub fn dummy() -> CheckWatcher { |
62 | let state = Arc::new(CheckState::new()); | 58 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None } |
63 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } | ||
64 | } | 59 | } |
65 | 60 | ||
66 | /// Schedule a re-start of the cargo check worker. | 61 | /// Schedule a re-start of the cargo check worker. |
@@ -87,84 +82,13 @@ impl std::ops::Drop for CheckWatcher { | |||
87 | } | 82 | } |
88 | } | 83 | } |
89 | 84 | ||
90 | #[derive(Clone, Debug)] | ||
91 | pub struct CheckState { | ||
92 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, | ||
93 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, | ||
94 | } | ||
95 | |||
96 | impl CheckState { | ||
97 | fn new() -> CheckState { | ||
98 | CheckState { | ||
99 | diagnostic_collection: HashMap::new(), | ||
100 | suggested_fix_collection: HashMap::new(), | ||
101 | } | ||
102 | } | ||
103 | |||
104 | /// Clear the cached diagnostics, and schedule updating diagnostics by the | ||
105 | /// server, to clear stale results. | ||
106 | pub fn clear(&mut self) -> Vec<Url> { | ||
107 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); | ||
108 | self.diagnostic_collection.clear(); | ||
109 | self.suggested_fix_collection.clear(); | ||
110 | cleared_files | ||
111 | } | ||
112 | |||
113 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { | ||
114 | self.diagnostic_collection.get(uri).map(|d| d.as_slice()) | ||
115 | } | ||
116 | |||
117 | pub fn fixes_for(&self, uri: &Url) -> Option<&[SuggestedFix]> { | ||
118 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) | ||
119 | } | ||
120 | |||
121 | pub fn add_diagnostic_with_fixes(&mut self, file_uri: Url, diagnostic: DiagnosticWithFixes) { | ||
122 | for fix in diagnostic.suggested_fixes { | ||
123 | self.add_suggested_fix_for_diagnostic(fix, &diagnostic.diagnostic); | ||
124 | } | ||
125 | self.add_diagnostic(file_uri, diagnostic.diagnostic); | ||
126 | } | ||
127 | |||
128 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { | ||
129 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); | ||
130 | |||
131 | // If we're building multiple targets it's possible we've already seen this diagnostic | ||
132 | let is_duplicate = diagnostics.iter().any(|d| are_diagnostics_equal(d, &diagnostic)); | ||
133 | if is_duplicate { | ||
134 | return; | ||
135 | } | ||
136 | |||
137 | diagnostics.push(diagnostic); | ||
138 | } | ||
139 | |||
140 | fn add_suggested_fix_for_diagnostic( | ||
141 | &mut self, | ||
142 | mut suggested_fix: SuggestedFix, | ||
143 | diagnostic: &Diagnostic, | ||
144 | ) { | ||
145 | let file_uri = suggested_fix.location.uri.clone(); | ||
146 | let file_suggestions = self.suggested_fix_collection.entry(file_uri).or_default(); | ||
147 | |||
148 | let existing_suggestion: Option<&mut SuggestedFix> = | ||
149 | file_suggestions.iter_mut().find(|s| s == &&suggested_fix); | ||
150 | if let Some(existing_suggestion) = existing_suggestion { | ||
151 | // The existing suggestion also applies to this new diagnostic | ||
152 | existing_suggestion.diagnostics.push(diagnostic.clone()); | ||
153 | } else { | ||
154 | // We haven't seen this suggestion before | ||
155 | suggested_fix.diagnostics.push(diagnostic.clone()); | ||
156 | file_suggestions.push(suggested_fix); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | #[derive(Debug)] | 85 | #[derive(Debug)] |
162 | pub enum CheckTask { | 86 | pub enum CheckTask { |
163 | /// Request a clearing of all cached diagnostics from the check watcher | 87 | /// Request a clearing of all cached diagnostics from the check watcher |
164 | ClearDiagnostics, | 88 | ClearDiagnostics, |
165 | 89 | ||
166 | /// Request adding a diagnostic with fixes included to a file | 90 | /// Request adding a diagnostic with fixes included to a file |
167 | AddDiagnostic(Url, DiagnosticWithFixes), | 91 | AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> }, |
168 | 92 | ||
169 | /// Request check progress notification to client | 93 | /// Request check progress notification to client |
170 | Status(WorkDoneProgress), | 94 | Status(WorkDoneProgress), |
@@ -279,10 +203,17 @@ impl CheckWatcherThread { | |||
279 | None => return, | 203 | None => return, |
280 | }; | 204 | }; |
281 | 205 | ||
282 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; | 206 | let MappedRustDiagnostic { location, diagnostic, fixes } = map_result; |
207 | let fixes = fixes | ||
208 | .into_iter() | ||
209 | .map(|fix| { | ||
210 | CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into() | ||
211 | }) | ||
212 | .collect(); | ||
283 | 213 | ||
284 | let diagnostic = DiagnosticWithFixes { diagnostic, suggested_fixes }; | 214 | task_send |
285 | task_send.send(CheckTask::AddDiagnostic(location.uri, diagnostic)).unwrap(); | 215 | .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes }) |
216 | .unwrap(); | ||
286 | } | 217 | } |
287 | 218 | ||
288 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | 219 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} |
@@ -294,7 +225,7 @@ impl CheckWatcherThread { | |||
294 | #[derive(Debug)] | 225 | #[derive(Debug)] |
295 | pub struct DiagnosticWithFixes { | 226 | pub struct DiagnosticWithFixes { |
296 | diagnostic: Diagnostic, | 227 | diagnostic: Diagnostic, |
297 | suggested_fixes: Vec<SuggestedFix>, | 228 | fixes: Vec<CodeAction>, |
298 | } | 229 | } |
299 | 230 | ||
300 | /// WatchThread exists to wrap around the communication needed to be able to | 231 | /// WatchThread exists to wrap around the communication needed to be able to |
@@ -429,10 +360,3 @@ impl std::ops::Drop for WatchThread { | |||
429 | } | 360 | } |
430 | } | 361 | } |
431 | } | 362 | } |
432 | |||
433 | fn are_diagnostics_equal(left: &Diagnostic, right: &Diagnostic) -> bool { | ||
434 | left.source == right.source | ||
435 | && left.severity == right.severity | ||
436 | && left.range == right.range | ||
437 | && left.message == right.message | ||
438 | } | ||