diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 80 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 79 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 6 |
3 files changed, 97 insertions, 68 deletions
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs index 20fa5a924..7f4c9280c 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 = CheckWatcherThread::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), |
@@ -170,26 +175,20 @@ pub enum CheckCommand { | |||
170 | Update, | 175 | Update, |
171 | } | 176 | } |
172 | 177 | ||
173 | struct CheckWatcherState { | 178 | struct CheckWatcherThread { |
174 | options: CheckOptions, | 179 | options: CheckOptions, |
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 CheckWatcherThread { |
182 | fn new( | 186 | fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread { |
183 | options: CheckOptions, | 187 | CheckWatcherThread { |
184 | workspace_root: PathBuf, | ||
185 | shared: Arc<RwLock<CheckWatcherSharedState>>, | ||
186 | ) -> 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..7822be2e2 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -9,7 +9,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; | |||
9 | 9 | ||
10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; | 10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; |
11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
12 | use lsp_types::{ClientCapabilities, NumberOrString}; | 12 | use lsp_types::{ClientCapabilities, NumberOrString, Url}; |
13 | use ra_cargo_watch::{CheckOptions, CheckTask}; | 13 | use ra_cargo_watch::{CheckOptions, CheckTask}; |
14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
15 | use ra_prof::profile; | 15 | use ra_prof::profile; |
@@ -336,28 +336,7 @@ fn loop_turn( | |||
336 | world_state.maybe_collect_garbage(); | 336 | world_state.maybe_collect_garbage(); |
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) => on_check_task(task, world_state, task_sender)?, |
340 | CheckTask::Update(uri) => { | ||
341 | // We manually send a diagnostic update when the watcher asks | ||
342 | // us to, to avoid the issue of having to change the file to | ||
343 | // receive updated diagnostics. | ||
344 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | ||
345 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
346 | let params = | ||
347 | handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | ||
348 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
349 | task_sender.send(Task::Notify(not)).unwrap(); | ||
350 | } | ||
351 | } | ||
352 | CheckTask::Status(progress) => { | ||
353 | let params = req::ProgressParams { | ||
354 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | ||
355 | value: req::ProgressParamsValue::WorkDone(progress), | ||
356 | }; | ||
357 | let not = notification_new::<req::Progress>(params); | ||
358 | task_sender.send(Task::Notify(not)).unwrap(); | ||
359 | } | ||
360 | }, | ||
361 | Event::Msg(msg) => match msg { | 340 | Event::Msg(msg) => match msg { |
362 | Message::Request(req) => on_request( | 341 | Message::Request(req) => on_request( |
363 | world_state, | 342 | world_state, |
@@ -605,6 +584,60 @@ fn on_notification( | |||
605 | Ok(()) | 584 | Ok(()) |
606 | } | 585 | } |
607 | 586 | ||
587 | fn on_check_task( | ||
588 | task: CheckTask, | ||
589 | world_state: &WorldState, | ||
590 | task_sender: &Sender<Task>, | ||
591 | ) -> Result<()> { | ||
592 | match task { | ||
593 | CheckTask::ClearDiagnostics => { | ||
594 | let cleared_files = world_state.check_watcher.state.write().clear(); | ||
595 | |||
596 | // Send updated diagnostics for each cleared file | ||
597 | for url in cleared_files { | ||
598 | publish_diagnostics_for_url(&url, world_state, task_sender)?; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | CheckTask::AddDiagnostic(url, diagnostic) => { | ||
603 | world_state | ||
604 | .check_watcher | ||
605 | .state | ||
606 | .write() | ||
607 | .add_diagnostic_with_fixes(url.clone(), diagnostic); | ||
608 | |||
609 | // We manually send a diagnostic update when the watcher asks | ||
610 | // us to, to avoid the issue of having to change the file to | ||
611 | // receive updated diagnostics. | ||
612 | publish_diagnostics_for_url(&url, world_state, task_sender)?; | ||
613 | } | ||
614 | |||
615 | CheckTask::Status(progress) => { | ||
616 | let params = req::ProgressParams { | ||
617 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | ||
618 | value: req::ProgressParamsValue::WorkDone(progress), | ||
619 | }; | ||
620 | let not = notification_new::<req::Progress>(params); | ||
621 | task_sender.send(Task::Notify(not)).unwrap(); | ||
622 | } | ||
623 | } | ||
624 | Ok(()) | ||
625 | } | ||
626 | |||
627 | fn publish_diagnostics_for_url( | ||
628 | url: &Url, | ||
629 | world_state: &WorldState, | ||
630 | task_sender: &Sender<Task>, | ||
631 | ) -> Result<()> { | ||
632 | let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; | ||
633 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
634 | let params = handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | ||
635 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
636 | task_sender.send(Task::Notify(not)).unwrap(); | ||
637 | } | ||
638 | Ok(()) | ||
639 | } | ||
640 | |||
608 | struct PoolDispatcher<'a> { | 641 | struct PoolDispatcher<'a> { |
609 | req: Option<Request>, | 642 | req: Option<Request>, |
610 | pool: &'a ThreadPool, | 643 | pool: &'a ThreadPool, |
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 | ||