diff options
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 74 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 28 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 6 |
3 files changed, 64 insertions, 44 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index 20fa5a924..76fdc0031 100644 --- a/crates/ra_cargo_watch/src/lib.rs +++ b/crates/ra_cargo_watch/src/lib.rs | |||
@@ -38,7 +38,7 @@ pub struct CheckOptions { | |||
38 | #[derive(Debug)] | 38 | #[derive(Debug)] |
39 | pub struct CheckWatcher { | 39 | pub struct CheckWatcher { |
40 | pub task_recv: Receiver<CheckTask>, | 40 | pub task_recv: Receiver<CheckTask>, |
41 | pub shared: Arc<RwLock<CheckWatcherSharedState>>, | 41 | pub state: Arc<RwLock<CheckState>>, |
42 | cmd_send: Option<Sender<CheckCommand>>, | 42 | cmd_send: Option<Sender<CheckCommand>>, |
43 | handle: Option<JoinHandle<()>>, | 43 | handle: Option<JoinHandle<()>>, |
44 | } | 44 | } |
@@ -46,22 +46,21 @@ pub struct CheckWatcher { | |||
46 | impl CheckWatcher { | 46 | impl CheckWatcher { |
47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { | 47 | pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { |
48 | let options = options.clone(); | 48 | let options = options.clone(); |
49 | let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new())); | 49 | let state = Arc::new(RwLock::new(CheckState::new())); |
50 | 50 | ||
51 | let (task_send, task_recv) = unbounded::<CheckTask>(); | 51 | let (task_send, task_recv) = unbounded::<CheckTask>(); |
52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); | 52 | let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); |
53 | let shared_ = shared.clone(); | ||
54 | let handle = std::thread::spawn(move || { | 53 | let handle = std::thread::spawn(move || { |
55 | let mut check = CheckWatcherState::new(options, workspace_root, shared_); | 54 | let mut check = CheckWatcherState::new(options, workspace_root); |
56 | check.run(&task_send, &cmd_recv); | 55 | check.run(&task_send, &cmd_recv); |
57 | }); | 56 | }); |
58 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), shared } | 57 | CheckWatcher { task_recv, cmd_send: Some(cmd_send), handle: Some(handle), state } |
59 | } | 58 | } |
60 | 59 | ||
61 | /// Returns a CheckWatcher that doesn't actually do anything | 60 | /// Returns a CheckWatcher that doesn't actually do anything |
62 | pub fn dummy() -> CheckWatcher { | 61 | pub fn dummy() -> CheckWatcher { |
63 | let shared = Arc::new(RwLock::new(CheckWatcherSharedState::new())); | 62 | let state = Arc::new(RwLock::new(CheckState::new())); |
64 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, shared } | 63 | CheckWatcher { task_recv: never(), cmd_send: None, handle: None, state } |
65 | } | 64 | } |
66 | 65 | ||
67 | /// Schedule a re-start of the cargo check worker. | 66 | /// Schedule a re-start of the cargo check worker. |
@@ -89,14 +88,14 @@ impl std::ops::Drop for CheckWatcher { | |||
89 | } | 88 | } |
90 | 89 | ||
91 | #[derive(Debug)] | 90 | #[derive(Debug)] |
92 | pub struct CheckWatcherSharedState { | 91 | pub struct CheckState { |
93 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, | 92 | diagnostic_collection: HashMap<Url, Vec<Diagnostic>>, |
94 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, | 93 | suggested_fix_collection: HashMap<Url, Vec<SuggestedFix>>, |
95 | } | 94 | } |
96 | 95 | ||
97 | impl CheckWatcherSharedState { | 96 | impl CheckState { |
98 | fn new() -> CheckWatcherSharedState { | 97 | fn new() -> CheckState { |
99 | CheckWatcherSharedState { | 98 | CheckState { |
100 | diagnostic_collection: HashMap::new(), | 99 | diagnostic_collection: HashMap::new(), |
101 | suggested_fix_collection: HashMap::new(), | 100 | suggested_fix_collection: HashMap::new(), |
102 | } | 101 | } |
@@ -104,15 +103,11 @@ impl CheckWatcherSharedState { | |||
104 | 103 | ||
105 | /// Clear the cached diagnostics, and schedule updating diagnostics by the | 104 | /// Clear the cached diagnostics, and schedule updating diagnostics by the |
106 | /// server, to clear stale results. | 105 | /// server, to clear stale results. |
107 | pub fn clear(&mut self, task_send: &Sender<CheckTask>) { | 106 | pub fn clear(&mut self) -> Vec<Url> { |
108 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); | 107 | let cleared_files: Vec<Url> = self.diagnostic_collection.keys().cloned().collect(); |
109 | |||
110 | self.diagnostic_collection.clear(); | 108 | self.diagnostic_collection.clear(); |
111 | self.suggested_fix_collection.clear(); | 109 | self.suggested_fix_collection.clear(); |
112 | 110 | cleared_files | |
113 | for uri in cleared_files { | ||
114 | task_send.send(CheckTask::Update(uri.clone())).unwrap(); | ||
115 | } | ||
116 | } | 111 | } |
117 | 112 | ||
118 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { | 113 | pub fn diagnostics_for(&self, uri: &Url) -> Option<&[Diagnostic]> { |
@@ -123,6 +118,13 @@ impl CheckWatcherSharedState { | |||
123 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) | 118 | self.suggested_fix_collection.get(uri).map(|d| d.as_slice()) |
124 | } | 119 | } |
125 | 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 | |||
126 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { | 128 | fn add_diagnostic(&mut self, file_uri: Url, diagnostic: Diagnostic) { |
127 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); | 129 | let diagnostics = self.diagnostic_collection.entry(file_uri).or_default(); |
128 | 130 | ||
@@ -158,8 +160,11 @@ impl CheckWatcherSharedState { | |||
158 | 160 | ||
159 | #[derive(Debug)] | 161 | #[derive(Debug)] |
160 | pub enum CheckTask { | 162 | pub enum CheckTask { |
161 | /// Request a update of the given files diagnostics | 163 | /// Request a clearing of all cached diagnostics from the check watcher |
162 | Update(Url), | 164 | ClearDiagnostics, |
165 | |||
166 | /// Request adding a diagnostic with fixes included to a file | ||
167 | AddDiagnostic(Url, DiagnosticWithFixes), | ||
163 | 168 | ||
164 | /// Request check progress notification to client | 169 | /// Request check progress notification to client |
165 | Status(WorkDoneProgress), | 170 | Status(WorkDoneProgress), |
@@ -175,21 +180,15 @@ struct CheckWatcherState { | |||
175 | workspace_root: PathBuf, | 180 | workspace_root: PathBuf, |
176 | watcher: WatchThread, | 181 | watcher: WatchThread, |
177 | last_update_req: Option<Instant>, | 182 | last_update_req: Option<Instant>, |
178 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
179 | } | 183 | } |
180 | 184 | ||
181 | impl CheckWatcherState { | 185 | impl CheckWatcherState { |
182 | fn new( | 186 | fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherState { |
183 | options: CheckOptions, | ||
184 | workspace_root: PathBuf, | ||
185 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
186 | ) -> CheckWatcherState { | ||
187 | CheckWatcherState { | 187 | CheckWatcherState { |
188 | options, | 188 | options, |
189 | workspace_root, | 189 | workspace_root, |
190 | watcher: WatchThread::dummy(), | 190 | watcher: WatchThread::dummy(), |
191 | last_update_req: None, | 191 | last_update_req: None, |
192 | shared, | ||
193 | } | 192 | } |
194 | } | 193 | } |
195 | 194 | ||
@@ -215,7 +214,7 @@ impl CheckWatcherState { | |||
215 | 214 | ||
216 | if self.should_recheck() { | 215 | if self.should_recheck() { |
217 | self.last_update_req.take(); | 216 | self.last_update_req.take(); |
218 | self.shared.write().clear(task_send); | 217 | task_send.send(CheckTask::ClearDiagnostics).unwrap(); |
219 | 218 | ||
220 | // By replacing the watcher, we drop the previous one which | 219 | // By replacing the watcher, we drop the previous one which |
221 | // causes it to shut down automatically. | 220 | // causes it to shut down automatically. |
@@ -240,7 +239,7 @@ impl CheckWatcherState { | |||
240 | } | 239 | } |
241 | } | 240 | } |
242 | 241 | ||
243 | fn handle_message(&mut self, msg: CheckEvent, task_send: &Sender<CheckTask>) { | 242 | fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) { |
244 | match msg { | 243 | match msg { |
245 | CheckEvent::Begin => { | 244 | CheckEvent::Begin => { |
246 | task_send | 245 | task_send |
@@ -279,18 +278,9 @@ impl CheckWatcherState { | |||
279 | }; | 278 | }; |
280 | 279 | ||
281 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; | 280 | let MappedRustDiagnostic { location, diagnostic, suggested_fixes } = map_result; |
282 | let file_uri = location.uri.clone(); | ||
283 | 281 | ||
284 | if !suggested_fixes.is_empty() { | 282 | let diagnostic = DiagnosticWithFixes { diagnostic, suggested_fixes }; |
285 | for suggested_fix in suggested_fixes { | 283 | task_send.send(CheckTask::AddDiagnostic(location.uri, diagnostic)).unwrap(); |
286 | self.shared | ||
287 | .write() | ||
288 | .add_suggested_fix_for_diagnostic(suggested_fix, &diagnostic); | ||
289 | } | ||
290 | } | ||
291 | self.shared.write().add_diagnostic(file_uri, diagnostic); | ||
292 | |||
293 | task_send.send(CheckTask::Update(location.uri)).unwrap(); | ||
294 | } | 284 | } |
295 | 285 | ||
296 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} | 286 | CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} |
@@ -299,6 +289,12 @@ impl CheckWatcherState { | |||
299 | } | 289 | } |
300 | } | 290 | } |
301 | 291 | ||
292 | #[derive(Debug)] | ||
293 | pub struct DiagnosticWithFixes { | ||
294 | diagnostic: Diagnostic, | ||
295 | suggested_fixes: Vec<SuggestedFix>, | ||
296 | } | ||
297 | |||
302 | /// WatchThread exists to wrap around the communication needed to be able to | 298 | /// WatchThread exists to wrap around the communication needed to be able to |
303 | /// run `cargo check` without blocking. Currently the Rust standard library | 299 | /// run `cargo check` without blocking. Currently the Rust standard library |
304 | /// doesn't provide a way to read sub-process output without blocking, so we | 300 | /// doesn't provide a way to read sub-process output without blocking, so we |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 84012b99d..e087c4f7e 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -337,11 +337,34 @@ fn loop_turn( | |||
337 | loop_state.in_flight_libraries -= 1; | 337 | loop_state.in_flight_libraries -= 1; |
338 | } | 338 | } |
339 | Event::CheckWatcher(task) => match task { | 339 | Event::CheckWatcher(task) => match task { |
340 | CheckTask::Update(uri) => { | 340 | CheckTask::ClearDiagnostics => { |
341 | let cleared_files = world_state.check_watcher.state.write().clear(); | ||
342 | |||
343 | // Send updated diagnostics for each cleared file | ||
344 | for url in cleared_files { | ||
345 | let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; | ||
346 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
347 | let params = handlers::publish_diagnostics( | ||
348 | &world_state.snapshot(), | ||
349 | FileId(file_id.0), | ||
350 | )?; | ||
351 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
352 | task_sender.send(Task::Notify(not)).unwrap(); | ||
353 | } | ||
354 | } | ||
355 | } | ||
356 | |||
357 | CheckTask::AddDiagnostic(url, diagnostic) => { | ||
358 | world_state | ||
359 | .check_watcher | ||
360 | .state | ||
361 | .write() | ||
362 | .add_diagnostic_with_fixes(url.clone(), diagnostic); | ||
363 | |||
341 | // We manually send a diagnostic update when the watcher asks | 364 | // We manually send a diagnostic update when the watcher asks |
342 | // us to, to avoid the issue of having to change the file to | 365 | // us to, to avoid the issue of having to change the file to |
343 | // receive updated diagnostics. | 366 | // receive updated diagnostics. |
344 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | 367 | let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; |
345 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | 368 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { |
346 | let params = | 369 | let params = |
347 | handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | 370 | handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; |
@@ -349,6 +372,7 @@ fn loop_turn( | |||
349 | task_sender.send(Task::Notify(not)).unwrap(); | 372 | task_sender.send(Task::Notify(not)).unwrap(); |
350 | } | 373 | } |
351 | } | 374 | } |
375 | |||
352 | CheckTask::Status(progress) => { | 376 | CheckTask::Status(progress) => { |
353 | let params = req::ProgressParams { | 377 | let params = req::ProgressParams { |
354 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | 378 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index c0175c726..7a3030a51 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -13,7 +13,7 @@ use lsp_server::ErrorCode; | |||
13 | use lsp_types::Url; | 13 | use lsp_types::Url; |
14 | use parking_lot::RwLock; | 14 | use parking_lot::RwLock; |
15 | use ra_cargo_watch::{ | 15 | use ra_cargo_watch::{ |
16 | url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher, CheckWatcherSharedState, | 16 | url_from_path_with_drive_lowercasing, CheckOptions, CheckState, CheckWatcher, |
17 | }; | 17 | }; |
18 | use ra_ide::{ | 18 | use ra_ide::{ |
19 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 19 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
@@ -64,7 +64,7 @@ pub struct WorldSnapshot { | |||
64 | pub analysis: Analysis, | 64 | pub analysis: Analysis, |
65 | pub vfs: Arc<RwLock<Vfs>>, | 65 | pub vfs: Arc<RwLock<Vfs>>, |
66 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 66 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
67 | pub check_watcher: Arc<RwLock<CheckWatcherSharedState>>, | 67 | pub check_watcher: Arc<RwLock<CheckState>>, |
68 | } | 68 | } |
69 | 69 | ||
70 | impl WorldState { | 70 | impl WorldState { |
@@ -220,7 +220,7 @@ impl WorldState { | |||
220 | analysis: self.analysis_host.analysis(), | 220 | analysis: self.analysis_host.analysis(), |
221 | vfs: Arc::clone(&self.vfs), | 221 | vfs: Arc::clone(&self.vfs), |
222 | latest_requests: Arc::clone(&self.latest_requests), | 222 | latest_requests: Arc::clone(&self.latest_requests), |
223 | check_watcher: self.check_watcher.shared.clone(), | 223 | check_watcher: self.check_watcher.state.clone(), |
224 | } | 224 | } |
225 | } | 225 | } |
226 | 226 | ||