diff options
Diffstat (limited to 'crates/ra_lsp_server/src')
-rw-r--r-- | crates/ra_lsp_server/src/init.rs | 39 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main.rs | 20 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 57 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/req.rs | 10 |
5 files changed, 84 insertions, 45 deletions
diff --git a/crates/ra_lsp_server/src/init.rs b/crates/ra_lsp_server/src/init.rs new file mode 100644 index 000000000..0b7a47a0b --- /dev/null +++ b/crates/ra_lsp_server/src/init.rs | |||
@@ -0,0 +1,39 @@ | |||
1 | use serde::{Deserialize, Deserializer}; | ||
2 | |||
3 | /// Client provided initialization options | ||
4 | #[derive(Deserialize, Clone, Copy, Debug)] | ||
5 | #[serde(rename_all = "camelCase")] | ||
6 | pub struct InitializationOptions { | ||
7 | /// Whether the client supports our custom highlighting publishing decorations. | ||
8 | /// This is different to the highlightingOn setting, which is whether the user | ||
9 | /// wants our custom highlighting to be used. | ||
10 | /// | ||
11 | /// Defaults to `true` | ||
12 | #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] | ||
13 | pub publish_decorations: bool, | ||
14 | |||
15 | /// Whether or not the workspace loaded notification should be sent | ||
16 | /// | ||
17 | /// Defaults to `true` | ||
18 | #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] | ||
19 | pub show_workspace_loaded: bool, | ||
20 | } | ||
21 | |||
22 | impl Default for InitializationOptions { | ||
23 | fn default() -> InitializationOptions { | ||
24 | InitializationOptions { publish_decorations: true, show_workspace_loaded: true } | ||
25 | } | ||
26 | } | ||
27 | |||
28 | fn bool_true() -> bool { | ||
29 | true | ||
30 | } | ||
31 | |||
32 | /// Deserializes a null value to a bool true by default | ||
33 | fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> | ||
34 | where | ||
35 | D: Deserializer<'de>, | ||
36 | { | ||
37 | let opt = Option::deserialize(deserializer)?; | ||
38 | Ok(opt.unwrap_or(true)) | ||
39 | } | ||
diff --git a/crates/ra_lsp_server/src/lib.rs b/crates/ra_lsp_server/src/lib.rs index 5b5f3b948..59e16a47c 100644 --- a/crates/ra_lsp_server/src/lib.rs +++ b/crates/ra_lsp_server/src/lib.rs | |||
@@ -5,7 +5,8 @@ mod main_loop; | |||
5 | mod markdown; | 5 | mod markdown; |
6 | mod project_model; | 6 | mod project_model; |
7 | pub mod req; | 7 | pub mod req; |
8 | pub mod init; | ||
8 | mod server_world; | 9 | mod server_world; |
9 | 10 | ||
10 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; | 11 | pub type Result<T> = ::std::result::Result<T, ::failure::Error>; |
11 | pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError}; | 12 | pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError, init::InitializationOptions}; |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 03f83c7be..5a2905207 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -2,7 +2,7 @@ use serde::Deserialize; | |||
2 | use flexi_logger::{Duplicate, Logger}; | 2 | use flexi_logger::{Duplicate, Logger}; |
3 | use gen_lsp_server::{run_server, stdio_transport}; | 3 | use gen_lsp_server::{run_server, stdio_transport}; |
4 | 4 | ||
5 | use ra_lsp_server::Result; | 5 | use ra_lsp_server::{Result, InitializationOptions}; |
6 | 6 | ||
7 | fn main() -> Result<()> { | 7 | fn main() -> Result<()> { |
8 | ::std::env::set_var("RUST_BACKTRACE", "short"); | 8 | ::std::env::set_var("RUST_BACKTRACE", "short"); |
@@ -24,26 +24,18 @@ fn main() -> Result<()> { | |||
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 | ||
27 | #[derive(Deserialize)] | ||
28 | #[serde(rename_all = "camelCase")] | ||
29 | struct InitializationOptions { | ||
30 | // Whether the client supports our custom highlighting publishing decorations. | ||
31 | // This is different to the highlightingOn setting, which is whether the user | ||
32 | // wants our custom highlighting to be used. | ||
33 | publish_decorations: Option<bool>, | ||
34 | } | ||
35 | |||
36 | fn main_inner() -> Result<()> { | 27 | fn main_inner() -> Result<()> { |
37 | let (receiver, sender, threads) = stdio_transport(); | 28 | let (receiver, sender, threads) = stdio_transport(); |
38 | let cwd = ::std::env::current_dir()?; | 29 | let cwd = ::std::env::current_dir()?; |
39 | run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { | 30 | run_server(ra_lsp_server::server_capabilities(), receiver, sender, |params, r, s| { |
40 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); | 31 | let root = params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd); |
41 | let supports_decorations = params | 32 | |
33 | let opts = params | ||
42 | .initialization_options | 34 | .initialization_options |
43 | .and_then(|v| InitializationOptions::deserialize(v).ok()) | 35 | .and_then(|v| InitializationOptions::deserialize(v).ok()) |
44 | .and_then(|it| it.publish_decorations) | 36 | .unwrap_or(InitializationOptions::default()); |
45 | == Some(true); | 37 | |
46 | ra_lsp_server::main_loop(false, root, supports_decorations, r, s) | 38 | ra_lsp_server::main_loop(root, opts, r, s) |
47 | })?; | 39 | })?; |
48 | log::info!("shutting down IO..."); | 40 | log::info!("shutting down IO..."); |
49 | threads.join()?; | 41 | threads.join()?; |
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs index ce50fb301..d0c2a95ef 100644 --- a/crates/ra_lsp_server/src/main_loop.rs +++ b/crates/ra_lsp_server/src/main_loop.rs | |||
@@ -22,6 +22,7 @@ use crate::{ | |||
22 | req, | 22 | req, |
23 | server_world::{ServerWorld, ServerWorldState}, | 23 | server_world::{ServerWorld, ServerWorldState}, |
24 | Result, | 24 | Result, |
25 | InitializationOptions, | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | #[derive(Debug, Fail)] | 28 | #[derive(Debug, Fail)] |
@@ -46,9 +47,8 @@ enum Task { | |||
46 | const THREADPOOL_SIZE: usize = 8; | 47 | const THREADPOOL_SIZE: usize = 8; |
47 | 48 | ||
48 | pub fn main_loop( | 49 | pub fn main_loop( |
49 | internal_mode: bool, | ||
50 | ws_root: PathBuf, | 50 | ws_root: PathBuf, |
51 | supports_decorations: bool, | 51 | options: InitializationOptions, |
52 | msg_receiver: &Receiver<RawMessage>, | 52 | msg_receiver: &Receiver<RawMessage>, |
53 | msg_sender: &Sender<RawMessage>, | 53 | msg_sender: &Sender<RawMessage>, |
54 | ) -> Result<()> { | 54 | ) -> Result<()> { |
@@ -63,6 +63,12 @@ pub fn main_loop( | |||
63 | Ok(ws) => vec![ws], | 63 | Ok(ws) => vec![ws], |
64 | Err(e) => { | 64 | Err(e) => { |
65 | log::error!("loading workspace failed: {}", e); | 65 | log::error!("loading workspace failed: {}", e); |
66 | |||
67 | show_message( | ||
68 | req::MessageType::Error, | ||
69 | format!("rust-analyzer failed to load workspace: {}", e), | ||
70 | msg_sender, | ||
71 | ); | ||
66 | Vec::new() | 72 | Vec::new() |
67 | } | 73 | } |
68 | } | 74 | } |
@@ -75,8 +81,7 @@ pub fn main_loop( | |||
75 | let mut pending_requests = FxHashSet::default(); | 81 | let mut pending_requests = FxHashSet::default(); |
76 | let mut subs = Subscriptions::new(); | 82 | let mut subs = Subscriptions::new(); |
77 | let main_res = main_loop_inner( | 83 | let main_res = main_loop_inner( |
78 | internal_mode, | 84 | options, |
79 | supports_decorations, | ||
80 | &pool, | 85 | &pool, |
81 | msg_sender, | 86 | msg_sender, |
82 | msg_receiver, | 87 | msg_receiver, |
@@ -143,8 +148,7 @@ impl fmt::Debug for Event { | |||
143 | } | 148 | } |
144 | 149 | ||
145 | fn main_loop_inner( | 150 | fn main_loop_inner( |
146 | internal_mode: bool, | 151 | options: InitializationOptions, |
147 | supports_decorations: bool, | ||
148 | pool: &ThreadPool, | 152 | pool: &ThreadPool, |
149 | msg_sender: &Sender<RawMessage>, | 153 | msg_sender: &Sender<RawMessage>, |
150 | msg_receiver: &Receiver<RawMessage>, | 154 | msg_receiver: &Receiver<RawMessage>, |
@@ -158,6 +162,7 @@ fn main_loop_inner( | |||
158 | // time to always have a thread ready to react to input. | 162 | // time to always have a thread ready to react to input. |
159 | let mut in_flight_libraries = 0; | 163 | let mut in_flight_libraries = 0; |
160 | let mut pending_libraries = Vec::new(); | 164 | let mut pending_libraries = Vec::new(); |
165 | let mut send_workspace_notification = true; | ||
161 | 166 | ||
162 | let (libdata_sender, libdata_receiver) = unbounded(); | 167 | let (libdata_sender, libdata_receiver) = unbounded(); |
163 | loop { | 168 | loop { |
@@ -185,7 +190,6 @@ fn main_loop_inner( | |||
185 | state_changed = true; | 190 | state_changed = true; |
186 | } | 191 | } |
187 | Event::Lib(lib) => { | 192 | Event::Lib(lib) => { |
188 | feedback(internal_mode, "library loaded", msg_sender); | ||
189 | state.add_lib(lib); | 193 | state.add_lib(lib); |
190 | in_flight_libraries -= 1; | 194 | in_flight_libraries -= 1; |
191 | } | 195 | } |
@@ -199,7 +203,7 @@ fn main_loop_inner( | |||
199 | Ok((id, ())) => { | 203 | Ok((id, ())) => { |
200 | state.collect_garbage(); | 204 | state.collect_garbage(); |
201 | let resp = RawResponse::ok::<req::CollectGarbage>(id, &()); | 205 | let resp = RawResponse::ok::<req::CollectGarbage>(id, &()); |
202 | msg_sender.send(RawMessage::Response(resp)).unwrap() | 206 | msg_sender.send(resp.into()).unwrap() |
203 | } | 207 | } |
204 | Err(req) => { | 208 | Err(req) => { |
205 | match on_request(state, pending_requests, pool, &task_sender, req)? { | 209 | match on_request(state, pending_requests, pool, &task_sender, req)? { |
@@ -211,7 +215,7 @@ fn main_loop_inner( | |||
211 | ErrorCode::MethodNotFound as i32, | 215 | ErrorCode::MethodNotFound as i32, |
212 | "unknown request".to_string(), | 216 | "unknown request".to_string(), |
213 | ); | 217 | ); |
214 | msg_sender.send(RawMessage::Response(resp)).unwrap() | 218 | msg_sender.send(resp.into()).unwrap() |
215 | } | 219 | } |
216 | } | 220 | } |
217 | } | 221 | } |
@@ -239,15 +243,23 @@ fn main_loop_inner( | |||
239 | }); | 243 | }); |
240 | } | 244 | } |
241 | 245 | ||
242 | if state.roots_to_scan == 0 && pending_libraries.is_empty() && in_flight_libraries == 0 { | 246 | if send_workspace_notification |
243 | feedback(internal_mode, "workspace loaded", msg_sender); | 247 | && state.roots_to_scan == 0 |
248 | && pending_libraries.is_empty() | ||
249 | && in_flight_libraries == 0 | ||
250 | { | ||
251 | if options.show_workspace_loaded { | ||
252 | show_message(req::MessageType::Info, "workspace loaded", msg_sender); | ||
253 | } | ||
254 | // Only send the notification first time | ||
255 | send_workspace_notification = false; | ||
244 | } | 256 | } |
245 | 257 | ||
246 | if state_changed { | 258 | if state_changed { |
247 | update_file_notifications_on_threadpool( | 259 | update_file_notifications_on_threadpool( |
248 | pool, | 260 | pool, |
249 | state.snapshot(), | 261 | state.snapshot(), |
250 | supports_decorations, | 262 | options.publish_decorations, |
251 | task_sender.clone(), | 263 | task_sender.clone(), |
252 | subs.subscriptions(), | 264 | subs.subscriptions(), |
253 | ) | 265 | ) |
@@ -260,11 +272,11 @@ fn on_task(task: Task, msg_sender: &Sender<RawMessage>, pending_requests: &mut F | |||
260 | match task { | 272 | match task { |
261 | Task::Respond(response) => { | 273 | Task::Respond(response) => { |
262 | if pending_requests.remove(&response.id) { | 274 | if pending_requests.remove(&response.id) { |
263 | msg_sender.send(RawMessage::Response(response)).unwrap(); | 275 | msg_sender.send(response.into()).unwrap(); |
264 | } | 276 | } |
265 | } | 277 | } |
266 | Task::Notify(n) => { | 278 | Task::Notify(n) => { |
267 | msg_sender.send(RawMessage::Notification(n)).unwrap(); | 279 | msg_sender.send(n.into()).unwrap(); |
268 | } | 280 | } |
269 | } | 281 | } |
270 | } | 282 | } |
@@ -336,7 +348,7 @@ fn on_notification( | |||
336 | ErrorCode::RequestCanceled as i32, | 348 | ErrorCode::RequestCanceled as i32, |
337 | "canceled by client".to_string(), | 349 | "canceled by client".to_string(), |
338 | ); | 350 | ); |
339 | msg_sender.send(RawMessage::Response(response)).unwrap() | 351 | msg_sender.send(response.into()).unwrap() |
340 | } | 352 | } |
341 | return Ok(()); | 353 | return Ok(()); |
342 | } | 354 | } |
@@ -375,7 +387,7 @@ fn on_notification( | |||
375 | } | 387 | } |
376 | let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; | 388 | let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; |
377 | let not = RawNotification::new::<req::PublishDiagnostics>(¶ms); | 389 | let not = RawNotification::new::<req::PublishDiagnostics>(¶ms); |
378 | msg_sender.send(RawMessage::Notification(not)).unwrap(); | 390 | msg_sender.send(not.into()).unwrap(); |
379 | return Ok(()); | 391 | return Ok(()); |
380 | } | 392 | } |
381 | Err(not) => not, | 393 | Err(not) => not, |
@@ -496,12 +508,13 @@ fn update_file_notifications_on_threadpool( | |||
496 | }); | 508 | }); |
497 | } | 509 | } |
498 | 510 | ||
499 | fn feedback(intrnal_mode: bool, msg: &str, sender: &Sender<RawMessage>) { | 511 | fn show_message<M: Into<String>>(typ: req::MessageType, msg: M, sender: &Sender<RawMessage>) { |
500 | if !intrnal_mode { | 512 | let not = RawNotification::new::<req::ShowMessage>(&req::ShowMessageParams { |
501 | return; | 513 | typ, |
502 | } | 514 | message: msg.into(), |
503 | let not = RawNotification::new::<req::InternalFeedback>(&msg.to_string()); | 515 | }); |
504 | sender.send(RawMessage::Notification(not)).unwrap(); | 516 | |
517 | sender.send(not.into()).unwrap(); | ||
505 | } | 518 | } |
506 | 519 | ||
507 | fn is_canceled(e: &failure::Error) -> bool { | 520 | fn is_canceled(e: &failure::Error) -> bool { |
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 5c589f969..e0571fd78 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs | |||
@@ -8,7 +8,8 @@ pub use lsp_types::{ | |||
8 | CompletionParams, CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, | 8 | CompletionParams, CompletionResponse, DocumentOnTypeFormattingParams, DocumentSymbolParams, |
9 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, | 9 | DocumentSymbolResponse, ExecuteCommandParams, Hover, InitializeResult, |
10 | PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, | 10 | PublishDiagnosticsParams, ReferenceParams, SignatureHelp, TextDocumentEdit, |
11 | TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams | 11 | TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, |
12 | MessageType, ShowMessageParams, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | pub enum AnalyzerStatus {} | 15 | pub enum AnalyzerStatus {} |
@@ -171,10 +172,3 @@ pub struct SourceChange { | |||
171 | pub workspace_edit: WorkspaceEdit, | 172 | pub workspace_edit: WorkspaceEdit, |
172 | pub cursor_position: Option<TextDocumentPositionParams>, | 173 | pub cursor_position: Option<TextDocumentPositionParams>, |
173 | } | 174 | } |
174 | |||
175 | pub enum InternalFeedback {} | ||
176 | |||
177 | impl Notification for InternalFeedback { | ||
178 | const METHOD: &'static str = "internalFeedback"; | ||
179 | type Params = String; | ||
180 | } | ||