aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server
diff options
context:
space:
mode:
authorSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
committerSeivan Heidari <[email protected]>2019-12-23 14:35:31 +0000
commitb21d9337d9200e2cfdc90b386591c72c302dc03e (patch)
treef81f5c08f821115cee26fa4d3ceaae88c7807fd5 /crates/ra_lsp_server
parent18a0937585b836ec5ed054b9ae48e0156ab6d9ef (diff)
parentce07a2daa9e53aa86a769f8641b14c2878444fbc (diff)
Merge branch 'master' into feature/themes
Diffstat (limited to 'crates/ra_lsp_server')
-rw-r--r--crates/ra_lsp_server/Cargo.toml4
-rw-r--r--crates/ra_lsp_server/build.rs15
-rw-r--r--crates/ra_lsp_server/src/caps.rs20
-rw-r--r--crates/ra_lsp_server/src/config.rs5
-rw-r--r--crates/ra_lsp_server/src/conv.rs7
-rw-r--r--crates/ra_lsp_server/src/main.rs25
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs19
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs18
-rw-r--r--crates/ra_lsp_server/src/main_loop/pending_requests.rs2
-rw-r--r--crates/ra_lsp_server/src/main_loop/subscriptions.rs2
-rw-r--r--crates/ra_lsp_server/src/markdown.rs2
-rw-r--r--crates/ra_lsp_server/src/req.rs29
-rw-r--r--crates/ra_lsp_server/src/world.rs75
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs118
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/support.rs3
15 files changed, 277 insertions, 67 deletions
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 21aef842c..60cbc38a9 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -13,9 +13,8 @@ relative-path = "1.0.0"
13serde_json = "1.0.34" 13serde_json = "1.0.34"
14serde = { version = "1.0.83", features = ["derive"] } 14serde = { version = "1.0.83", features = ["derive"] }
15crossbeam-channel = "0.4" 15crossbeam-channel = "0.4"
16flexi_logger = "0.14.0"
17log = "0.4.3" 16log = "0.4.3"
18lsp-types = { version = "0.61.0", features = ["proposed"] } 17lsp-types = { version = "0.66.0", features = ["proposed"] }
19rustc-hash = "1.0" 18rustc-hash = "1.0"
20parking_lot = "0.10.0" 19parking_lot = "0.10.0"
21jod-thread = "0.1.0" 20jod-thread = "0.1.0"
@@ -27,6 +26,7 @@ lsp-server = "0.3.0"
27ra_project_model = { path = "../ra_project_model" } 26ra_project_model = { path = "../ra_project_model" }
28ra_prof = { path = "../ra_prof" } 27ra_prof = { path = "../ra_prof" }
29ra_vfs_glob = { path = "../ra_vfs_glob" } 28ra_vfs_glob = { path = "../ra_vfs_glob" }
29env_logger = { version = "0.7.1", default-features = false, features = ["humantime"] }
30 30
31[dev-dependencies] 31[dev-dependencies]
32tempfile = "3" 32tempfile = "3"
diff --git a/crates/ra_lsp_server/build.rs b/crates/ra_lsp_server/build.rs
new file mode 100644
index 000000000..05f9772c0
--- /dev/null
+++ b/crates/ra_lsp_server/build.rs
@@ -0,0 +1,15 @@
1//! Just embed git-hash to `--version`
2
3use std::process::Command;
4
5fn main() {
6 let rev = rev().unwrap_or_else(|| "???????".to_string());
7 println!("cargo:rustc-env=REV={}", rev)
8}
9
10fn rev() -> Option<String> {
11 let output = Command::new("git").args(&["rev-parse", "HEAD"]).output().ok()?;
12 let stdout = String::from_utf8(output.stdout).ok()?;
13 let short_hash = stdout.get(0..7)?;
14 Some(short_hash.to_owned())
15}
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index eea0965ed..eeca67ee1 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -1,11 +1,12 @@
1//! FIXME: write short doc here 1//! Advertizes the capabilities of the LSP Server.
2 2
3use lsp_types::{ 3use lsp_types::{
4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions, 4 CodeActionProviderCapability, CodeLensOptions, CompletionOptions,
5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability, GenericCapability, 5 DocumentOnTypeFormattingOptions, FoldingRangeProviderCapability,
6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability, ServerCapabilities, 6 ImplementationProviderCapability, RenameOptions, RenameProviderCapability,
7 SignatureHelpOptions, TextDocumentSyncCapability, TextDocumentSyncKind, 7 SelectionRangeProviderCapability, ServerCapabilities, SignatureHelpOptions,
8 TextDocumentSyncOptions, TypeDefinitionProviderCapability, 8 TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
9 TypeDefinitionProviderCapability, WorkDoneProgressOptions,
9}; 10};
10 11
11pub fn server_capabilities() -> ServerCapabilities { 12pub fn server_capabilities() -> ServerCapabilities {
@@ -21,10 +22,14 @@ pub fn server_capabilities() -> ServerCapabilities {
21 completion_provider: Some(CompletionOptions { 22 completion_provider: Some(CompletionOptions {
22 resolve_provider: None, 23 resolve_provider: None,
23 trigger_characters: Some(vec![":".to_string(), ".".to_string()]), 24 trigger_characters: Some(vec![":".to_string(), ".".to_string()]),
25 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
24 }), 26 }),
25 signature_help_provider: Some(SignatureHelpOptions { 27 signature_help_provider: Some(SignatureHelpOptions {
26 trigger_characters: Some(vec!["(".to_string(), ",".to_string(), ")".to_string()]), 28 trigger_characters: Some(vec!["(".to_string(), ",".to_string()]),
29 retrigger_characters: None,
30 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
27 }), 31 }),
32 declaration_provider: None,
28 definition_provider: Some(true), 33 definition_provider: Some(true),
29 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)), 34 type_definition_provider: Some(TypeDefinitionProviderCapability::Simple(true)),
30 implementation_provider: Some(ImplementationProviderCapability::Simple(true)), 35 implementation_provider: Some(ImplementationProviderCapability::Simple(true)),
@@ -40,10 +45,11 @@ pub fn server_capabilities() -> ServerCapabilities {
40 first_trigger_character: "=".to_string(), 45 first_trigger_character: "=".to_string(),
41 more_trigger_character: Some(vec![".".to_string(), ">".to_string()]), 46 more_trigger_character: Some(vec![".".to_string(), ">".to_string()]),
42 }), 47 }),
43 selection_range_provider: Some(GenericCapability::default()), 48 selection_range_provider: Some(SelectionRangeProviderCapability::Simple(true)),
44 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)), 49 folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
45 rename_provider: Some(RenameProviderCapability::Options(RenameOptions { 50 rename_provider: Some(RenameProviderCapability::Options(RenameOptions {
46 prepare_provider: Some(true), 51 prepare_provider: Some(true),
52 work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
47 })), 53 })),
48 document_link_provider: None, 54 document_link_provider: None,
49 color_provider: None, 55 color_provider: None,
diff --git a/crates/ra_lsp_server/src/config.rs b/crates/ra_lsp_server/src/config.rs
index 8045f3d60..67942aa41 100644
--- a/crates/ra_lsp_server/src/config.rs
+++ b/crates/ra_lsp_server/src/config.rs
@@ -9,6 +9,7 @@
9 9
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11 11
12use ra_project_model::CargoFeatures;
12use serde::{Deserialize, Deserializer}; 13use serde::{Deserialize, Deserializer};
13 14
14/// Client provided initialization options 15/// Client provided initialization options
@@ -37,6 +38,9 @@ pub struct ServerConfig {
37 38
38 /// Fine grained feature flags to disable specific features. 39 /// Fine grained feature flags to disable specific features.
39 pub feature_flags: FxHashMap<String, bool>, 40 pub feature_flags: FxHashMap<String, bool>,
41
42 /// Cargo feature configurations.
43 pub cargo_features: CargoFeatures,
40} 44}
41 45
42impl Default for ServerConfig { 46impl Default for ServerConfig {
@@ -49,6 +53,7 @@ impl Default for ServerConfig {
49 max_inlay_hint_length: None, 53 max_inlay_hint_length: None,
50 with_sysroot: true, 54 with_sysroot: true,
51 feature_flags: FxHashMap::default(), 55 feature_flags: FxHashMap::default(),
56 cargo_features: Default::default(),
52 } 57 }
53 } 58 }
54} 59}
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index b13093cfe..e93d4ea33 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Convenience module responsible for translating between rust-analyzer's types and LSP types.
2 2
3use lsp_types::{ 3use lsp_types::{
4 self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation, 4 self, CreateFile, DiagnosticSeverity, DocumentChangeOperation, DocumentChanges, Documentation,
@@ -130,6 +130,11 @@ impl ConvWith<(&LineIndex, LineEndings)> for CompletionItem {
130 deprecated: Some(self.deprecated()), 130 deprecated: Some(self.deprecated()),
131 ..Default::default() 131 ..Default::default()
132 }; 132 };
133
134 if self.deprecated() {
135 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
136 }
137
133 res.insert_text_format = Some(match self.insert_text_format() { 138 res.insert_text_format = Some(match self.insert_text_format() {
134 InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet, 139 InsertTextFormat::Snippet => lsp_types::InsertTextFormat::Snippet,
135 InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText, 140 InsertTextFormat::PlainText => lsp_types::InsertTextFormat::PlainText,
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs
index e13c8ca14..cdd925c9f 100644
--- a/crates/ra_lsp_server/src/main.rs
+++ b/crates/ra_lsp_server/src/main.rs
@@ -1,24 +1,22 @@
1//! `ra_lsp_server` binary 1//! `ra_lsp_server` binary
2 2
3use flexi_logger::{Duplicate, Logger};
4use lsp_server::Connection; 3use lsp_server::Connection;
5use ra_lsp_server::{show_message, Result, ServerConfig}; 4use ra_lsp_server::{show_message, Result, ServerConfig};
6use ra_prof; 5use ra_prof;
7 6
8fn main() -> Result<()> { 7fn main() -> Result<()> {
9 setup_logging()?; 8 setup_logging()?;
10 run_server()?; 9 match Args::parse()? {
10 Args::Version => println!("rust-analyzer {}", env!("REV")),
11 Args::Run => run_server()?,
12 }
11 Ok(()) 13 Ok(())
12} 14}
13 15
14fn setup_logging() -> Result<()> { 16fn setup_logging() -> Result<()> {
15 std::env::set_var("RUST_BACKTRACE", "short"); 17 std::env::set_var("RUST_BACKTRACE", "short");
16 18
17 let logger = Logger::with_env_or_str("error").duplicate_to_stderr(Duplicate::All); 19 env_logger::try_init()?;
18 match std::env::var("RA_LOG_DIR") {
19 Ok(ref v) if v == "1" => logger.log_to_file().directory("log").start()?,
20 _ => logger.start()?,
21 };
22 20
23 ra_prof::set_filter(match std::env::var("RA_PROFILE") { 21 ra_prof::set_filter(match std::env::var("RA_PROFILE") {
24 Ok(spec) => ra_prof::Filter::from_spec(&spec), 22 Ok(spec) => ra_prof::Filter::from_spec(&spec),
@@ -27,6 +25,19 @@ fn setup_logging() -> Result<()> {
27 Ok(()) 25 Ok(())
28} 26}
29 27
28enum Args {
29 Version,
30 Run,
31}
32
33impl Args {
34 fn parse() -> Result<Args> {
35 let res =
36 if std::env::args().any(|it| it == "--version") { Args::Version } else { Args::Run };
37 Ok(res)
38 }
39}
40
30fn run_server() -> Result<()> { 41fn run_server() -> Result<()> {
31 log::info!("lifecycle: server started"); 42 log::info!("lifecycle: server started");
32 43
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 83845f1e0..dda318e43 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -1,4 +1,5 @@
1//! FIXME: write short doc here 1//! The main loop of `ra_lsp_server` responsible for dispatching LSP requests/replies and
2//! notifications back to the client.
2 3
3mod handlers; 4mod handlers;
4mod subscriptions; 5mod subscriptions;
@@ -67,6 +68,7 @@ pub fn main_loop(
67 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( 68 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot(
68 ws_root.as_path(), 69 ws_root.as_path(),
69 config.with_sysroot, 70 config.with_sysroot,
71 &config.cargo_features,
70 ); 72 );
71 match workspace { 73 match workspace {
72 Ok(workspace) => loaded_workspaces.push(workspace), 74 Ok(workspace) => loaded_workspaces.push(workspace),
@@ -130,7 +132,7 @@ pub fn main_loop(
130 let feature_flags = { 132 let feature_flags = {
131 let mut ff = FeatureFlags::default(); 133 let mut ff = FeatureFlags::default();
132 for (flag, value) in config.feature_flags { 134 for (flag, value) in config.feature_flags {
133 if let Err(_) = ff.set(flag.as_str(), value) { 135 if ff.set(flag.as_str(), value).is_err() {
134 log::error!("unknown feature flag: {:?}", flag); 136 log::error!("unknown feature flag: {:?}", flag);
135 show_message( 137 show_message(
136 req::MessageType::Error, 138 req::MessageType::Error,
@@ -303,7 +305,6 @@ fn loop_turn(
303 log::info!("queued count = {}", queue_count); 305 log::info!("queued count = {}", queue_count);
304 } 306 }
305 307
306 let mut state_changed = false;
307 match event { 308 match event {
308 Event::Task(task) => { 309 Event::Task(task) => {
309 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state); 310 on_task(task, &connection.sender, &mut loop_state.pending_requests, world_state);
@@ -311,7 +312,6 @@ fn loop_turn(
311 } 312 }
312 Event::Vfs(task) => { 313 Event::Vfs(task) => {
313 world_state.vfs.write().handle_task(task); 314 world_state.vfs.write().handle_task(task);
314 state_changed = true;
315 } 315 }
316 Event::Lib(lib) => { 316 Event::Lib(lib) => {
317 world_state.add_lib(lib); 317 world_state.add_lib(lib);
@@ -336,7 +336,6 @@ fn loop_turn(
336 &mut loop_state.subscriptions, 336 &mut loop_state.subscriptions,
337 not, 337 not,
338 )?; 338 )?;
339 state_changed = true;
340 } 339 }
341 Message::Response(resp) => { 340 Message::Response(resp) => {
342 let removed = loop_state.pending_responses.remove(&resp.id); 341 let removed = loop_state.pending_responses.remove(&resp.id);
@@ -347,7 +346,12 @@ fn loop_turn(
347 }, 346 },
348 }; 347 };
349 348
350 loop_state.pending_libraries.extend(world_state.process_changes()); 349 let mut state_changed = false;
350 if let Some(changes) = world_state.process_changes() {
351 state_changed = true;
352 loop_state.pending_libraries.extend(changes);
353 }
354
351 while loop_state.in_flight_libraries < MAX_IN_FLIGHT_LIBS 355 while loop_state.in_flight_libraries < MAX_IN_FLIGHT_LIBS
352 && !loop_state.pending_libraries.is_empty() 356 && !loop_state.pending_libraries.is_empty()
353 { 357 {
@@ -520,7 +524,8 @@ fn on_notification(
520 if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) { 524 if let Some(file_id) = state.vfs.write().remove_file_overlay(path.as_path()) {
521 subs.remove_sub(FileId(file_id.0)); 525 subs.remove_sub(FileId(file_id.0));
522 } 526 }
523 let params = req::PublishDiagnosticsParams { uri, diagnostics: Vec::new() }; 527 let params =
528 req::PublishDiagnosticsParams { uri, diagnostics: Vec::new(), version: None };
524 let not = notification_new::<req::PublishDiagnostics>(params); 529 let not = notification_new::<req::PublishDiagnostics>(params);
525 msg_sender.send(not.into()).unwrap(); 530 msg_sender.send(not.into()).unwrap();
526 return Ok(()); 531 return Ok(());
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index c81fa7f67..39eb3df3e 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -1,4 +1,5 @@
1//! FIXME: write short doc here 1//! This module is responsible for implementing handlers for Lanuage Server Protocol.
2//! The majority of requests are fulfilled by calling into the `ra_ide` crate.
2 3
3use std::{fmt::Write as _, io::Write as _}; 4use std::{fmt::Write as _, io::Write as _};
4 5
@@ -164,7 +165,7 @@ pub fn handle_on_type_formatting(
164 165
165 // in `ra_ide`, the `on_type` invariant is that 166 // in `ra_ide`, the `on_type` invariant is that
166 // `text.char_at(position) == typed_char`. 167 // `text.char_at(position) == typed_char`.
167 position.offset = position.offset - TextUnit::of_char('.'); 168 position.offset -= TextUnit::of_char('.');
168 let char_typed = params.ch.chars().next().unwrap_or('\0'); 169 let char_typed = params.ch.chars().next().unwrap_or('\0');
169 170
170 // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`, 171 // We have an assist that inserts ` ` after typing `->` in `fn foo() ->{`,
@@ -480,8 +481,6 @@ pub fn handle_prepare_rename(
480 let _p = profile("handle_prepare_rename"); 481 let _p = profile("handle_prepare_rename");
481 let position = params.try_conv_with(&world)?; 482 let position = params.try_conv_with(&world)?;
482 483
483 // We support renaming references like handle_rename does.
484 // In the future we may want to reject the renaming of things like keywords here too.
485 let optional_change = world.analysis().rename(position, "dummy")?; 484 let optional_change = world.analysis().rename(position, "dummy")?;
486 let range = match optional_change { 485 let range = match optional_change {
487 None => return Ok(None), 486 None => return Ok(None),
@@ -557,12 +556,18 @@ pub fn handle_formatting(
557 let _p = profile("handle_formatting"); 556 let _p = profile("handle_formatting");
558 let file_id = params.text_document.try_conv_with(&world)?; 557 let file_id = params.text_document.try_conv_with(&world)?;
559 let file = world.analysis().file_text(file_id)?; 558 let file = world.analysis().file_text(file_id)?;
559 let crate_ids = world.analysis().crate_for(file_id)?;
560 560
561 let file_line_index = world.analysis().file_line_index(file_id)?; 561 let file_line_index = world.analysis().file_line_index(file_id)?;
562 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); 562 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
563 563
564 use std::process; 564 use std::process;
565 let mut rustfmt = process::Command::new("rustfmt"); 565 let mut rustfmt = process::Command::new("rustfmt");
566 if let Some(&crate_id) = crate_ids.first() {
567 // Assume all crates are in the same edition
568 let edition = world.analysis().crate_edition(crate_id)?;
569 rustfmt.args(&["--edition", &edition.to_string()]);
570 }
566 rustfmt.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()); 571 rustfmt.stdin(process::Stdio::piped()).stdout(process::Stdio::piped());
567 572
568 if let Ok(path) = params.text_document.uri.to_file_path() { 573 if let Ok(path) = params.text_document.uri.to_file_path() {
@@ -644,6 +649,7 @@ pub fn handle_code_action(
644 diagnostics: None, 649 diagnostics: None,
645 edit: None, 650 edit: None,
646 command: Some(command), 651 command: Some(command),
652 is_preferred: None,
647 }; 653 };
648 res.push(action.into()); 654 res.push(action.into());
649 } 655 }
@@ -666,6 +672,7 @@ pub fn handle_code_action(
666 diagnostics: None, 672 diagnostics: None,
667 edit: None, 673 edit: None,
668 command: Some(command), 674 command: Some(command),
675 is_preferred: None,
669 }; 676 };
670 res.push(action.into()); 677 res.push(action.into());
671 } 678 }
@@ -824,9 +831,10 @@ pub fn publish_diagnostics(
824 source: Some("rust-analyzer".to_string()), 831 source: Some("rust-analyzer".to_string()),
825 message: d.message, 832 message: d.message,
826 related_information: None, 833 related_information: None,
834 tags: None,
827 }) 835 })
828 .collect(); 836 .collect();
829 Ok(req::PublishDiagnosticsParams { uri, diagnostics }) 837 Ok(req::PublishDiagnosticsParams { uri, diagnostics, version: None })
830} 838}
831 839
832pub fn publish_decorations( 840pub fn publish_decorations(
diff --git a/crates/ra_lsp_server/src/main_loop/pending_requests.rs b/crates/ra_lsp_server/src/main_loop/pending_requests.rs
index e7ea7aa5b..2d2213464 100644
--- a/crates/ra_lsp_server/src/main_loop/pending_requests.rs
+++ b/crates/ra_lsp_server/src/main_loop/pending_requests.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Datastructures that keep track of inflight requests.
2 2
3use std::time::{Duration, Instant}; 3use std::time::{Duration, Instant};
4 4
diff --git a/crates/ra_lsp_server/src/main_loop/subscriptions.rs b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
index 609b2adcc..b0bae90f5 100644
--- a/crates/ra_lsp_server/src/main_loop/subscriptions.rs
+++ b/crates/ra_lsp_server/src/main_loop/subscriptions.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Keeps track of file subscriptions.
2 2
3use ra_ide::FileId; 3use ra_ide::FileId;
4use rustc_hash::FxHashSet; 4use rustc_hash::FxHashSet;
diff --git a/crates/ra_lsp_server/src/markdown.rs b/crates/ra_lsp_server/src/markdown.rs
index f51fc4ade..76bef45cc 100644
--- a/crates/ra_lsp_server/src/markdown.rs
+++ b/crates/ra_lsp_server/src/markdown.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Transforms markdown
2 2
3pub(crate) fn format_docs(src: &str) -> String { 3pub(crate) fn format_docs(src: &str) -> String {
4 let mut processed_lines = Vec::new(); 4 let mut processed_lines = Vec::new();
diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs
index 39361b7e8..b34e6f9b8 100644
--- a/crates/ra_lsp_server/src/req.rs
+++ b/crates/ra_lsp_server/src/req.rs
@@ -1,4 +1,4 @@
1//! FIXME: write short doc here 1//! Defines `rust-analyzer` specific custom messages.
2 2
3use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url}; 3use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url};
4use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
@@ -10,8 +10,9 @@ pub use lsp_types::{
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, PublishDiagnosticsParams,
13 ReferenceParams, Registration, RegistrationParams, ShowMessageParams, SignatureHelp, 13 ReferenceParams, Registration, RegistrationParams, SelectionRange, SelectionRangeParams,
14 TextDocumentEdit, TextDocumentPositionParams, TextEdit, WorkspaceEdit, WorkspaceSymbolParams, 14 ShowMessageParams, SignatureHelp, TextDocumentEdit, TextDocumentPositionParams, TextEdit,
15 WorkspaceEdit, WorkspaceSymbolParams,
15}; 16};
16 17
17pub enum AnalyzerStatus {} 18pub enum AnalyzerStatus {}
@@ -67,28 +68,6 @@ pub struct ExpandMacroParams {
67 pub position: Option<Position>, 68 pub position: Option<Position>,
68} 69}
69 70
70pub enum SelectionRangeRequest {}
71
72impl Request for SelectionRangeRequest {
73 type Params = SelectionRangeParams;
74 type Result = Vec<SelectionRange>;
75 const METHOD: &'static str = "textDocument/selectionRange";
76}
77
78#[derive(Deserialize, Debug)]
79#[serde(rename_all = "camelCase")]
80pub struct SelectionRangeParams {
81 pub text_document: TextDocumentIdentifier,
82 pub positions: Vec<Position>,
83}
84
85#[derive(Serialize, Debug)]
86#[serde(rename_all = "camelCase")]
87pub struct SelectionRange {
88 pub range: Range,
89 pub parent: Option<Box<SelectionRange>>,
90}
91
92pub enum FindMatchingBrace {} 71pub enum FindMatchingBrace {}
93 72
94impl Request for FindMatchingBrace { 73impl Request for FindMatchingBrace {
diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs
index 927449b45..79431e7e6 100644
--- a/crates/ra_lsp_server/src/world.rs
+++ b/crates/ra_lsp_server/src/world.rs
@@ -1,4 +1,7 @@
1//! FIXME: write short doc here 1//! The context or environment in which the language server functions.
2//! In our server implementation this is know as the `WorldState`.
3//!
4//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`.
2 5
3use std::{ 6use std::{
4 path::{Path, PathBuf}, 7 path::{Path, PathBuf},
@@ -17,11 +20,13 @@ use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace};
17use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; 20use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
18use ra_vfs_glob::{Glob, RustPackageFilterBuilder}; 21use ra_vfs_glob::{Glob, RustPackageFilterBuilder};
19use relative_path::RelativePathBuf; 22use relative_path::RelativePathBuf;
23use std::path::{Component, Prefix};
20 24
21use crate::{ 25use crate::{
22 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 26 main_loop::pending_requests::{CompletedRequest, LatestRequests},
23 LspError, Result, 27 LspError, Result,
24}; 28};
29use std::str::FromStr;
25 30
26#[derive(Debug, Clone)] 31#[derive(Debug, Clone)]
27pub struct Options { 32pub struct Options {
@@ -140,10 +145,10 @@ impl WorldState {
140 /// FIXME: better API here 145 /// FIXME: better API here
141 pub fn process_changes( 146 pub fn process_changes(
142 &mut self, 147 &mut self,
143 ) -> Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)> { 148 ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
144 let changes = self.vfs.write().commit_changes(); 149 let changes = self.vfs.write().commit_changes();
145 if changes.is_empty() { 150 if changes.is_empty() {
146 return Vec::new(); 151 return None;
147 } 152 }
148 let mut libs = Vec::new(); 153 let mut libs = Vec::new();
149 let mut change = AnalysisChange::new(); 154 let mut change = AnalysisChange::new();
@@ -177,7 +182,7 @@ impl WorldState {
177 } 182 }
178 } 183 }
179 self.analysis_host.apply_change(change); 184 self.analysis_host.apply_change(change);
180 libs 185 Some(libs)
181 } 186 }
182 187
183 pub fn add_lib(&mut self, data: LibraryData) { 188 pub fn add_lib(&mut self, data: LibraryData) {
@@ -233,8 +238,8 @@ impl WorldSnapshot {
233 238
234 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { 239 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> {
235 let path = self.vfs.read().file2path(VfsFile(id.0)); 240 let path = self.vfs.read().file2path(VfsFile(id.0));
236 let url = Url::from_file_path(&path) 241 let url = url_from_path_with_drive_lowercasing(path)?;
237 .map_err(|_| format!("can't convert path to url: {}", path.display()))?; 242
238 Ok(url) 243 Ok(url)
239 } 244 }
240 245
@@ -279,3 +284,61 @@ impl WorldSnapshot {
279 self.analysis.feature_flags() 284 self.analysis.feature_flags()
280 } 285 }
281} 286}
287
288/// Returns a `Url` object from a given path, will lowercase drive letters if present.
289/// This will only happen when processing windows paths.
290///
291/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
292fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
293 let component_has_windows_drive = path.as_ref().components().any(|comp| {
294 if let Component::Prefix(c) = comp {
295 match c.kind() {
296 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
297 _ => return false,
298 }
299 }
300 false
301 });
302
303 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
304 if component_has_windows_drive {
305 let url_original = Url::from_file_path(&path)
306 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
307
308 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
309
310 // There is a drive partition, but we never found a colon.
311 // This should not happen, but in this case we just pass it through.
312 if drive_partition.len() == 1 {
313 return Ok(url_original);
314 }
315
316 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
317 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
318
319 Ok(url)
320 } else {
321 Ok(Url::from_file_path(&path)
322 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
323 }
324}
325
326// `Url` is not able to parse windows paths on unix machines.
327#[cfg(target_os = "windows")]
328#[cfg(test)]
329mod path_conversion_windows_tests {
330 use super::url_from_path_with_drive_lowercasing;
331 #[test]
332 fn test_lowercase_drive_letter_with_drive() {
333 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
334
335 assert_eq!(url.to_string(), "file:///c:/Test");
336 }
337
338 #[test]
339 fn test_drive_without_colon_passthrough() {
340 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
341
342 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
343 }
344}
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index 2ba82ab05..dff63a12d 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -4,7 +4,8 @@ use std::{collections::HashMap, time::Instant};
4 4
5use lsp_types::{ 5use lsp_types::{
6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions, 6 CodeActionContext, DidOpenTextDocumentParams, DocumentFormattingParams, FormattingOptions,
7 Position, Range, TextDocumentItem, TextDocumentPositionParams, 7 PartialResultParams, Position, Range, TextDocumentItem, TextDocumentPositionParams,
8 WorkDoneProgressParams,
8}; 9};
9use ra_lsp_server::req::{ 10use ra_lsp_server::req::{
10 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, 11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
@@ -12,15 +13,19 @@ use ra_lsp_server::req::{
12}; 13};
13use serde_json::json; 14use serde_json::json;
14use tempfile::TempDir; 15use tempfile::TempDir;
16use test_utils::skip_slow_tests;
15 17
16use crate::support::{project, Project}; 18use crate::support::{project, Project};
17 19
18const LOG: &'static str = "";
19const PROFILE: &'static str = ""; 20const PROFILE: &'static str = "";
20// const PROFILE: &'static str = "*@3>100"; 21// const PROFILE: &'static str = "*@3>100";
21 22
22#[test] 23#[test]
23fn completes_items_from_standard_library() { 24fn completes_items_from_standard_library() {
25 if skip_slow_tests() {
26 return;
27 }
28
24 let project_start = Instant::now(); 29 let project_start = Instant::now();
25 let server = Project::with_fixture( 30 let server = Project::with_fixture(
26 r#" 31 r#"
@@ -44,6 +49,8 @@ use std::collections::Spam;
44 Position::new(0, 23), 49 Position::new(0, 23),
45 ), 50 ),
46 context: None, 51 context: None,
52 partial_result_params: PartialResultParams::default(),
53 work_done_progress_params: WorkDoneProgressParams::default(),
47 }); 54 });
48 assert!(format!("{}", res).contains("HashMap")); 55 assert!(format!("{}", res).contains("HashMap"));
49 eprintln!("completion took {:?}", completion_start.elapsed()); 56 eprintln!("completion took {:?}", completion_start.elapsed());
@@ -51,6 +58,10 @@ use std::collections::Spam;
51 58
52#[test] 59#[test]
53fn test_runnables_no_project() { 60fn test_runnables_no_project() {
61 if skip_slow_tests() {
62 return;
63 }
64
54 let server = project( 65 let server = project(
55 r" 66 r"
56//- lib.rs 67//- lib.rs
@@ -100,6 +111,10 @@ fn foo() {
100 111
101#[test] 112#[test]
102fn test_runnables_project() { 113fn test_runnables_project() {
114 if skip_slow_tests() {
115 return;
116 }
117
103 let code = r#" 118 let code = r#"
104//- foo/Cargo.toml 119//- foo/Cargo.toml
105[package] 120[package]
@@ -171,8 +186,13 @@ fn main() {}
171 186
172#[test] 187#[test]
173fn test_format_document() { 188fn test_format_document() {
189 if skip_slow_tests() {
190 return;
191 }
192
174 let server = project( 193 let server = project(
175 r#" 194 r#"
195//- Cargo.toml
176[package] 196[package]
177name = "foo" 197name = "foo"
178version = "0.0.0" 198version = "0.0.0"
@@ -194,8 +214,12 @@ pub use std::collections::HashMap;
194 options: FormattingOptions { 214 options: FormattingOptions {
195 tab_size: 4, 215 tab_size: 4,
196 insert_spaces: false, 216 insert_spaces: false,
217 insert_final_newline: None,
218 trim_final_newlines: None,
219 trim_trailing_whitespace: None,
197 properties: HashMap::new(), 220 properties: HashMap::new(),
198 }, 221 },
222 work_done_progress_params: WorkDoneProgressParams::default(),
199 }, 223 },
200 json!([ 224 json!([
201 { 225 {
@@ -221,7 +245,77 @@ pub use std::collections::HashMap;
221} 245}
222 246
223#[test] 247#[test]
248fn test_format_document_2018() {
249 if skip_slow_tests() {
250 return;
251 }
252
253 let server = project(
254 r#"
255//- Cargo.toml
256[package]
257name = "foo"
258version = "0.0.0"
259edition = "2018"
260
261//- src/lib.rs
262mod bar;
263
264async fn test() {
265}
266
267fn main() {
268}
269
270pub use std::collections::HashMap;
271"#,
272 );
273 server.wait_until_workspace_is_loaded();
274
275 server.request::<Formatting>(
276 DocumentFormattingParams {
277 text_document: server.doc_id("src/lib.rs"),
278 options: FormattingOptions {
279 tab_size: 4,
280 insert_spaces: false,
281 properties: HashMap::new(),
282 insert_final_newline: None,
283 trim_final_newlines: None,
284 trim_trailing_whitespace: None,
285 },
286 work_done_progress_params: WorkDoneProgressParams::default(),
287 },
288 json!([
289 {
290 "newText": r#"mod bar;
291
292async fn test() {}
293
294fn main() {}
295
296pub use std::collections::HashMap;
297"#,
298 "range": {
299 "end": {
300 "character": 0,
301 "line": 10
302 },
303 "start": {
304 "character": 0,
305 "line": 0
306 }
307 }
308 }
309 ]),
310 );
311}
312
313#[test]
224fn test_missing_module_code_action() { 314fn test_missing_module_code_action() {
315 if skip_slow_tests() {
316 return;
317 }
318
225 let server = project( 319 let server = project(
226 r#" 320 r#"
227//- Cargo.toml 321//- Cargo.toml
@@ -242,6 +336,8 @@ fn main() {}
242 text_document: server.doc_id("src/lib.rs"), 336 text_document: server.doc_id("src/lib.rs"),
243 range: Range::new(Position::new(0, 4), Position::new(0, 7)), 337 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
244 context: empty_context(), 338 context: empty_context(),
339 partial_result_params: PartialResultParams::default(),
340 work_done_progress_params: WorkDoneProgressParams::default(),
245 }, 341 },
246 json!([ 342 json!([
247 { 343 {
@@ -273,6 +369,8 @@ fn main() {}
273 text_document: server.doc_id("src/lib.rs"), 369 text_document: server.doc_id("src/lib.rs"),
274 range: Range::new(Position::new(2, 4), Position::new(2, 7)), 370 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
275 context: empty_context(), 371 context: empty_context(),
372 partial_result_params: PartialResultParams::default(),
373 work_done_progress_params: WorkDoneProgressParams::default(),
276 }, 374 },
277 json!([]), 375 json!([]),
278 ); 376 );
@@ -280,6 +378,10 @@ fn main() {}
280 378
281#[test] 379#[test]
282fn test_missing_module_code_action_in_json_project() { 380fn test_missing_module_code_action_in_json_project() {
381 if skip_slow_tests() {
382 return;
383 }
384
283 let tmp_dir = TempDir::new().unwrap(); 385 let tmp_dir = TempDir::new().unwrap();
284 386
285 let path = tmp_dir.path(); 387 let path = tmp_dir.path();
@@ -317,6 +419,8 @@ fn main() {{}}
317 text_document: server.doc_id("src/lib.rs"), 419 text_document: server.doc_id("src/lib.rs"),
318 range: Range::new(Position::new(0, 4), Position::new(0, 7)), 420 range: Range::new(Position::new(0, 4), Position::new(0, 7)),
319 context: empty_context(), 421 context: empty_context(),
422 partial_result_params: PartialResultParams::default(),
423 work_done_progress_params: WorkDoneProgressParams::default(),
320 }, 424 },
321 json!([ 425 json!([
322 { 426 {
@@ -348,6 +452,8 @@ fn main() {{}}
348 text_document: server.doc_id("src/lib.rs"), 452 text_document: server.doc_id("src/lib.rs"),
349 range: Range::new(Position::new(2, 4), Position::new(2, 7)), 453 range: Range::new(Position::new(2, 4), Position::new(2, 7)),
350 context: empty_context(), 454 context: empty_context(),
455 partial_result_params: PartialResultParams::default(),
456 work_done_progress_params: WorkDoneProgressParams::default(),
351 }, 457 },
352 json!([]), 458 json!([]),
353 ); 459 );
@@ -355,6 +461,10 @@ fn main() {{}}
355 461
356#[test] 462#[test]
357fn diagnostics_dont_block_typing() { 463fn diagnostics_dont_block_typing() {
464 if skip_slow_tests() {
465 return;
466 }
467
358 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect(); 468 let librs: String = (0..10).map(|i| format!("mod m{};", i)).collect();
359 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect(); 469 let libs: String = (0..10).map(|i| format!("//- src/m{}.rs\nfn foo() {{}}\n\n", i)).collect();
360 let server = Project::with_fixture(&format!( 470 let server = Project::with_fixture(&format!(
@@ -423,6 +533,10 @@ fn main() {{}}
423 533
424#[test] 534#[test]
425fn preserves_dos_line_endings() { 535fn preserves_dos_line_endings() {
536 if skip_slow_tests() {
537 return;
538 }
539
426 let server = Project::with_fixture( 540 let server = Project::with_fixture(
427 &" 541 &"
428//- Cargo.toml 542//- Cargo.toml
diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs
index 86073b57d..d5ea52fa9 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/support.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs
@@ -7,7 +7,6 @@ use std::{
7}; 7};
8 8
9use crossbeam_channel::{after, select, Receiver}; 9use crossbeam_channel::{after, select, Receiver};
10use flexi_logger::Logger;
11use lsp_server::{Connection, Message, Notification, Request}; 10use lsp_server::{Connection, Message, Notification, Request};
12use lsp_types::{ 11use lsp_types::{
13 notification::{DidOpenTextDocument, Exit}, 12 notification::{DidOpenTextDocument, Exit},
@@ -53,7 +52,7 @@ impl<'a> Project<'a> {
53 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); 52 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
54 static INIT: Once = Once::new(); 53 static INIT: Once = Once::new();
55 INIT.call_once(|| { 54 INIT.call_once(|| {
56 let _ = Logger::with_env_or_str(crate::LOG).start().unwrap(); 55 let _ = env_logger::builder().is_test(true).try_init().unwrap();
57 ra_prof::set_filter(if crate::PROFILE.is_empty() { 56 ra_prof::set_filter(if crate::PROFILE.is_empty() {
58 ra_prof::Filter::disabled() 57 ra_prof::Filter::disabled()
59 } else { 58 } else {