aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/init.rs39
-rw-r--r--crates/ra_lsp_server/src/lib.rs3
-rw-r--r--crates/ra_lsp_server/src/main.rs20
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs57
-rw-r--r--crates/ra_lsp_server/src/req.rs10
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs10
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs21
8 files changed, 105 insertions, 57 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index ef6dea393..a36e6b5ae 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -12,7 +12,7 @@ failure_derive = "0.1.4"
12serde_json = "1.0.34" 12serde_json = "1.0.34"
13serde = "1.0.83" 13serde = "1.0.83"
14crossbeam-channel = "0.3.5" 14crossbeam-channel = "0.3.5"
15flexi_logger = "0.10.0" 15flexi_logger = "0.11.0"
16log = "0.4.3" 16log = "0.4.3"
17url_serde = "0.2.0" 17url_serde = "0.2.0"
18lsp-types = "0.56.0" 18lsp-types = "0.56.0"
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 @@
1use serde::{Deserialize, Deserializer};
2
3/// Client provided initialization options
4#[derive(Deserialize, Clone, Copy, Debug)]
5#[serde(rename_all = "camelCase")]
6pub 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
22impl Default for InitializationOptions {
23 fn default() -> InitializationOptions {
24 InitializationOptions { publish_decorations: true, show_workspace_loaded: true }
25 }
26}
27
28fn bool_true() -> bool {
29 true
30}
31
32/// Deserializes a null value to a bool true by default
33fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error>
34where
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;
5mod markdown; 5mod markdown;
6mod project_model; 6mod project_model;
7pub mod req; 7pub mod req;
8pub mod init;
8mod server_world; 9mod server_world;
9 10
10pub type Result<T> = ::std::result::Result<T, ::failure::Error>; 11pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
11pub use crate::{caps::server_capabilities, main_loop::main_loop, main_loop::LspError}; 12pub 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;
2use flexi_logger::{Duplicate, Logger}; 2use flexi_logger::{Duplicate, Logger};
3use gen_lsp_server::{run_server, stdio_transport}; 3use gen_lsp_server::{run_server, stdio_transport};
4 4
5use ra_lsp_server::Result; 5use ra_lsp_server::{Result, InitializationOptions};
6 6
7fn main() -> Result<()> { 7fn 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")]
29struct 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
36fn main_inner() -> Result<()> { 27fn 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 {
46const THREADPOOL_SIZE: usize = 8; 47const THREADPOOL_SIZE: usize = 8;
47 48
48pub fn main_loop( 49pub 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
145fn main_loop_inner( 150fn 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>(&params); 389 let not = RawNotification::new::<req::PublishDiagnostics>(&params);
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
499fn feedback(intrnal_mode: bool, msg: &str, sender: &Sender<RawMessage>) { 511fn 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
507fn is_canceled(e: &failure::Error) -> bool { 520fn 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
14pub enum AnalyzerStatus {} 15pub 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
175pub enum InternalFeedback {}
176
177impl Notification for InternalFeedback {
178 const METHOD: &'static str = "internalFeedback";
179 type Params = String;
180}
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index 996bf8e01..1c099a78f 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -31,7 +31,7 @@ version = "0.0.0"
31use std::collections::Spam; 31use std::collections::Spam;
32"#, 32"#,
33 ); 33 );
34 server.wait_for_feedback("workspace loaded"); 34 server.wait_for_message("workspace loaded");
35 eprintln!("loading took {:?}", project_start.elapsed()); 35 eprintln!("loading took {:?}", project_start.elapsed());
36 let completion_start = Instant::now(); 36 let completion_start = Instant::now();
37 let res = server.send_request::<Completion>(CompletionParams { 37 let res = server.send_request::<Completion>(CompletionParams {
@@ -53,7 +53,7 @@ fn foo() {
53} 53}
54", 54",
55 ); 55 );
56 server.wait_for_feedback("workspace loaded"); 56 server.wait_for_message("workspace loaded");
57 server.request::<Runnables>( 57 server.request::<Runnables>(
58 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, 58 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
59 json!([ 59 json!([
@@ -107,7 +107,7 @@ pub fn foo() {}
107fn test_eggs() {} 107fn test_eggs() {}
108"#, 108"#,
109 ); 109 );
110 server.wait_for_feedback("workspace loaded"); 110 server.wait_for_message("workspace loaded");
111 server.request::<Runnables>( 111 server.request::<Runnables>(
112 RunnablesParams { 112 RunnablesParams {
113 text_document: server.doc_id("tests/spam.rs"), 113 text_document: server.doc_id("tests/spam.rs"),
@@ -167,7 +167,7 @@ fn main() {
167pub use std::collections::HashMap; 167pub use std::collections::HashMap;
168"#, 168"#,
169 ); 169 );
170 server.wait_for_feedback("workspace loaded"); 170 server.wait_for_message("workspace loaded");
171 171
172 server.request::<Formatting>( 172 server.request::<Formatting>(
173 DocumentFormattingParams { 173 DocumentFormattingParams {
@@ -216,7 +216,7 @@ mod bar;
216fn main() {} 216fn main() {}
217"#, 217"#,
218 ); 218 );
219 server.wait_for_feedback("workspace loaded"); 219 server.wait_for_message("workspace loaded");
220 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None }; 220 let empty_context = || CodeActionContext { diagnostics: Vec::new(), only: None };
221 server.request::<CodeActionRequest>( 221 server.request::<CodeActionRequest>(
222 CodeActionParams { 222 CodeActionParams {
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index f4e7eaf75..8bfc8d622 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -13,6 +13,7 @@ use lsp_types::{
13 notification::DidOpenTextDocument, 13 notification::DidOpenTextDocument,
14 request::{Request, Shutdown}, 14 request::{Request, Shutdown},
15 DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, 15 DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url,
16 notification::{Notification, ShowMessage},
16}; 17};
17use serde::Serialize; 18use serde::Serialize;
18use serde_json::{to_string_pretty, Value}; 19use serde_json::{to_string_pretty, Value};
@@ -22,6 +23,7 @@ use test_utils::{parse_fixture, find_mismatch};
22 23
23use ra_lsp_server::{ 24use ra_lsp_server::{
24 main_loop, req, 25 main_loop, req,
26 InitializationOptions,
25}; 27};
26 28
27pub fn project(fixture: &str) -> Server { 29pub fn project(fixture: &str) -> Server {
@@ -56,7 +58,13 @@ impl Server {
56 "test server", 58 "test server",
57 128, 59 128,
58 move |mut msg_receiver, mut msg_sender| { 60 move |mut msg_receiver, mut msg_sender| {
59 main_loop(true, path, true, &mut msg_receiver, &mut msg_sender).unwrap() 61 main_loop(
62 path,
63 InitializationOptions::default(),
64 &mut msg_receiver,
65 &mut msg_sender,
66 )
67 .unwrap()
60 }, 68 },
61 ); 69 );
62 let res = Server { 70 let res = Server {
@@ -133,13 +141,14 @@ impl Server {
133 } 141 }
134 panic!("no response"); 142 panic!("no response");
135 } 143 }
136 pub fn wait_for_feedback(&self, feedback: &str) { 144 pub fn wait_for_message(&self, message: &str) {
137 self.wait_for_feedback_n(feedback, 1) 145 self.wait_for_message_n(message, 1)
138 } 146 }
139 pub fn wait_for_feedback_n(&self, feedback: &str, n: usize) { 147 pub fn wait_for_message_n(&self, message: &str, n: usize) {
140 let f = |msg: &RawMessage| match msg { 148 let f = |msg: &RawMessage| match msg {
141 RawMessage::Notification(n) if n.method == "internalFeedback" => { 149 RawMessage::Notification(n) if n.method == ShowMessage::METHOD => {
142 return n.clone().cast::<req::InternalFeedback>().unwrap() == feedback; 150 let msg = n.clone().cast::<req::ShowMessage>().unwrap();
151 msg.message == message
143 } 152 }
144 _ => false, 153 _ => false,
145 }; 154 };