diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-12-29 12:57:24 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-12-29 12:57:24 +0000 |
commit | dc48f89581843248660ceb755bb20469ab6ac0c9 (patch) | |
tree | 05a7e47d1c68c250025be1ce6492f56c15353749 /crates/ra_lsp_server | |
parent | cdcb3d3833d3d5b37b2cd4dac91a6e9366f20aea (diff) | |
parent | 899dbebd02b41b12d89c9f485e85208b39b81932 (diff) |
Merge #2668
2668: In-server cargo check watching r=matklad a=kiljacken
Opening a draft now so people can follow the progress, and comment if they spot something stupid.
Things that need doing:
- [x] Running cargo check on save
- [x] Pipe through configuration options from client
- [x] Tests for parsing behavior
- [x] Remove existing cargo watch support from VSCode extension
- [x] Progress notification in VSCode extension using LSP 3.15 `$/progress` notification
- [ ] ~~Rework ra-ide diagnostics to support secondary messages~~
- [ ] ~~Make cargo-check watcher use ra-ide diagnostics~~
~~I'd love some input on whether to try to keep the status bar progress thingy for VSCode? It will require some plumbing, and maintaining yet another rust-analyzer specific LSP notification, which I'm not sure we want to.~~
Fixes #1894
Co-authored-by: Emil Lauridsen <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r-- | crates/ra_lsp_server/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/caps.rs | 4 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/config.rs | 9 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 44 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop/handlers.rs | 28 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 8 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 10 |
7 files changed, 96 insertions, 8 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 030e9033c..9b7dcb6e9 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -27,6 +27,7 @@ ra_project_model = { path = "../ra_project_model" } | |||
27 | ra_prof = { path = "../ra_prof" } | 27 | ra_prof = { path = "../ra_prof" } |
28 | ra_vfs_glob = { path = "../ra_vfs_glob" } | 28 | ra_vfs_glob = { path = "../ra_vfs_glob" } |
29 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } | 29 | env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] } |
30 | ra_cargo_watch = { path = "../ra_cargo_watch" } | ||
30 | 31 | ||
31 | [dev-dependencies] | 32 | [dev-dependencies] |
32 | tempfile = "3" | 33 | tempfile = "3" |
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs index ceb4c4259..0dee1f6fe 100644 --- a/crates/ra_lsp_server/src/caps.rs +++ b/crates/ra_lsp_server/src/caps.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use lsp_types::{ | 3 | use lsp_types::{ |
4 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, | 4 | CodeActionProviderCapability, CodeLensOptions, CompletionOptions, |
5 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, | 5 | DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, |
6 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, | 6 | ImplementationProviderCapability, RenameOptions, RenameProviderCapability, SaveOptions, |
7 | SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, | 7 | SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions, |
8 | TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, | 8 | TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, |
9 | TypeDefinitionProviderCapability, WorkDoneProgressOptions, | 9 | TypeDefinitionProviderCapability, WorkDoneProgressOptions, |
@@ -16,7 +16,7 @@ pub fn server_capabilities() -> ServerCapabilities { | |||
16 | change: Some(TextDocumentSyncKind::Full), | 16 | change: Some(TextDocumentSyncKind::Full), |
17 | will_save: None, | 17 | will_save: None, |
18 | will_save_wait_until: None, | 18 | will_save_wait_until: None, |
19 | save: None, | 19 | save: Some(SaveOptions::default()), |
20 | })), | 20 | })), |
21 | hover_provider: Some(true), | 21 | hover_provider: Some(true), |
22 | completion_provider: Some(CompletionOptions { | 22 | completion_provider: Some(CompletionOptions { |
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs index 67942aa41..2d7948d74 100644 --- a/crates/ra_lsp_server/src/config.rs +++ b/crates/ra_lsp_server/src/config.rs | |||
@@ -32,6 +32,11 @@ pub struct ServerConfig { | |||
32 | 32 | ||
33 | pub max_inlay_hint_length: Option<usize>, | 33 | pub max_inlay_hint_length: Option<usize>, |
34 | 34 | ||
35 | pub cargo_watch_enable: bool, | ||
36 | pub cargo_watch_args: Vec<String>, | ||
37 | pub cargo_watch_command: String, | ||
38 | pub cargo_watch_all_targets: bool, | ||
39 | |||
35 | /// For internal usage to make integrated tests faster. | 40 | /// For internal usage to make integrated tests faster. |
36 | #[serde(deserialize_with = "nullable_bool_true")] | 41 | #[serde(deserialize_with = "nullable_bool_true")] |
37 | pub with_sysroot: bool, | 42 | pub with_sysroot: bool, |
@@ -51,6 +56,10 @@ impl Default for ServerConfig { | |||
51 | use_client_watching: false, | 56 | use_client_watching: false, |
52 | lru_capacity: None, | 57 | lru_capacity: None, |
53 | max_inlay_hint_length: None, | 58 | max_inlay_hint_length: None, |
59 | cargo_watch_enable: true, | ||
60 | cargo_watch_args: Vec::new(), | ||
61 | cargo_watch_command: "check".to_string(), | ||
62 | cargo_watch_all_targets: true, | ||
54 | with_sysroot: true, | 63 | with_sysroot: true, |
55 | feature_flags: FxHashMap::default(), | 64 | feature_flags: FxHashMap::default(), |
56 | cargo_features: Default::default(), | 65 | cargo_features: Default::default(), |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index dda318e43..af1a487de 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -10,6 +10,7 @@ use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; | |||
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}; |
13 | use ra_cargo_watch::{CheckOptions, CheckTask}; | ||
13 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
14 | use ra_prof::profile; | 15 | use ra_prof::profile; |
15 | use ra_vfs::{VfsTask, Watch}; | 16 | use ra_vfs::{VfsTask, Watch}; |
@@ -126,6 +127,12 @@ pub fn main_loop( | |||
126 | .and_then(|it| it.line_folding_only) | 127 | .and_then(|it| it.line_folding_only) |
127 | .unwrap_or(false), | 128 | .unwrap_or(false), |
128 | max_inlay_hint_length: config.max_inlay_hint_length, | 129 | max_inlay_hint_length: config.max_inlay_hint_length, |
130 | cargo_watch: CheckOptions { | ||
131 | enable: config.cargo_watch_enable, | ||
132 | args: config.cargo_watch_args, | ||
133 | command: config.cargo_watch_command, | ||
134 | all_targets: config.cargo_watch_all_targets, | ||
135 | }, | ||
129 | } | 136 | } |
130 | }; | 137 | }; |
131 | 138 | ||
@@ -176,7 +183,11 @@ pub fn main_loop( | |||
176 | Ok(task) => Event::Vfs(task), | 183 | Ok(task) => Event::Vfs(task), |
177 | Err(RecvError) => Err("vfs died")?, | 184 | Err(RecvError) => Err("vfs died")?, |
178 | }, | 185 | }, |
179 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()) | 186 | recv(libdata_receiver) -> data => Event::Lib(data.unwrap()), |
187 | recv(world_state.check_watcher.task_recv) -> task => match task { | ||
188 | Ok(task) => Event::CheckWatcher(task), | ||
189 | Err(RecvError) => Err("check watcher died")?, | ||
190 | } | ||
180 | }; | 191 | }; |
181 | if let Event::Msg(Message::Request(req)) = &event { | 192 | if let Event::Msg(Message::Request(req)) = &event { |
182 | if connection.handle_shutdown(&req)? { | 193 | if connection.handle_shutdown(&req)? { |
@@ -222,6 +233,7 @@ enum Event { | |||
222 | Task(Task), | 233 | Task(Task), |
223 | Vfs(VfsTask), | 234 | Vfs(VfsTask), |
224 | Lib(LibraryData), | 235 | Lib(LibraryData), |
236 | CheckWatcher(CheckTask), | ||
225 | } | 237 | } |
226 | 238 | ||
227 | impl fmt::Debug for Event { | 239 | impl fmt::Debug for Event { |
@@ -259,6 +271,7 @@ impl fmt::Debug for Event { | |||
259 | Event::Task(it) => fmt::Debug::fmt(it, f), | 271 | Event::Task(it) => fmt::Debug::fmt(it, f), |
260 | Event::Vfs(it) => fmt::Debug::fmt(it, f), | 272 | Event::Vfs(it) => fmt::Debug::fmt(it, f), |
261 | Event::Lib(it) => fmt::Debug::fmt(it, f), | 273 | Event::Lib(it) => fmt::Debug::fmt(it, f), |
274 | Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), | ||
262 | } | 275 | } |
263 | } | 276 | } |
264 | } | 277 | } |
@@ -318,6 +331,28 @@ fn loop_turn( | |||
318 | world_state.maybe_collect_garbage(); | 331 | world_state.maybe_collect_garbage(); |
319 | loop_state.in_flight_libraries -= 1; | 332 | loop_state.in_flight_libraries -= 1; |
320 | } | 333 | } |
334 | Event::CheckWatcher(task) => match task { | ||
335 | CheckTask::Update(uri) => { | ||
336 | // We manually send a diagnostic update when the watcher asks | ||
337 | // us to, to avoid the issue of having to change the file to | ||
338 | // receive updated diagnostics. | ||
339 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | ||
340 | if let Some(file_id) = world_state.vfs.read().path2file(&path) { | ||
341 | let params = | ||
342 | handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?; | ||
343 | let not = notification_new::<req::PublishDiagnostics>(params); | ||
344 | task_sender.send(Task::Notify(not)).unwrap(); | ||
345 | } | ||
346 | } | ||
347 | CheckTask::Status(progress) => { | ||
348 | let params = req::ProgressParams { | ||
349 | token: req::ProgressToken::String("rustAnalyzer/cargoWatcher".to_string()), | ||
350 | value: req::ProgressParamsValue::WorkDone(progress), | ||
351 | }; | ||
352 | let not = notification_new::<req::Progress>(params); | ||
353 | task_sender.send(Task::Notify(not)).unwrap(); | ||
354 | } | ||
355 | }, | ||
321 | Event::Msg(msg) => match msg { | 356 | Event::Msg(msg) => match msg { |
322 | Message::Request(req) => on_request( | 357 | Message::Request(req) => on_request( |
323 | world_state, | 358 | world_state, |
@@ -517,6 +552,13 @@ fn on_notification( | |||
517 | } | 552 | } |
518 | Err(not) => not, | 553 | Err(not) => not, |
519 | }; | 554 | }; |
555 | let not = match notification_cast::<req::DidSaveTextDocument>(not) { | ||
556 | Ok(_params) => { | ||
557 | state.check_watcher.update(); | ||
558 | return Ok(()); | ||
559 | } | ||
560 | Err(not) => not, | ||
561 | }; | ||
520 | let not = match notification_cast::<req::DidCloseTextDocument>(not) { | 562 | let not = match notification_cast::<req::DidCloseTextDocument>(not) { |
521 | Ok(params) => { | 563 | Ok(params) => { |
522 | let uri = params.text_document.uri; | 564 | let uri = params.text_document.uri; |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 39eb3df3e..331beab13 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -654,6 +654,29 @@ pub fn handle_code_action( | |||
654 | res.push(action.into()); | 654 | res.push(action.into()); |
655 | } | 655 | } |
656 | 656 | ||
657 | for fix in world.check_watcher.read().fixes_for(¶ms.text_document.uri).into_iter().flatten() | ||
658 | { | ||
659 | let fix_range = fix.location.range.conv_with(&line_index); | ||
660 | if fix_range.intersection(&range).is_none() { | ||
661 | continue; | ||
662 | } | ||
663 | |||
664 | let edits = vec![TextEdit::new(fix.location.range, fix.replacement.clone())]; | ||
665 | let mut edit_map = std::collections::HashMap::new(); | ||
666 | edit_map.insert(fix.location.uri.clone(), edits); | ||
667 | let edit = WorkspaceEdit::new(edit_map); | ||
668 | |||
669 | let action = CodeAction { | ||
670 | title: fix.title.clone(), | ||
671 | kind: Some("quickfix".to_string()), | ||
672 | diagnostics: Some(fix.diagnostics.clone()), | ||
673 | edit: Some(edit), | ||
674 | command: None, | ||
675 | is_preferred: None, | ||
676 | }; | ||
677 | res.push(action.into()); | ||
678 | } | ||
679 | |||
657 | for assist in assists { | 680 | for assist in assists { |
658 | let title = assist.change.label.clone(); | 681 | let title = assist.change.label.clone(); |
659 | let edit = assist.change.try_conv_with(&world)?; | 682 | let edit = assist.change.try_conv_with(&world)?; |
@@ -820,7 +843,7 @@ pub fn publish_diagnostics( | |||
820 | let _p = profile("publish_diagnostics"); | 843 | let _p = profile("publish_diagnostics"); |
821 | let uri = world.file_id_to_uri(file_id)?; | 844 | let uri = world.file_id_to_uri(file_id)?; |
822 | let line_index = world.analysis().file_line_index(file_id)?; | 845 | let line_index = world.analysis().file_line_index(file_id)?; |
823 | let diagnostics = world | 846 | let mut diagnostics: Vec<Diagnostic> = world |
824 | .analysis() | 847 | .analysis() |
825 | .diagnostics(file_id)? | 848 | .diagnostics(file_id)? |
826 | .into_iter() | 849 | .into_iter() |
@@ -834,6 +857,9 @@ pub fn publish_diagnostics( | |||
834 | tags: None, | 857 | tags: None, |
835 | }) | 858 | }) |
836 | .collect(); | 859 | .collect(); |
860 | if let Some(check_diags) = world.check_watcher.read().diagnostics_for(&uri) { | ||
861 | diagnostics.extend(check_diags.iter().cloned()); | ||
862 | } | ||
837 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) | 863 | Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None }) |
838 | } | 864 | } |
839 | 865 | ||
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index b34e6f9b8..40edaf677 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -9,10 +9,10 @@ pub use lsp_types::{ | |||
9 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, | 9 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, |
10 | DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, | 10 | DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, |
11 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, | 11 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, |
12 | FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, | 12 | FileSystemWatcher, Hover, InitializeResult, MessageType, ProgressParams, ProgressParamsValue, |
13 | ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams, | 13 | ProgressToken, PublishDiagnosticsParams, ReferenceParams, Registration, RegistrationParams, |
14 | ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, | 14 | SelectionRange, SelectionRangeParams, ShowMessageParams, SignatureHelp, TextDocumentEdit, |
15 | WorkspaceEdit, WorkspaceSymbolParams, | 15 | TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, |
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub enum AnalyzerStatus {} | 18 | pub enum AnalyzerStatus {} |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 79431e7e6..4b3959e38 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -12,6 +12,7 @@ use crossbeam_channel::{unbounded, Receiver}; | |||
12 | use lsp_server::ErrorCode; | 12 | 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::{CheckOptions, CheckWatcher, CheckWatcherSharedState}; | ||
15 | use ra_ide::{ | 16 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, | 17 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FeatureFlags, FileId, LibraryData, |
17 | SourceRootId, | 18 | SourceRootId, |
@@ -34,6 +35,7 @@ pub struct Options { | |||
34 | pub supports_location_link: bool, | 35 | pub supports_location_link: bool, |
35 | pub line_folding_only: bool, | 36 | pub line_folding_only: bool, |
36 | pub max_inlay_hint_length: Option<usize>, | 37 | pub max_inlay_hint_length: Option<usize>, |
38 | pub cargo_watch: CheckOptions, | ||
37 | } | 39 | } |
38 | 40 | ||
39 | /// `WorldState` is the primary mutable state of the language server | 41 | /// `WorldState` is the primary mutable state of the language server |
@@ -52,6 +54,7 @@ pub struct WorldState { | |||
52 | pub vfs: Arc<RwLock<Vfs>>, | 54 | pub vfs: Arc<RwLock<Vfs>>, |
53 | pub task_receiver: Receiver<VfsTask>, | 55 | pub task_receiver: Receiver<VfsTask>, |
54 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 56 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
57 | pub check_watcher: CheckWatcher, | ||
55 | } | 58 | } |
56 | 59 | ||
57 | /// An immutable snapshot of the world's state at a point in time. | 60 | /// An immutable snapshot of the world's state at a point in time. |
@@ -61,6 +64,7 @@ pub struct WorldSnapshot { | |||
61 | pub analysis: Analysis, | 64 | pub analysis: Analysis, |
62 | pub vfs: Arc<RwLock<Vfs>>, | 65 | pub vfs: Arc<RwLock<Vfs>>, |
63 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 66 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
67 | pub check_watcher: Arc<RwLock<CheckWatcherSharedState>>, | ||
64 | } | 68 | } |
65 | 69 | ||
66 | impl WorldState { | 70 | impl WorldState { |
@@ -127,6 +131,10 @@ impl WorldState { | |||
127 | } | 131 | } |
128 | change.set_crate_graph(crate_graph); | 132 | change.set_crate_graph(crate_graph); |
129 | 133 | ||
134 | // FIXME: Figure out the multi-workspace situation | ||
135 | let check_watcher = | ||
136 | CheckWatcher::new(&options.cargo_watch, folder_roots.first().cloned().unwrap()); | ||
137 | |||
130 | let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); | 138 | let mut analysis_host = AnalysisHost::new(lru_capacity, feature_flags); |
131 | analysis_host.apply_change(change); | 139 | analysis_host.apply_change(change); |
132 | WorldState { | 140 | WorldState { |
@@ -138,6 +146,7 @@ impl WorldState { | |||
138 | vfs: Arc::new(RwLock::new(vfs)), | 146 | vfs: Arc::new(RwLock::new(vfs)), |
139 | task_receiver, | 147 | task_receiver, |
140 | latest_requests: Default::default(), | 148 | latest_requests: Default::default(), |
149 | check_watcher, | ||
141 | } | 150 | } |
142 | } | 151 | } |
143 | 152 | ||
@@ -199,6 +208,7 @@ impl WorldState { | |||
199 | analysis: self.analysis_host.analysis(), | 208 | analysis: self.analysis_host.analysis(), |
200 | vfs: Arc::clone(&self.vfs), | 209 | vfs: Arc::clone(&self.vfs), |
201 | latest_requests: Arc::clone(&self.latest_requests), | 210 | latest_requests: Arc::clone(&self.latest_requests), |
211 | check_watcher: self.check_watcher.shared.clone(), | ||
202 | } | 212 | } |
203 | } | 213 | } |
204 | 214 | ||