diff options
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r-- | crates/ra_lsp_server/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/config.rs | 3 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 174 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 9 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/world.rs | 5 |
5 files changed, 130 insertions, 63 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 07cc2dd32..46a0f958c 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml | |||
@@ -16,7 +16,7 @@ lsp-types = { version = "0.61.0", features = ["proposed"] } | |||
16 | rustc-hash = "1.0" | 16 | rustc-hash = "1.0" |
17 | parking_lot = "0.9.0" | 17 | parking_lot = "0.9.0" |
18 | jod-thread = "0.1.0" | 18 | jod-thread = "0.1.0" |
19 | ra_vfs = "0.3.0" | 19 | ra_vfs = "0.4.0" |
20 | ra_syntax = { path = "../ra_syntax" } | 20 | ra_syntax = { path = "../ra_syntax" } |
21 | ra_text_edit = { path = "../ra_text_edit" } | 21 | ra_text_edit = { path = "../ra_text_edit" } |
22 | ra_ide_api = { path = "../ra_ide_api" } | 22 | ra_ide_api = { path = "../ra_ide_api" } |
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs index 5c5ae3e18..cf53e7c4c 100644 --- a/crates/ra_lsp_server/src/config.rs +++ b/crates/ra_lsp_server/src/config.rs | |||
@@ -15,6 +15,8 @@ pub struct ServerConfig { | |||
15 | pub publish_decorations: bool, | 15 | pub publish_decorations: bool, |
16 | 16 | ||
17 | pub exclude_globs: Vec<String>, | 17 | pub exclude_globs: Vec<String>, |
18 | #[serde(deserialize_with = "nullable_bool_false")] | ||
19 | pub use_client_watching: bool, | ||
18 | 20 | ||
19 | pub lru_capacity: Option<usize>, | 21 | pub lru_capacity: Option<usize>, |
20 | 22 | ||
@@ -31,6 +33,7 @@ impl Default for ServerConfig { | |||
31 | ServerConfig { | 33 | ServerConfig { |
32 | publish_decorations: false, | 34 | publish_decorations: false, |
33 | exclude_globs: Vec::new(), | 35 | exclude_globs: Vec::new(), |
36 | use_client_watching: false, | ||
34 | lru_capacity: None, | 37 | lru_capacity: None, |
35 | with_sysroot: true, | 38 | with_sysroot: true, |
36 | feature_flags: FxHashMap::default(), | 39 | feature_flags: FxHashMap::default(), |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index 80f0216e8..25fa51b8a 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -9,8 +9,9 @@ use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestI | |||
9 | use lsp_types::{ClientCapabilities, NumberOrString}; | 9 | use lsp_types::{ClientCapabilities, NumberOrString}; |
10 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 10 | use ra_ide_api::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | use ra_vfs::VfsTask; | 12 | use ra_vfs::{VfsTask, Watch}; |
13 | use relative_path::RelativePathBuf; | 13 | use relative_path::RelativePathBuf; |
14 | use rustc_hash::FxHashSet; | ||
14 | use serde::{de::DeserializeOwned, Serialize}; | 15 | use serde::{de::DeserializeOwned, Serialize}; |
15 | use threadpool::ThreadPool; | 16 | use threadpool::ThreadPool; |
16 | 17 | ||
@@ -55,72 +56,96 @@ pub fn main_loop( | |||
55 | ) -> Result<()> { | 56 | ) -> Result<()> { |
56 | log::info!("server_config: {:#?}", config); | 57 | log::info!("server_config: {:#?}", config); |
57 | 58 | ||
58 | // FIXME: support dynamic workspace loading. | 59 | let mut loop_state = LoopState::default(); |
59 | let workspaces = { | 60 | let mut world_state = { |
60 | let mut loaded_workspaces = Vec::new(); | 61 | // FIXME: support dynamic workspace loading. |
61 | for ws_root in &ws_roots { | 62 | let workspaces = { |
62 | let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( | 63 | let mut loaded_workspaces = Vec::new(); |
63 | ws_root.as_path(), | 64 | for ws_root in &ws_roots { |
64 | config.with_sysroot, | 65 | let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( |
65 | ); | 66 | ws_root.as_path(), |
66 | match workspace { | 67 | config.with_sysroot, |
67 | Ok(workspace) => loaded_workspaces.push(workspace), | 68 | ); |
68 | Err(e) => { | 69 | match workspace { |
69 | log::error!("loading workspace failed: {}", e); | 70 | Ok(workspace) => loaded_workspaces.push(workspace), |
71 | Err(e) => { | ||
72 | log::error!("loading workspace failed: {}", e); | ||
73 | |||
74 | show_message( | ||
75 | req::MessageType::Error, | ||
76 | format!("rust-analyzer failed to load workspace: {}", e), | ||
77 | &connection.sender, | ||
78 | ); | ||
79 | } | ||
80 | } | ||
81 | } | ||
82 | loaded_workspaces | ||
83 | }; | ||
70 | 84 | ||
85 | let globs = config | ||
86 | .exclude_globs | ||
87 | .iter() | ||
88 | .map(|glob| ra_vfs_glob::Glob::new(glob)) | ||
89 | .collect::<std::result::Result<Vec<_>, _>>()?; | ||
90 | |||
91 | if config.use_client_watching { | ||
92 | let registration_options = req::DidChangeWatchedFilesRegistrationOptions { | ||
93 | watchers: workspaces | ||
94 | .iter() | ||
95 | .flat_map(|ws| ws.to_roots()) | ||
96 | .filter(|root| root.is_member()) | ||
97 | .map(|root| format!("{}/**/*.rs", root.path().display())) | ||
98 | .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None }) | ||
99 | .collect(), | ||
100 | }; | ||
101 | let registration = req::Registration { | ||
102 | id: "file-watcher".to_string(), | ||
103 | method: "workspace/didChangeWatchedFiles".to_string(), | ||
104 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | ||
105 | }; | ||
106 | let params = req::RegistrationParams { registrations: vec![registration] }; | ||
107 | let request = | ||
108 | request_new::<req::RegisterCapability>(loop_state.next_request_id(), params); | ||
109 | connection.sender.send(request.into()).unwrap(); | ||
110 | } | ||
111 | |||
112 | let feature_flags = { | ||
113 | let mut ff = FeatureFlags::default(); | ||
114 | for (flag, value) in config.feature_flags { | ||
115 | if let Err(_) = ff.set(flag.as_str(), value) { | ||
116 | log::error!("unknown feature flag: {:?}", flag); | ||
71 | show_message( | 117 | show_message( |
72 | req::MessageType::Error, | 118 | req::MessageType::Error, |
73 | format!("rust-analyzer failed to load workspace: {}", e), | 119 | format!("unknown feature flag: {:?}", flag), |
74 | &connection.sender, | 120 | &connection.sender, |
75 | ); | 121 | ); |
76 | } | 122 | } |
77 | } | 123 | } |
78 | } | 124 | ff |
79 | loaded_workspaces | 125 | }; |
80 | }; | 126 | log::info!("feature_flags: {:#?}", feature_flags); |
81 | 127 | ||
82 | let globs = config | 128 | WorldState::new( |
83 | .exclude_globs | 129 | ws_roots, |
84 | .iter() | 130 | workspaces, |
85 | .map(|glob| ra_vfs_glob::Glob::new(glob)) | 131 | config.lru_capacity, |
86 | .collect::<std::result::Result<Vec<_>, _>>()?; | 132 | &globs, |
87 | 133 | Watch(!config.use_client_watching), | |
88 | let feature_flags = { | 134 | Options { |
89 | let mut ff = FeatureFlags::default(); | 135 | publish_decorations: config.publish_decorations, |
90 | for (flag, value) in config.feature_flags { | 136 | supports_location_link: client_caps |
91 | if let Err(_) = ff.set(flag.as_str(), value) { | 137 | .text_document |
92 | log::error!("unknown feature flag: {:?}", flag); | 138 | .and_then(|it| it.definition) |
93 | show_message( | 139 | .and_then(|it| it.link_support) |
94 | req::MessageType::Error, | 140 | .unwrap_or(false), |
95 | format!("unknown feature flag: {:?}", flag), | 141 | }, |
96 | &connection.sender, | 142 | feature_flags, |
97 | ); | 143 | ) |
98 | } | ||
99 | } | ||
100 | ff | ||
101 | }; | 144 | }; |
102 | log::info!("feature_flags: {:#?}", feature_flags); | ||
103 | |||
104 | let mut world_state = WorldState::new( | ||
105 | ws_roots, | ||
106 | workspaces, | ||
107 | config.lru_capacity, | ||
108 | &globs, | ||
109 | Options { | ||
110 | publish_decorations: config.publish_decorations, | ||
111 | supports_location_link: client_caps | ||
112 | .text_document | ||
113 | .and_then(|it| it.definition) | ||
114 | .and_then(|it| it.link_support) | ||
115 | .unwrap_or(false), | ||
116 | }, | ||
117 | feature_flags, | ||
118 | ); | ||
119 | 145 | ||
120 | let pool = ThreadPool::new(THREADPOOL_SIZE); | 146 | let pool = ThreadPool::new(THREADPOOL_SIZE); |
121 | let (task_sender, task_receiver) = unbounded::<Task>(); | 147 | let (task_sender, task_receiver) = unbounded::<Task>(); |
122 | let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); | 148 | let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>(); |
123 | let mut loop_state = LoopState::default(); | ||
124 | 149 | ||
125 | log::info!("server initialized, serving requests"); | 150 | log::info!("server initialized, serving requests"); |
126 | { | 151 | { |
@@ -227,6 +252,8 @@ impl fmt::Debug for Event { | |||
227 | 252 | ||
228 | #[derive(Debug, Default)] | 253 | #[derive(Debug, Default)] |
229 | struct LoopState { | 254 | struct LoopState { |
255 | next_request_id: u64, | ||
256 | pending_responses: FxHashSet<RequestId>, | ||
230 | pending_requests: PendingRequests, | 257 | pending_requests: PendingRequests, |
231 | subscriptions: Subscriptions, | 258 | subscriptions: Subscriptions, |
232 | // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same | 259 | // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same |
@@ -236,6 +263,16 @@ struct LoopState { | |||
236 | workspace_loaded: bool, | 263 | workspace_loaded: bool, |
237 | } | 264 | } |
238 | 265 | ||
266 | impl LoopState { | ||
267 | fn next_request_id(&mut self) -> RequestId { | ||
268 | self.next_request_id += 1; | ||
269 | let res: RequestId = self.next_request_id.into(); | ||
270 | let inserted = self.pending_responses.insert(res.clone()); | ||
271 | assert!(inserted); | ||
272 | res | ||
273 | } | ||
274 | } | ||
275 | |||
239 | fn loop_turn( | 276 | fn loop_turn( |
240 | pool: &ThreadPool, | 277 | pool: &ThreadPool, |
241 | task_sender: &Sender<Task>, | 278 | task_sender: &Sender<Task>, |
@@ -290,7 +327,12 @@ fn loop_turn( | |||
290 | )?; | 327 | )?; |
291 | state_changed = true; | 328 | state_changed = true; |
292 | } | 329 | } |
293 | Message::Response(resp) => log::error!("unexpected response: {:?}", resp), | 330 | Message::Response(resp) => { |
331 | let removed = loop_state.pending_responses.remove(&resp.id); | ||
332 | if !removed { | ||
333 | log::error!("unexpected response: {:?}", resp) | ||
334 | } | ||
335 | } | ||
294 | }, | 336 | }, |
295 | }; | 337 | }; |
296 | 338 | ||
@@ -479,6 +521,18 @@ fn on_notification( | |||
479 | } | 521 | } |
480 | Err(not) => not, | 522 | Err(not) => not, |
481 | }; | 523 | }; |
524 | let not = match notification_cast::<req::DidChangeWatchedFiles>(not) { | ||
525 | Ok(params) => { | ||
526 | let mut vfs = state.vfs.write(); | ||
527 | for change in params.changes { | ||
528 | let uri = change.uri; | ||
529 | let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; | ||
530 | vfs.notify_changed(path) | ||
531 | } | ||
532 | return Ok(()); | ||
533 | } | ||
534 | Err(not) => not, | ||
535 | }; | ||
482 | log::error!("unhandled notification: {:?}", not); | 536 | log::error!("unhandled notification: {:?}", not); |
483 | Ok(()) | 537 | Ok(()) |
484 | } | 538 | } |
@@ -682,3 +736,11 @@ where | |||
682 | { | 736 | { |
683 | Notification::new(N::METHOD.to_string(), params) | 737 | Notification::new(N::METHOD.to_string(), params) |
684 | } | 738 | } |
739 | |||
740 | fn request_new<R>(id: RequestId, params: R::Params) -> Request | ||
741 | where | ||
742 | R: lsp_types::request::Request, | ||
743 | R::Params: Serialize, | ||
744 | { | ||
745 | Request::new(id, R::METHOD.to_string(), params) | ||
746 | } | ||
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 1b23f0c3d..0540f166e 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -5,10 +5,11 @@ use serde::{Deserialize, Serialize}; | |||
5 | pub use lsp_types::{ | 5 | pub use lsp_types::{ |
6 | notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, | 6 | notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, |
7 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, | 7 | CodeLensParams, CompletionParams, CompletionResponse, DidChangeConfigurationParams, |
8 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, Hover, | 8 | DidChangeWatchedFilesParams, DidChangeWatchedFilesRegistrationOptions, |
9 | InitializeResult, MessageType, PublishDiagnosticsParams, ReferenceParams, ShowMessageParams, | 9 | DocumentOnTypeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, |
10 | SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, | 10 | FileSystemWatcher, Hover, InitializeResult, MessageType, PublishDiagnosticsParams, |
11 | WorkspaceSymbolParams, | 11 | ReferenceParams, Registration, RegistrationParams, ShowMessageParams, SignatureHelp, |
12 | TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | pub enum AnalyzerStatus {} | 15 | pub enum AnalyzerStatus {} |
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index e1c5c3343..086ecd587 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs | |||
@@ -12,7 +12,7 @@ use ra_ide_api::{ | |||
12 | SourceRootId, | 12 | SourceRootId, |
13 | }; | 13 | }; |
14 | use ra_project_model::ProjectWorkspace; | 14 | use ra_project_model::ProjectWorkspace; |
15 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask}; | 15 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
16 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; | 16 | use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; |
17 | use relative_path::RelativePathBuf; | 17 | use relative_path::RelativePathBuf; |
18 | 18 | ||
@@ -60,6 +60,7 @@ impl WorldState { | |||
60 | workspaces: Vec<ProjectWorkspace>, | 60 | workspaces: Vec<ProjectWorkspace>, |
61 | lru_capacity: Option<usize>, | 61 | lru_capacity: Option<usize>, |
62 | exclude_globs: &[Glob], | 62 | exclude_globs: &[Glob], |
63 | watch: Watch, | ||
63 | options: Options, | 64 | options: Options, |
64 | feature_flags: FeatureFlags, | 65 | feature_flags: FeatureFlags, |
65 | ) -> WorldState { | 66 | ) -> WorldState { |
@@ -85,7 +86,7 @@ impl WorldState { | |||
85 | } | 86 | } |
86 | let (task_sender, task_receiver) = unbounded(); | 87 | let (task_sender, task_receiver) = unbounded(); |
87 | let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); | 88 | let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); |
88 | let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender); | 89 | let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); |
89 | let roots_to_scan = vfs_roots.len(); | 90 | let roots_to_scan = vfs_roots.len(); |
90 | for r in vfs_roots { | 91 | for r in vfs_roots { |
91 | let vfs_root_path = vfs.root2path(r); | 92 | let vfs_root_path = vfs.root2path(r); |