aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock62
-rw-r--r--crates/ra_arena/src/map.rs10
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs30
-rw-r--r--crates/ra_flycheck/Cargo.toml (renamed from crates/ra_cargo_watch/Cargo.toml)2
-rw-r--r--crates/ra_flycheck/src/conv.rs (renamed from crates/ra_cargo_watch/src/conv.rs)0
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap)2
-rw-r--r--crates/ra_flycheck/src/conv/test.rs (renamed from crates/ra_cargo_watch/src/conv/test.rs)0
-rw-r--r--crates/ra_flycheck/src/lib.rs (renamed from crates/ra_cargo_watch/src/lib.rs)16
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs27
-rw-r--r--crates/ra_ide/src/completion.rs10
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs2
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs8
-rw-r--r--crates/ra_ide/src/completion/presentation.rs12
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs6
-rw-r--r--crates/ra_ide/src/inlay_hints.rs68
-rw-r--r--crates/ra_ide/src/lib.rs12
-rw-r--r--crates/ra_proc_macro/Cargo.toml5
-rw-r--r--crates/ra_proc_macro/src/lib.rs85
-rw-r--r--crates/ra_proc_macro/src/msg.rs93
-rw-r--r--crates/ra_proc_macro/src/process.rs196
-rw-r--r--crates/ra_proc_macro/src/rpc.rs266
-rw-r--r--crates/ra_project_model/Cargo.toml1
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs91
-rw-r--r--crates/ra_syntax/src/ast/edit.rs29
-rw-r--r--crates/ra_syntax/src/parsing/text_tree_sink.rs13
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt17
-rw-r--r--crates/ra_tt/src/lib.rs7
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_bench.rs4
-rw-r--r--crates/rust-analyzer/src/conv.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop.rs37
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs14
-rw-r--r--crates/rust-analyzer/src/world.rs52
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs46
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/support.rs22
-rw-r--r--docs/user/features.md6
-rw-r--r--editors/code/package-lock.json59
-rw-r--r--editors/code/package.json8
-rw-r--r--editors/code/src/client.ts5
-rw-r--r--editors/code/src/commands/syntax_tree.ts165
-rw-r--r--editors/code/src/ctx.ts9
-rw-r--r--editors/code/src/main.ts12
-rw-r--r--editors/code/src/tasks.ts52
-rw-r--r--editors/code/src/util.ts4
53 files changed, 1266 insertions, 322 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a3887ce99..9a1a05683 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -11,9 +11,9 @@ dependencies = [
11 11
12[[package]] 12[[package]]
13name = "anyhow" 13name = "anyhow"
14version = "1.0.27" 14version = "1.0.28"
15source = "registry+https://github.com/rust-lang/crates.io-index" 15source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" 16checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff"
17 17
18[[package]] 18[[package]]
19name = "anymap" 19name = "anymap"
@@ -435,9 +435,9 @@ dependencies = [
435 435
436[[package]] 436[[package]]
437name = "hermit-abi" 437name = "hermit-abi"
438version = "0.1.8" 438version = "0.1.10"
439source = "registry+https://github.com/rust-lang/crates.io-index" 439source = "registry+https://github.com/rust-lang/crates.io-index"
440checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" 440checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e"
441dependencies = [ 441dependencies = [
442 "libc", 442 "libc",
443] 443]
@@ -792,9 +792,9 @@ dependencies = [
792 792
793[[package]] 793[[package]]
794name = "paste" 794name = "paste"
795version = "0.1.8" 795version = "0.1.9"
796source = "registry+https://github.com/rust-lang/crates.io-index" 796source = "registry+https://github.com/rust-lang/crates.io-index"
797checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9" 797checksum = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932"
798dependencies = [ 798dependencies = [
799 "paste-impl", 799 "paste-impl",
800 "proc-macro-hack", 800 "proc-macro-hack",
@@ -802,9 +802,9 @@ dependencies = [
802 802
803[[package]] 803[[package]]
804name = "paste-impl" 804name = "paste-impl"
805version = "0.1.8" 805version = "0.1.9"
806source = "registry+https://github.com/rust-lang/crates.io-index" 806source = "registry+https://github.com/rust-lang/crates.io-index"
807checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c" 807checksum = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729"
808dependencies = [ 808dependencies = [
809 "proc-macro-hack", 809 "proc-macro-hack",
810 "proc-macro2", 810 "proc-macro2",
@@ -848,9 +848,9 @@ checksum = "fcfdefadc3d57ca21cf17990a28ef4c0f7c61383a28cb7604cf4a18e6ede1420"
848 848
849[[package]] 849[[package]]
850name = "proc-macro2" 850name = "proc-macro2"
851version = "1.0.9" 851version = "1.0.10"
852source = "registry+https://github.com/rust-lang/crates.io-index" 852source = "registry+https://github.com/rust-lang/crates.io-index"
853checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" 853checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3"
854dependencies = [ 854dependencies = [
855 "unicode-xid", 855 "unicode-xid",
856] 856]
@@ -887,19 +887,6 @@ dependencies = [
887] 887]
888 888
889[[package]] 889[[package]]
890name = "ra_cargo_watch"
891version = "0.1.0"
892dependencies = [
893 "cargo_metadata",
894 "crossbeam-channel",
895 "insta",
896 "jod-thread",
897 "log",
898 "lsp-types",
899 "serde_json",
900]
901
902[[package]]
903name = "ra_cfg" 890name = "ra_cfg"
904version = "0.1.0" 891version = "0.1.0"
905dependencies = [ 892dependencies = [
@@ -924,6 +911,19 @@ dependencies = [
924] 911]
925 912
926[[package]] 913[[package]]
914name = "ra_flycheck"
915version = "0.1.0"
916dependencies = [
917 "cargo_metadata",
918 "crossbeam-channel",
919 "insta",
920 "jod-thread",
921 "log",
922 "lsp-types",
923 "serde_json",
924]
925
926[[package]]
927name = "ra_fmt" 927name = "ra_fmt"
928version = "0.1.0" 928version = "0.1.0"
929dependencies = [ 929dependencies = [
@@ -1075,7 +1075,12 @@ dependencies = [
1075name = "ra_proc_macro" 1075name = "ra_proc_macro"
1076version = "0.1.0" 1076version = "0.1.0"
1077dependencies = [ 1077dependencies = [
1078 "crossbeam-channel",
1079 "jod-thread",
1080 "log",
1078 "ra_tt", 1081 "ra_tt",
1082 "serde",
1083 "serde_json",
1079] 1084]
1080 1085
1081[[package]] 1086[[package]]
@@ -1096,7 +1101,6 @@ dependencies = [
1096 "cargo_metadata", 1101 "cargo_metadata",
1097 "log", 1102 "log",
1098 "ra_arena", 1103 "ra_arena",
1099 "ra_cargo_watch",
1100 "ra_cfg", 1104 "ra_cfg",
1101 "ra_db", 1105 "ra_db",
1102 "ra_proc_macro", 1106 "ra_proc_macro",
@@ -1295,8 +1299,8 @@ dependencies = [
1295 "lsp-types", 1299 "lsp-types",
1296 "parking_lot", 1300 "parking_lot",
1297 "pico-args", 1301 "pico-args",
1298 "ra_cargo_watch",
1299 "ra_db", 1302 "ra_db",
1303 "ra_flycheck",
1300 "ra_hir", 1304 "ra_hir",
1301 "ra_hir_def", 1305 "ra_hir_def",
1302 "ra_hir_ty", 1306 "ra_hir_ty",
@@ -1426,9 +1430,9 @@ dependencies = [
1426 1430
1427[[package]] 1431[[package]]
1428name = "serde_json" 1432name = "serde_json"
1429version = "1.0.48" 1433version = "1.0.50"
1430source = "registry+https://github.com/rust-lang/crates.io-index" 1434source = "registry+https://github.com/rust-lang/crates.io-index"
1431checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" 1435checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867"
1432dependencies = [ 1436dependencies = [
1433 "itoa", 1437 "itoa",
1434 "ryu", 1438 "ryu",
@@ -1651,9 +1655,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1651 1655
1652[[package]] 1656[[package]]
1653name = "winapi-util" 1657name = "winapi-util"
1654version = "0.1.3" 1658version = "0.1.4"
1655source = "registry+https://github.com/rust-lang/crates.io-index" 1659source = "registry+https://github.com/rust-lang/crates.io-index"
1656checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" 1660checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e"
1657dependencies = [ 1661dependencies = [
1658 "winapi 0.3.8", 1662 "winapi 0.3.8",
1659] 1663]
diff --git a/crates/ra_arena/src/map.rs b/crates/ra_arena/src/map.rs
index 5e764113d..0f33907c0 100644
--- a/crates/ra_arena/src/map.rs
+++ b/crates/ra_arena/src/map.rs
@@ -14,14 +14,8 @@ pub struct ArenaMap<ID, V> {
14impl<T, V> ArenaMap<Idx<T>, V> { 14impl<T, V> ArenaMap<Idx<T>, V> {
15 pub fn insert(&mut self, id: Idx<T>, t: V) { 15 pub fn insert(&mut self, id: Idx<T>, t: V) {
16 let idx = Self::to_idx(id); 16 let idx = Self::to_idx(id);
17 if self.v.capacity() <= idx { 17
18 self.v.reserve(idx + 1 - self.v.capacity()); 18 self.v.resize_with((idx + 1).max(self.v.len()), || None);
19 }
20 if self.v.len() <= idx {
21 while self.v.len() <= idx {
22 self.v.push(None);
23 }
24 }
25 self.v[idx] = Some(t); 19 self.v[idx] = Some(t);
26 } 20 }
27 21
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 41bb97928..8d1af9933 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -1,15 +1,11 @@
1//! FIXME: write short doc here
2
3use std::iter; 1use std::iter;
4 2
5use hir::{Adt, HasSource, ModuleDef, Semantics}; 3use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 4use itertools::Itertools;
7use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 7
9use crate::{Assist, AssistCtx, AssistId}; 8use crate::{Assist, AssistCtx, AssistId};
10use ra_syntax::ast::{self, make, AstNode, NameOwner};
11
12use ast::{MatchArm, Pat};
13 9
14// Assist: fill_match_arms 10// Assist: fill_match_arms
15// 11//
@@ -717,4 +713,28 @@ mod tests {
717 "#, 713 "#,
718 ); 714 );
719 } 715 }
716
717 #[test]
718 fn fill_match_arms_placeholder() {
719 check_assist(
720 fill_match_arms,
721 r#"
722 enum A { One, Two, }
723 fn foo(a: A) {
724 match a<|> {
725 _ => (),
726 }
727 }
728 "#,
729 r#"
730 enum A { One, Two, }
731 fn foo(a: A) {
732 match <|>a {
733 A::One => {}
734 A::Two => {}
735 }
736 }
737 "#,
738 );
739 }
720} 740}
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index 300033a18..c9a9ddc12 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -1,6 +1,6 @@
1[package] 1[package]
2edition = "2018" 2edition = "2018"
3name = "ra_cargo_watch" 3name = "ra_flycheck"
4version = "0.1.0" 4version = "0.1.0"
5authors = ["rust-analyzer developers"] 5authors = ["rust-analyzer developers"]
6 6
diff --git a/crates/ra_cargo_watch/src/conv.rs b/crates/ra_flycheck/src/conv.rs
index 817543deb..817543deb 100644
--- a/crates/ra_cargo_watch/src/conv.rs
+++ b/crates/ra_flycheck/src/conv.rs
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
index a59fa84fa..4c9db0385 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
index 61ae0c9ae..7cde4d867 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_handles_macro_location.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
index 641da1a58..1cc37e087 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_macro_compiler_error.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
index 0557a2e79..615ed8378 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_multi_line_fix.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
index 754bc33a4..0df0fce18 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
index 78b7f7cc8..28ebcb3b3 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
index 5989ed202..5e0873281 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
index e34b546dc..e500d3cd6 100644
--- a/crates/ra_cargo_watch/src/conv/snapshots/ra_cargo_watch__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_cargo_watch/src/conv/test.rs 2source: crates/ra_flycheck/src/conv/test.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_cargo_watch/src/conv/test.rs b/crates/ra_flycheck/src/conv/test.rs
index 4e81455ca..4e81455ca 100644
--- a/crates/ra_cargo_watch/src/conv/test.rs
+++ b/crates/ra_flycheck/src/conv/test.rs
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 2692c1bf5..38940a77b 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -22,7 +22,7 @@ use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
22pub use crate::conv::url_from_path_with_drive_lowercasing; 22pub use crate::conv::url_from_path_with_drive_lowercasing;
23 23
24#[derive(Clone, Debug)] 24#[derive(Clone, Debug)]
25pub struct CheckOptions { 25pub struct CheckConfig {
26 pub enable: bool, 26 pub enable: bool,
27 pub args: Vec<String>, 27 pub args: Vec<String>,
28 pub command: String, 28 pub command: String,
@@ -42,13 +42,11 @@ pub struct CheckWatcher {
42} 42}
43 43
44impl CheckWatcher { 44impl CheckWatcher {
45 pub fn new(options: &CheckOptions, workspace_root: PathBuf) -> CheckWatcher { 45 pub fn new(config: CheckConfig, workspace_root: PathBuf) -> CheckWatcher {
46 let options = options.clone();
47
48 let (task_send, task_recv) = unbounded::<CheckTask>(); 46 let (task_send, task_recv) = unbounded::<CheckTask>();
49 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>(); 47 let (cmd_send, cmd_recv) = unbounded::<CheckCommand>();
50 let handle = jod_thread::spawn(move || { 48 let handle = jod_thread::spawn(move || {
51 let mut check = CheckWatcherThread::new(options, workspace_root); 49 let mut check = CheckWatcherThread::new(config, workspace_root);
52 check.run(&task_send, &cmd_recv); 50 check.run(&task_send, &cmd_recv);
53 }); 51 });
54 CheckWatcher { task_recv, cmd_send, handle: Some(handle) } 52 CheckWatcher { task_recv, cmd_send, handle: Some(handle) }
@@ -78,14 +76,14 @@ pub enum CheckCommand {
78} 76}
79 77
80struct CheckWatcherThread { 78struct CheckWatcherThread {
81 options: CheckOptions, 79 options: CheckConfig,
82 workspace_root: PathBuf, 80 workspace_root: PathBuf,
83 watcher: WatchThread, 81 watcher: WatchThread,
84 last_update_req: Option<Instant>, 82 last_update_req: Option<Instant>,
85} 83}
86 84
87impl CheckWatcherThread { 85impl CheckWatcherThread {
88 fn new(options: CheckOptions, workspace_root: PathBuf) -> CheckWatcherThread { 86 fn new(options: CheckConfig, workspace_root: PathBuf) -> CheckWatcherThread {
89 CheckWatcherThread { 87 CheckWatcherThread {
90 options, 88 options,
91 workspace_root, 89 workspace_root,
@@ -245,7 +243,7 @@ impl fmt::Display for CargoError {
245} 243}
246impl error::Error for CargoError {} 244impl error::Error for CargoError {}
247 245
248pub fn run_cargo( 246fn run_cargo(
249 args: &[String], 247 args: &[String],
250 current_dir: Option<&Path>, 248 current_dir: Option<&Path>,
251 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool, 249 on_message: &mut dyn FnMut(cargo_metadata::Message) -> bool,
@@ -324,7 +322,7 @@ impl WatchThread {
324 WatchThread { message_recv: never(), _handle: None } 322 WatchThread { message_recv: never(), _handle: None }
325 } 323 }
326 324
327 fn new(options: &CheckOptions, workspace_root: &Path) -> WatchThread { 325 fn new(options: &CheckConfig, workspace_root: &Path) -> WatchThread {
328 let mut args: Vec<String> = vec![ 326 let mut args: Vec<String> = vec![
329 options.command.clone(), 327 options.command.clone(),
330 "--workspace".to_string(), 328 "--workspace".to_string(),
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
index 4d270e0de..97d1208ec 100644
--- a/crates/ra_hir_expand/src/proc_macro.rs
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -9,6 +9,15 @@ pub struct ProcMacroExpander {
9 proc_macro_id: ProcMacroId, 9 proc_macro_id: ProcMacroId,
10} 10}
11 11
12macro_rules! err {
13 ($fmt:literal, $($tt:tt),*) => {
14 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown(format!($fmt, $($tt),*)))
15 };
16 ($fmt:literal) => {
17 mbe::ExpandError::ProcMacroError(tt::ExpansionError::Unknown($fmt.to_string()))
18 }
19}
20
12impl ProcMacroExpander { 21impl ProcMacroExpander {
13 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { 22 pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander {
14 ProcMacroExpander { krate, proc_macro_id } 23 ProcMacroExpander { krate, proc_macro_id }
@@ -25,8 +34,24 @@ impl ProcMacroExpander {
25 .proc_macro 34 .proc_macro
26 .get(self.proc_macro_id.0 as usize) 35 .get(self.proc_macro_id.0 as usize)
27 .clone() 36 .clone()
28 .ok_or_else(|| mbe::ExpandError::ConversionError)?; 37 .ok_or_else(|| err!("No derive macro found."))?;
38
39 let tt = remove_derive_atr(tt, &proc_macro.name)
40 .ok_or_else(|| err!("Fail to remove derive for custom derive"))?;
29 41
30 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) 42 proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from)
31 } 43 }
32} 44}
45
46fn remove_derive_atr(tt: &tt::Subtree, _name: &str) -> Option<tt::Subtree> {
47 // FIXME: proper handle the remove derive
48 // We assume the first 2 tokens are #[derive(name)]
49 if tt.token_trees.len() > 2 {
50 let mut tt = tt.clone();
51 tt.token_trees.remove(0);
52 tt.token_trees.remove(0);
53 return Some(tt);
54 }
55
56 None
57}
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index cd0757be5..b683572fb 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -34,15 +34,15 @@ pub use crate::completion::completion_item::{
34}; 34};
35 35
36#[derive(Clone, Debug, PartialEq, Eq)] 36#[derive(Clone, Debug, PartialEq, Eq)]
37pub struct CompletionOptions { 37pub struct CompletionConfig {
38 pub enable_postfix_completions: bool, 38 pub enable_postfix_completions: bool,
39 pub add_call_parenthesis: bool, 39 pub add_call_parenthesis: bool,
40 pub add_call_argument_snippets: bool, 40 pub add_call_argument_snippets: bool,
41} 41}
42 42
43impl Default for CompletionOptions { 43impl Default for CompletionConfig {
44 fn default() -> Self { 44 fn default() -> Self {
45 CompletionOptions { 45 CompletionConfig {
46 enable_postfix_completions: true, 46 enable_postfix_completions: true,
47 add_call_parenthesis: true, 47 add_call_parenthesis: true,
48 add_call_argument_snippets: true, 48 add_call_argument_snippets: true,
@@ -75,9 +75,9 @@ impl Default for CompletionOptions {
75pub(crate) fn completions( 75pub(crate) fn completions(
76 db: &RootDatabase, 76 db: &RootDatabase,
77 position: FilePosition, 77 position: FilePosition,
78 options: &CompletionOptions, 78 config: &CompletionConfig,
79) -> Option<Completions> { 79) -> Option<Completions> {
80 let ctx = CompletionContext::new(db, position, options)?; 80 let ctx = CompletionContext::new(db, position, config)?;
81 81
82 let mut acc = Completions::default(); 82 let mut acc = Completions::default();
83 83
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0a00054b2..29c2881c6 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -15,7 +15,7 @@ use crate::{
15}; 15};
16 16
17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { 17pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
18 if !ctx.options.enable_postfix_completions { 18 if !ctx.config.enable_postfix_completions {
19 return; 19 return;
20 } 20 }
21 21
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 319e33b61..fdc0da2c5 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11}; 11};
12use ra_text_edit::AtomTextEdit; 12use ra_text_edit::AtomTextEdit;
13 13
14use crate::{completion::CompletionOptions, FilePosition}; 14use crate::{completion::CompletionConfig, FilePosition};
15 15
16/// `CompletionContext` is created early during completion to figure out, where 16/// `CompletionContext` is created early during completion to figure out, where
17/// exactly is the cursor, syntax-wise. 17/// exactly is the cursor, syntax-wise.
@@ -19,7 +19,7 @@ use crate::{completion::CompletionOptions, FilePosition};
19pub(crate) struct CompletionContext<'a> { 19pub(crate) struct CompletionContext<'a> {
20 pub(super) sema: Semantics<'a, RootDatabase>, 20 pub(super) sema: Semantics<'a, RootDatabase>,
21 pub(super) db: &'a RootDatabase, 21 pub(super) db: &'a RootDatabase,
22 pub(super) options: &'a CompletionOptions, 22 pub(super) config: &'a CompletionConfig,
23 pub(super) offset: TextUnit, 23 pub(super) offset: TextUnit,
24 /// The token before the cursor, in the original file. 24 /// The token before the cursor, in the original file.
25 pub(super) original_token: SyntaxToken, 25 pub(super) original_token: SyntaxToken,
@@ -61,7 +61,7 @@ impl<'a> CompletionContext<'a> {
61 pub(super) fn new( 61 pub(super) fn new(
62 db: &'a RootDatabase, 62 db: &'a RootDatabase,
63 position: FilePosition, 63 position: FilePosition,
64 options: &'a CompletionOptions, 64 config: &'a CompletionConfig,
65 ) -> Option<CompletionContext<'a>> { 65 ) -> Option<CompletionContext<'a>> {
66 let sema = Semantics::new(db); 66 let sema = Semantics::new(db);
67 67
@@ -85,7 +85,7 @@ impl<'a> CompletionContext<'a> {
85 let mut ctx = CompletionContext { 85 let mut ctx = CompletionContext {
86 sema, 86 sema,
87 db, 87 db,
88 options, 88 config,
89 original_token, 89 original_token,
90 token, 90 token,
91 offset: position.offset, 91 offset: position.offset,
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 60f1b83f3..1c7c0924d 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -106,7 +106,7 @@ impl Completions {
106 }; 106 };
107 107
108 // Add `<>` for generic types 108 // Add `<>` for generic types
109 if ctx.is_path_type && !ctx.has_type_args && ctx.options.add_call_parenthesis { 109 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
110 let has_non_default_type_params = match resolution { 110 let has_non_default_type_params = match resolution {
111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db), 111 ScopeDef::ModuleDef(Adt(it)) => it.has_non_default_type_params(ctx.db),
112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db), 112 ScopeDef::ModuleDef(TypeAlias(it)) => it.has_non_default_type_params(ctx.db),
@@ -211,14 +211,14 @@ impl Completions {
211 .detail(function_signature.to_string()); 211 .detail(function_signature.to_string());
212 212
213 // If not an import, add parenthesis automatically. 213 // If not an import, add parenthesis automatically.
214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.options.add_call_parenthesis { 214 if ctx.use_item_syntax.is_none() && !ctx.is_call && ctx.config.add_call_parenthesis {
215 tested_by!(inserts_parens_for_function_calls); 215 tested_by!(inserts_parens_for_function_calls);
216 216
217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 { 217 let (snippet, label) = if params.is_empty() || has_self_param && params.len() == 1 {
218 (format!("{}()$0", name), format!("{}()", name)) 218 (format!("{}()$0", name), format!("{}()", name))
219 } else { 219 } else {
220 builder = builder.trigger_call_info(); 220 builder = builder.trigger_call_info();
221 let snippet = if ctx.options.add_call_argument_snippets { 221 let snippet = if ctx.config.add_call_argument_snippets {
222 let to_skip = if has_self_param { 1 } else { 0 }; 222 let to_skip = if has_self_param { 1 } else { 0 };
223 let function_params_snippet = function_signature 223 let function_params_snippet = function_signature
224 .parameter_names 224 .parameter_names
@@ -311,7 +311,7 @@ mod tests {
311 311
312 use crate::completion::{ 312 use crate::completion::{
313 test_utils::{do_completion, do_completion_with_options}, 313 test_utils::{do_completion, do_completion_with_options},
314 CompletionItem, CompletionKind, CompletionOptions, 314 CompletionConfig, CompletionItem, CompletionKind,
315 }; 315 };
316 316
317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> { 317 fn do_reference_completion(ra_fixture: &str) -> Vec<CompletionItem> {
@@ -320,7 +320,7 @@ mod tests {
320 320
321 fn do_reference_completion_with_options( 321 fn do_reference_completion_with_options(
322 ra_fixture: &str, 322 ra_fixture: &str,
323 options: CompletionOptions, 323 options: CompletionConfig,
324 ) -> Vec<CompletionItem> { 324 ) -> Vec<CompletionItem> {
325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) 325 do_completion_with_options(ra_fixture, CompletionKind::Reference, &options)
326 } 326 }
@@ -589,7 +589,7 @@ mod tests {
589 s.f<|> 589 s.f<|>
590 } 590 }
591 ", 591 ",
592 CompletionOptions { 592 CompletionConfig {
593 add_call_argument_snippets: false, 593 add_call_argument_snippets: false,
594 .. Default::default() 594 .. Default::default()
595 } 595 }
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index 136857315..eb90b5279 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -1,19 +1,19 @@
1//! Runs completion for testing purposes. 1//! Runs completion for testing purposes.
2 2
3use crate::{ 3use crate::{
4 completion::{completion_item::CompletionKind, CompletionOptions}, 4 completion::{completion_item::CompletionKind, CompletionConfig},
5 mock_analysis::{analysis_and_position, single_file_with_position}, 5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem, 6 CompletionItem,
7}; 7};
8 8
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionOptions::default()) 10 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 11}
12 12
13pub(crate) fn do_completion_with_options( 13pub(crate) fn do_completion_with_options(
14 code: &str, 14 code: &str,
15 kind: CompletionKind, 15 kind: CompletionKind,
16 options: &CompletionOptions, 16 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 17) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 18 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 19 analysis_and_position(code)
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index f4f0751c0..d06fc03d3 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
12 12
13#[derive(Clone, Debug, PartialEq, Eq)] 13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct InlayHintsOptions { 14pub struct InlayHintsConfig {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool, 17 pub chaining_hints: bool,
18 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
19} 19}
20 20
21impl Default for InlayHintsOptions { 21impl Default for InlayHintsConfig {
22 fn default() -> Self { 22 fn default() -> Self {
23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None } 23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
24 } 24 }
@@ -41,7 +41,7 @@ pub struct InlayHint {
41pub(crate) fn inlay_hints( 41pub(crate) fn inlay_hints(
42 db: &RootDatabase, 42 db: &RootDatabase,
43 file_id: FileId, 43 file_id: FileId,
44 options: &InlayHintsOptions, 44 config: &InlayHintsConfig,
45) -> Vec<InlayHint> { 45) -> Vec<InlayHint> {
46 let _p = profile("inlay_hints"); 46 let _p = profile("inlay_hints");
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
@@ -50,14 +50,14 @@ pub(crate) fn inlay_hints(
50 let mut res = Vec::new(); 50 let mut res = Vec::new();
51 for node in file.syntax().descendants() { 51 for node in file.syntax().descendants() {
52 if let Some(expr) = ast::Expr::cast(node.clone()) { 52 if let Some(expr) = ast::Expr::cast(node.clone()) {
53 get_chaining_hints(&mut res, &sema, options, expr); 53 get_chaining_hints(&mut res, &sema, config, expr);
54 } 54 }
55 55
56 match_ast! { 56 match_ast! {
57 match node { 57 match node {
58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 59 ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, options, it); }, 60 ast::BindPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
61 _ => (), 61 _ => (),
62 } 62 }
63 } 63 }
@@ -68,10 +68,10 @@ pub(crate) fn inlay_hints(
68fn get_chaining_hints( 68fn get_chaining_hints(
69 acc: &mut Vec<InlayHint>, 69 acc: &mut Vec<InlayHint>,
70 sema: &Semantics<RootDatabase>, 70 sema: &Semantics<RootDatabase>,
71 options: &InlayHintsOptions, 71 config: &InlayHintsConfig,
72 expr: ast::Expr, 72 expr: ast::Expr,
73) -> Option<()> { 73) -> Option<()> {
74 if !options.chaining_hints { 74 if !config.chaining_hints {
75 return None; 75 return None;
76 } 76 }
77 77
@@ -95,7 +95,7 @@ fn get_chaining_hints(
95 let next = tokens.next()?.kind(); 95 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind(); 96 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
98 let label = ty.display_truncated(sema.db, options.max_length).to_string(); 98 let label = ty.display_truncated(sema.db, config.max_length).to_string();
99 acc.push(InlayHint { 99 acc.push(InlayHint {
100 range: expr.syntax().text_range(), 100 range: expr.syntax().text_range(),
101 kind: InlayKind::ChainingHint, 101 kind: InlayKind::ChainingHint,
@@ -108,10 +108,10 @@ fn get_chaining_hints(
108fn get_param_name_hints( 108fn get_param_name_hints(
109 acc: &mut Vec<InlayHint>, 109 acc: &mut Vec<InlayHint>,
110 sema: &Semantics<RootDatabase>, 110 sema: &Semantics<RootDatabase>,
111 options: &InlayHintsOptions, 111 config: &InlayHintsConfig,
112 expr: ast::Expr, 112 expr: ast::Expr,
113) -> Option<()> { 113) -> Option<()> {
114 if !options.parameter_hints { 114 if !config.parameter_hints {
115 return None; 115 return None;
116 } 116 }
117 117
@@ -148,10 +148,10 @@ fn get_param_name_hints(
148fn get_bind_pat_hints( 148fn get_bind_pat_hints(
149 acc: &mut Vec<InlayHint>, 149 acc: &mut Vec<InlayHint>,
150 sema: &Semantics<RootDatabase>, 150 sema: &Semantics<RootDatabase>,
151 options: &InlayHintsOptions, 151 config: &InlayHintsConfig,
152 pat: ast::BindPat, 152 pat: ast::BindPat,
153) -> Option<()> { 153) -> Option<()> {
154 if !options.type_hints { 154 if !config.type_hints {
155 return None; 155 return None;
156 } 156 }
157 157
@@ -164,7 +164,7 @@ fn get_bind_pat_hints(
164 acc.push(InlayHint { 164 acc.push(InlayHint {
165 range: pat.syntax().text_range(), 165 range: pat.syntax().text_range(),
166 kind: InlayKind::TypeHint, 166 kind: InlayKind::TypeHint,
167 label: ty.display_truncated(sema.db, options.max_length).to_string().into(), 167 label: ty.display_truncated(sema.db, config.max_length).to_string().into(),
168 }); 168 });
169 Some(()) 169 Some(())
170} 170}
@@ -270,7 +270,7 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
270 270
271#[cfg(test)] 271#[cfg(test)]
272mod tests { 272mod tests {
273 use crate::inlay_hints::InlayHintsOptions; 273 use crate::inlay_hints::InlayHintsConfig;
274 use insta::assert_debug_snapshot; 274 use insta::assert_debug_snapshot;
275 275
276 use crate::mock_analysis::single_file; 276 use crate::mock_analysis::single_file;
@@ -284,7 +284,7 @@ mod tests {
284 let _x = foo(4, 4); 284 let _x = foo(4, 4);
285 }"#, 285 }"#,
286 ); 286 );
287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
288 [ 288 [
289 InlayHint { 289 InlayHint {
290 range: [106; 107), 290 range: [106; 107),
@@ -308,7 +308,7 @@ mod tests {
308 let _x = foo(4, 4); 308 let _x = foo(4, 4);
309 }"#, 309 }"#,
310 ); 310 );
311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); 311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
312 } 312 }
313 313
314 #[test] 314 #[test]
@@ -320,7 +320,7 @@ mod tests {
320 let _x = foo(4, 4); 320 let _x = foo(4, 4);
321 }"#, 321 }"#,
322 ); 322 );
323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" 323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
324 [ 324 [
325 InlayHint { 325 InlayHint {
326 range: [97; 99), 326 range: [97; 99),
@@ -344,7 +344,7 @@ fn main() {
344}"#, 344}"#,
345 ); 345 );
346 346
347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 347 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
348 [ 348 [
349 InlayHint { 349 InlayHint {
350 range: [69; 71), 350 range: [69; 71),
@@ -401,7 +401,7 @@ fn main() {
401}"#, 401}"#,
402 ); 402 );
403 403
404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 404 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
405 [ 405 [
406 InlayHint { 406 InlayHint {
407 range: [193; 197), 407 range: [193; 197),
@@ -481,7 +481,7 @@ fn main() {
481}"#, 481}"#,
482 ); 482 );
483 483
484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 484 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
485 [ 485 [
486 InlayHint { 486 InlayHint {
487 range: [21; 30), 487 range: [21; 30),
@@ -545,7 +545,7 @@ fn main() {
545}"#, 545}"#,
546 ); 546 );
547 547
548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 548 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
549 [ 549 [
550 InlayHint { 550 InlayHint {
551 range: [21; 30), 551 range: [21; 30),
@@ -595,7 +595,7 @@ fn main() {
595}"#, 595}"#,
596 ); 596 );
597 597
598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 598 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
599 [ 599 [
600 InlayHint { 600 InlayHint {
601 range: [188; 192), 601 range: [188; 192),
@@ -690,7 +690,7 @@ fn main() {
690}"#, 690}"#,
691 ); 691 );
692 692
693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 693 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
694 [ 694 [
695 InlayHint { 695 InlayHint {
696 range: [188; 192), 696 range: [188; 192),
@@ -785,7 +785,7 @@ fn main() {
785}"#, 785}"#,
786 ); 786 );
787 787
788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 788 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
789 [ 789 [
790 InlayHint { 790 InlayHint {
791 range: [252; 256), 791 range: [252; 256),
@@ -857,7 +857,7 @@ fn main() {
857}"#, 857}"#,
858 ); 858 );
859 859
860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 860 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
861 [ 861 [
862 InlayHint { 862 InlayHint {
863 range: [74; 75), 863 range: [74; 75),
@@ -945,7 +945,7 @@ fn main() {
945}"#, 945}"#,
946 ); 946 );
947 947
948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions::default()).unwrap(), @r###" 948 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
949 [ 949 [
950 InlayHint { 950 InlayHint {
951 range: [798; 809), 951 range: [798; 809),
@@ -1067,7 +1067,7 @@ fn main() {
1067}"#, 1067}"#,
1068 ); 1068 );
1069 1069
1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1070 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1071 [] 1071 []
1072 "### 1072 "###
1073 ); 1073 );
@@ -1093,7 +1093,7 @@ fn main() {
1093}"#, 1093}"#,
1094 ); 1094 );
1095 1095
1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions { max_length: Some(8), ..Default::default() }).unwrap(), @r###" 1096 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1097 [] 1097 []
1098 "### 1098 "###
1099 ); 1099 );
@@ -1115,7 +1115,7 @@ fn main() {
1115 .into_c(); 1115 .into_c();
1116 }"#, 1116 }"#,
1117 ); 1117 );
1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1119 [ 1119 [
1120 InlayHint { 1120 InlayHint {
1121 range: [232; 269), 1121 range: [232; 269),
@@ -1144,7 +1144,7 @@ fn main() {
1144 let c = A(B(C)).into_b().into_c(); 1144 let c = A(B(C)).into_b().into_c();
1145 }"#, 1145 }"#,
1146 ); 1146 );
1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); 1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1148 } 1148 }
1149 1149
1150 #[test] 1150 #[test]
@@ -1162,7 +1162,7 @@ fn main() {
1162 .0; 1162 .0;
1163 }"#, 1163 }"#,
1164 ); 1164 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [ 1166 [
1167 InlayHint { 1167 InlayHint {
1168 range: [150; 221), 1168 range: [150; 221),
@@ -1204,7 +1204,7 @@ fn main() {
1204 .into_c(); 1204 .into_c();
1205 }"#, 1205 }"#,
1206 ); 1206 );
1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" 1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1208 [ 1208 [
1209 InlayHint { 1209 InlayHint {
1210 range: [403; 452), 1210 range: [403; 452),
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 937c9caa5..285381086 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -62,13 +62,13 @@ use crate::display::ToNav;
62pub use crate::{ 62pub use crate::{
63 assists::{Assist, AssistId}, 63 assists::{Assist, AssistId},
64 call_hierarchy::CallItem, 64 call_hierarchy::CallItem,
65 completion::{CompletionItem, CompletionItemKind, CompletionOptions, InsertTextFormat}, 65 completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat},
66 diagnostics::Severity, 66 diagnostics::Severity,
67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, 67 display::{file_structure, FunctionSignature, NavigationTarget, StructureNode},
68 expand_macro::ExpandedMacro, 68 expand_macro::ExpandedMacro,
69 folding_ranges::{Fold, FoldKind}, 69 folding_ranges::{Fold, FoldKind},
70 hover::HoverResult, 70 hover::HoverResult,
71 inlay_hints::{InlayHint, InlayHintsOptions, InlayKind}, 71 inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, 72 references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult},
73 runnables::{Runnable, RunnableKind, TestId}, 73 runnables::{Runnable, RunnableKind, TestId},
74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 74 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -325,9 +325,9 @@ impl Analysis {
325 pub fn inlay_hints( 325 pub fn inlay_hints(
326 &self, 326 &self,
327 file_id: FileId, 327 file_id: FileId,
328 inlay_hint_opts: &InlayHintsOptions, 328 config: &InlayHintsConfig,
329 ) -> Cancelable<Vec<InlayHint>> { 329 ) -> Cancelable<Vec<InlayHint>> {
330 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, inlay_hint_opts)) 330 self.with_db(|db| inlay_hints::inlay_hints(db, file_id, config))
331 } 331 }
332 332
333 /// Returns the set of folding ranges. 333 /// Returns the set of folding ranges.
@@ -450,9 +450,9 @@ impl Analysis {
450 pub fn completions( 450 pub fn completions(
451 &self, 451 &self,
452 position: FilePosition, 452 position: FilePosition,
453 options: &CompletionOptions, 453 config: &CompletionConfig,
454 ) -> Cancelable<Option<Vec<CompletionItem>>> { 454 ) -> Cancelable<Option<Vec<CompletionItem>>> {
455 self.with_db(|db| completion::completions(db, position, options).map(Into::into)) 455 self.with_db(|db| completion::completions(db, position, config).map(Into::into))
456 } 456 }
457 457
458 /// Computes assists (aka code actions aka intentions) for the given 458 /// Computes assists (aka code actions aka intentions) for the given
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml
index bc2c37296..d009ceb82 100644
--- a/crates/ra_proc_macro/Cargo.toml
+++ b/crates/ra_proc_macro/Cargo.toml
@@ -10,3 +10,8 @@ doctest = false
10 10
11[dependencies] 11[dependencies]
12ra_tt = { path = "../ra_tt" } 12ra_tt = { path = "../ra_tt" }
13serde = { version = "1.0", features = ["derive"] }
14serde_json = "1.0"
15log = "0.4.8"
16crossbeam-channel = "0.4.0"
17jod-thread = "0.1.1"
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs
index 5e21dd487..51fbb046a 100644
--- a/crates/ra_proc_macro/src/lib.rs
+++ b/crates/ra_proc_macro/src/lib.rs
@@ -5,55 +5,104 @@
5//! is used to provide basic infrastructure for communication between two 5//! is used to provide basic infrastructure for communication between two
6//! processes: Client (RA itself), Server (the external program) 6//! processes: Client (RA itself), Server (the external program)
7 7
8mod rpc;
9mod process;
10pub mod msg;
11
12use process::{ProcMacroProcessSrv, ProcMacroProcessThread};
8use ra_tt::{SmolStr, Subtree}; 13use ra_tt::{SmolStr, Subtree};
14use rpc::ProcMacroKind;
9use std::{ 15use std::{
10 path::{Path, PathBuf}, 16 path::{Path, PathBuf},
11 sync::Arc, 17 sync::Arc,
12}; 18};
13 19
14#[derive(Debug, Clone, PartialEq, Eq)] 20pub use rpc::{ExpansionResult, ExpansionTask};
21
22#[derive(Debug, Clone)]
15pub struct ProcMacroProcessExpander { 23pub struct ProcMacroProcessExpander {
16 process: Arc<ProcMacroProcessSrv>, 24 process: Arc<ProcMacroProcessSrv>,
25 dylib_path: PathBuf,
17 name: SmolStr, 26 name: SmolStr,
18} 27}
19 28
29impl Eq for ProcMacroProcessExpander {}
30impl PartialEq for ProcMacroProcessExpander {
31 fn eq(&self, other: &Self) -> bool {
32 self.name == other.name
33 && self.dylib_path == other.dylib_path
34 && Arc::ptr_eq(&self.process, &other.process)
35 }
36}
37
20impl ra_tt::TokenExpander for ProcMacroProcessExpander { 38impl ra_tt::TokenExpander for ProcMacroProcessExpander {
21 fn expand( 39 fn expand(
22 &self, 40 &self,
23 _subtree: &Subtree, 41 subtree: &Subtree,
24 _attr: Option<&Subtree>, 42 _attr: Option<&Subtree>,
25 ) -> Result<Subtree, ra_tt::ExpansionError> { 43 ) -> Result<Subtree, ra_tt::ExpansionError> {
26 // FIXME: do nothing for now 44 self.process.custom_derive(&self.dylib_path, subtree, &self.name)
27 Ok(Subtree::default())
28 } 45 }
29} 46}
30 47
31#[derive(Debug, Clone, PartialEq, Eq)] 48#[derive(Debug)]
32pub struct ProcMacroProcessSrv { 49enum ProcMacroClientKind {
33 path: PathBuf, 50 Process { process: Arc<ProcMacroProcessSrv>, thread: ProcMacroProcessThread },
51 Dummy,
34} 52}
35 53
36#[derive(Debug, Clone, PartialEq, Eq)] 54#[derive(Debug)]
37pub enum ProcMacroClient { 55pub struct ProcMacroClient {
38 Process { process: Arc<ProcMacroProcessSrv> }, 56 kind: ProcMacroClientKind,
39 Dummy,
40} 57}
41 58
42impl ProcMacroClient { 59impl ProcMacroClient {
43 pub fn extern_process(process_path: &Path) -> ProcMacroClient { 60 pub fn extern_process(process_path: &Path) -> Result<ProcMacroClient, std::io::Error> {
44 let process = ProcMacroProcessSrv { path: process_path.into() }; 61 let (thread, process) = ProcMacroProcessSrv::run(process_path)?;
45 ProcMacroClient::Process { process: Arc::new(process) } 62 Ok(ProcMacroClient {
63 kind: ProcMacroClientKind::Process { process: Arc::new(process), thread },
64 })
46 } 65 }
47 66
48 pub fn dummy() -> ProcMacroClient { 67 pub fn dummy() -> ProcMacroClient {
49 ProcMacroClient::Dummy 68 ProcMacroClient { kind: ProcMacroClientKind::Dummy }
50 } 69 }
51 70
52 pub fn by_dylib_path( 71 pub fn by_dylib_path(
53 &self, 72 &self,
54 _dylib_path: &Path, 73 dylib_path: &Path,
55 ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { 74 ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> {
56 // FIXME: return empty for now 75 match &self.kind {
57 vec![] 76 ProcMacroClientKind::Dummy => vec![],
77 ProcMacroClientKind::Process { process, .. } => {
78 let macros = match process.find_proc_macros(dylib_path) {
79 Err(err) => {
80 eprintln!("Fail to find proc macro. Error: {:#?}", err);
81 return vec![];
82 }
83 Ok(macros) => macros,
84 };
85
86 macros
87 .into_iter()
88 .filter_map(|(name, kind)| {
89 // FIXME: Support custom derive only for now.
90 match kind {
91 ProcMacroKind::CustomDerive => {
92 let name = SmolStr::new(&name);
93 let expander: Arc<dyn ra_tt::TokenExpander> =
94 Arc::new(ProcMacroProcessExpander {
95 process: process.clone(),
96 name: name.clone(),
97 dylib_path: dylib_path.into(),
98 });
99 Some((name, expander))
100 }
101 _ => None,
102 }
103 })
104 .collect()
105 }
106 }
58 } 107 }
59} 108}
diff --git a/crates/ra_proc_macro/src/msg.rs b/crates/ra_proc_macro/src/msg.rs
new file mode 100644
index 000000000..aa95bcc8f
--- /dev/null
+++ b/crates/ra_proc_macro/src/msg.rs
@@ -0,0 +1,93 @@
1//! Defines messages for cross-process message based on `ndjson` wire protocol
2
3use std::{
4 convert::TryFrom,
5 io::{self, BufRead, Write},
6};
7
8use crate::{
9 rpc::{ListMacrosResult, ListMacrosTask},
10 ExpansionResult, ExpansionTask,
11};
12use serde::{de::DeserializeOwned, Deserialize, Serialize};
13
14#[derive(Debug, Serialize, Deserialize, Clone)]
15pub enum Request {
16 ListMacro(ListMacrosTask),
17 ExpansionMacro(ExpansionTask),
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone)]
21pub enum Response {
22 Error(ResponseError),
23 ListMacro(ListMacrosResult),
24 ExpansionMacro(ExpansionResult),
25}
26
27macro_rules! impl_try_from_response {
28 ($ty:ty, $tag:ident) => {
29 impl TryFrom<Response> for $ty {
30 type Error = &'static str;
31 fn try_from(value: Response) -> Result<Self, Self::Error> {
32 match value {
33 Response::$tag(res) => Ok(res),
34 _ => Err("Fail to convert from response"),
35 }
36 }
37 }
38 };
39}
40
41impl_try_from_response!(ListMacrosResult, ListMacro);
42impl_try_from_response!(ExpansionResult, ExpansionMacro);
43
44#[derive(Debug, Serialize, Deserialize, Clone)]
45pub struct ResponseError {
46 pub code: ErrorCode,
47 pub message: String,
48}
49
50#[derive(Debug, Serialize, Deserialize, Clone)]
51pub enum ErrorCode {
52 ServerErrorEnd,
53 ExpansionError,
54}
55
56pub trait Message: Sized + Serialize + DeserializeOwned {
57 fn read(r: &mut impl BufRead) -> io::Result<Option<Self>> {
58 let text = match read_json(r)? {
59 None => return Ok(None),
60 Some(text) => text,
61 };
62 let msg = serde_json::from_str(&text)?;
63 Ok(Some(msg))
64 }
65 fn write(self, w: &mut impl Write) -> io::Result<()> {
66 let text = serde_json::to_string(&self)?;
67 write_json(w, &text)
68 }
69}
70
71impl Message for Request {}
72impl Message for Response {}
73
74fn read_json(inp: &mut impl BufRead) -> io::Result<Option<String>> {
75 let mut buf = String::new();
76 if inp.read_line(&mut buf)? == 0 {
77 return Ok(None);
78 }
79 // Remove ending '\n'
80 let buf = &buf[..buf.len() - 1];
81 if buf.is_empty() {
82 return Ok(None);
83 }
84 Ok(Some(buf.to_string()))
85}
86
87fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
88 log::debug!("> {}", msg);
89 out.write_all(msg.as_bytes())?;
90 out.write_all(b"\n")?;
91 out.flush()?;
92 Ok(())
93}
diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs
new file mode 100644
index 000000000..e8c85be38
--- /dev/null
+++ b/crates/ra_proc_macro/src/process.rs
@@ -0,0 +1,196 @@
1//! Handle process life-time and message passing for proc-macro client
2
3use crossbeam_channel::{bounded, Receiver, Sender};
4use ra_tt::Subtree;
5
6use crate::msg::{ErrorCode, Message, Request, Response, ResponseError};
7use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind};
8
9use io::{BufRead, BufReader};
10use std::{
11 convert::{TryFrom, TryInto},
12 io::{self, Write},
13 path::{Path, PathBuf},
14 process::{Child, Command, Stdio},
15 sync::{Arc, Weak},
16};
17
18#[derive(Debug, Default)]
19pub(crate) struct ProcMacroProcessSrv {
20 inner: Option<Weak<Sender<Task>>>,
21}
22
23#[derive(Debug)]
24pub(crate) struct ProcMacroProcessThread {
25 // XXX: drop order is significant
26 sender: Arc<Sender<Task>>,
27 handle: jod_thread::JoinHandle<()>,
28}
29
30struct Task {
31 req: Request,
32 result_tx: Sender<Option<Response>>,
33}
34
35struct Process {
36 path: PathBuf,
37 child: Child,
38}
39
40impl Drop for Process {
41 fn drop(&mut self) {
42 let _ = self.child.kill();
43 }
44}
45
46impl Process {
47 fn run(process_path: &Path) -> Result<Process, io::Error> {
48 let child = Command::new(process_path.clone())
49 .stdin(Stdio::piped())
50 .stdout(Stdio::piped())
51 .stderr(Stdio::null())
52 .spawn()?;
53
54 Ok(Process { path: process_path.into(), child })
55 }
56
57 fn restart(&mut self) -> Result<(), io::Error> {
58 let _ = self.child.kill();
59 self.child = Command::new(self.path.clone())
60 .stdin(Stdio::piped())
61 .stdout(Stdio::piped())
62 .stderr(Stdio::null())
63 .spawn()?;
64 Ok(())
65 }
66
67 fn stdio(&mut self) -> Option<(impl Write, impl BufRead)> {
68 let stdin = self.child.stdin.take()?;
69 let stdout = self.child.stdout.take()?;
70 let read = BufReader::new(stdout);
71
72 Some((stdin, read))
73 }
74}
75
76impl ProcMacroProcessSrv {
77 pub fn run(
78 process_path: &Path,
79 ) -> Result<(ProcMacroProcessThread, ProcMacroProcessSrv), io::Error> {
80 let process = Process::run(process_path)?;
81
82 let (task_tx, task_rx) = bounded(0);
83 let handle = jod_thread::spawn(move || {
84 client_loop(task_rx, process);
85 });
86
87 let task_tx = Arc::new(task_tx);
88 let srv = ProcMacroProcessSrv { inner: Some(Arc::downgrade(&task_tx)) };
89 let thread = ProcMacroProcessThread { handle, sender: task_tx };
90
91 Ok((thread, srv))
92 }
93
94 pub fn find_proc_macros(
95 &self,
96 dylib_path: &Path,
97 ) -> Result<Vec<(String, ProcMacroKind)>, ra_tt::ExpansionError> {
98 let task = ListMacrosTask { lib: dylib_path.to_path_buf() };
99
100 let result: ListMacrosResult = self.send_task(Request::ListMacro(task))?;
101 Ok(result.macros)
102 }
103
104 pub fn custom_derive(
105 &self,
106 dylib_path: &Path,
107 subtree: &Subtree,
108 derive_name: &str,
109 ) -> Result<Subtree, ra_tt::ExpansionError> {
110 let task = ExpansionTask {
111 macro_body: subtree.clone(),
112 macro_name: derive_name.to_string(),
113 attributes: None,
114 lib: dylib_path.to_path_buf(),
115 };
116
117 let result: ExpansionResult = self.send_task(Request::ExpansionMacro(task))?;
118 Ok(result.expansion)
119 }
120
121 pub fn send_task<R>(&self, req: Request) -> Result<R, ra_tt::ExpansionError>
122 where
123 R: TryFrom<Response, Error = &'static str>,
124 {
125 let sender = match &self.inner {
126 None => return Err(ra_tt::ExpansionError::Unknown("No sender is found.".to_string())),
127 Some(it) => it,
128 };
129
130 let (result_tx, result_rx) = bounded(0);
131 let sender = match sender.upgrade() {
132 None => {
133 return Err(ra_tt::ExpansionError::Unknown("Proc macro process is closed.".into()))
134 }
135 Some(it) => it,
136 };
137 sender.send(Task { req: req.into(), result_tx }).unwrap();
138 let res = result_rx
139 .recv()
140 .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?;
141
142 match res {
143 Some(Response::Error(err)) => {
144 return Err(ra_tt::ExpansionError::ExpansionError(err.message));
145 }
146 Some(res) => Ok(res.try_into().map_err(|err| {
147 ra_tt::ExpansionError::Unknown(format!(
148 "Fail to get response, reason : {:#?} ",
149 err
150 ))
151 })?),
152 None => Err(ra_tt::ExpansionError::Unknown("Empty result".into())),
153 }
154 }
155}
156
157fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
158 let (mut stdin, mut stdout) = match process.stdio() {
159 None => return,
160 Some(it) => it,
161 };
162
163 for task in task_rx {
164 let Task { req, result_tx } = task;
165
166 match send_request(&mut stdin, &mut stdout, req) {
167 Ok(res) => result_tx.send(res).unwrap(),
168 Err(_err) => {
169 let res = Response::Error(ResponseError {
170 code: ErrorCode::ServerErrorEnd,
171 message: "Server closed".into(),
172 });
173 result_tx.send(res.into()).unwrap();
174 // Restart the process
175 if process.restart().is_err() {
176 break;
177 }
178 let stdio = match process.stdio() {
179 None => break,
180 Some(it) => it,
181 };
182 stdin = stdio.0;
183 stdout = stdio.1;
184 }
185 }
186 }
187}
188
189fn send_request(
190 mut writer: &mut impl Write,
191 mut reader: &mut impl BufRead,
192 req: Request,
193) -> Result<Option<Response>, io::Error> {
194 req.write(&mut writer)?;
195 Ok(Response::read(&mut reader)?)
196}
diff --git a/crates/ra_proc_macro/src/rpc.rs b/crates/ra_proc_macro/src/rpc.rs
new file mode 100644
index 000000000..66b3f55db
--- /dev/null
+++ b/crates/ra_proc_macro/src/rpc.rs
@@ -0,0 +1,266 @@
1//! Data struture serialization related stuffs for RPC
2//!
3//! Define all necessary rpc serialization data structure,
4//! which include ra_tt related data and some task messages.
5//! Although adding Serialize and Deserialize trait to ra_tt directly seem to be much easier,
6//! we deliberately duplicate the ra_tt struct with #[serde(with = "XXDef")]
7//! for separation of code responsibility.
8
9use ra_tt::{
10 Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId,
11 TokenTree,
12};
13use serde::{Deserialize, Serialize};
14use std::path::PathBuf;
15
16#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
17pub struct ListMacrosTask {
18 pub lib: PathBuf,
19}
20
21#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
22pub enum ProcMacroKind {
23 CustomDerive,
24 FuncLike,
25 Attr,
26}
27
28#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
29pub struct ListMacrosResult {
30 pub macros: Vec<(String, ProcMacroKind)>,
31}
32
33#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
34pub struct ExpansionTask {
35 /// Argument of macro call.
36 ///
37 /// In custom derive that would be a struct or enum; in attribute-like macro - underlying
38 /// item; in function-like macro - the macro body.
39 #[serde(with = "SubtreeDef")]
40 pub macro_body: Subtree,
41
42 /// Names of macros to expand.
43 ///
44 /// In custom derive those are names of derived traits (`Serialize`, `Getters`, etc.). In
45 /// attribute-like and functiona-like macros - single name of macro itself (`show_streams`).
46 pub macro_name: String,
47
48 /// Possible attributes for the attribute-like macros.
49 #[serde(with = "opt_subtree_def")]
50 pub attributes: Option<Subtree>,
51
52 pub lib: PathBuf,
53}
54
55#[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)]
56pub struct ExpansionResult {
57 #[serde(with = "SubtreeDef")]
58 pub expansion: Subtree,
59}
60
61#[derive(Serialize, Deserialize)]
62#[serde(remote = "DelimiterKind")]
63enum DelimiterKindDef {
64 Parenthesis,
65 Brace,
66 Bracket,
67}
68
69#[derive(Serialize, Deserialize)]
70#[serde(remote = "TokenId")]
71struct TokenIdDef(u32);
72
73#[derive(Serialize, Deserialize)]
74#[serde(remote = "Delimiter")]
75struct DelimiterDef {
76 #[serde(with = "TokenIdDef")]
77 pub id: TokenId,
78 #[serde(with = "DelimiterKindDef")]
79 pub kind: DelimiterKind,
80}
81
82#[derive(Serialize, Deserialize)]
83#[serde(remote = "Subtree")]
84struct SubtreeDef {
85 #[serde(default, with = "opt_delimiter_def")]
86 pub delimiter: Option<Delimiter>,
87 #[serde(with = "vec_token_tree")]
88 pub token_trees: Vec<TokenTree>,
89}
90
91#[derive(Serialize, Deserialize)]
92#[serde(remote = "TokenTree")]
93enum TokenTreeDef {
94 #[serde(with = "LeafDef")]
95 Leaf(Leaf),
96 #[serde(with = "SubtreeDef")]
97 Subtree(Subtree),
98}
99
100#[derive(Serialize, Deserialize)]
101#[serde(remote = "Leaf")]
102enum LeafDef {
103 #[serde(with = "LiteralDef")]
104 Literal(Literal),
105 #[serde(with = "PunctDef")]
106 Punct(Punct),
107 #[serde(with = "IdentDef")]
108 Ident(Ident),
109}
110
111#[derive(Serialize, Deserialize)]
112#[serde(remote = "Literal")]
113struct LiteralDef {
114 pub text: SmolStr,
115 #[serde(with = "TokenIdDef")]
116 pub id: TokenId,
117}
118
119#[derive(Serialize, Deserialize)]
120#[serde(remote = "Punct")]
121struct PunctDef {
122 pub char: char,
123 #[serde(with = "SpacingDef")]
124 pub spacing: Spacing,
125 #[serde(with = "TokenIdDef")]
126 pub id: TokenId,
127}
128
129#[derive(Serialize, Deserialize)]
130#[serde(remote = "Spacing")]
131enum SpacingDef {
132 Alone,
133 Joint,
134}
135
136#[derive(Serialize, Deserialize)]
137#[serde(remote = "Ident")]
138struct IdentDef {
139 pub text: SmolStr,
140 #[serde(with = "TokenIdDef")]
141 pub id: TokenId,
142}
143
144mod opt_delimiter_def {
145 use super::{Delimiter, DelimiterDef};
146 use serde::{Deserialize, Deserializer, Serialize, Serializer};
147
148 pub fn serialize<S>(value: &Option<Delimiter>, serializer: S) -> Result<S::Ok, S::Error>
149 where
150 S: Serializer,
151 {
152 #[derive(Serialize)]
153 struct Helper<'a>(#[serde(with = "DelimiterDef")] &'a Delimiter);
154 value.as_ref().map(Helper).serialize(serializer)
155 }
156
157 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Delimiter>, D::Error>
158 where
159 D: Deserializer<'de>,
160 {
161 #[derive(Deserialize)]
162 struct Helper(#[serde(with = "DelimiterDef")] Delimiter);
163 let helper = Option::deserialize(deserializer)?;
164 Ok(helper.map(|Helper(external)| external))
165 }
166}
167
168mod opt_subtree_def {
169 use super::{Subtree, SubtreeDef};
170 use serde::{Deserialize, Deserializer, Serialize, Serializer};
171
172 pub fn serialize<S>(value: &Option<Subtree>, serializer: S) -> Result<S::Ok, S::Error>
173 where
174 S: Serializer,
175 {
176 #[derive(Serialize)]
177 struct Helper<'a>(#[serde(with = "SubtreeDef")] &'a Subtree);
178 value.as_ref().map(Helper).serialize(serializer)
179 }
180
181 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Subtree>, D::Error>
182 where
183 D: Deserializer<'de>,
184 {
185 #[derive(Deserialize)]
186 struct Helper(#[serde(with = "SubtreeDef")] Subtree);
187 let helper = Option::deserialize(deserializer)?;
188 Ok(helper.map(|Helper(external)| external))
189 }
190}
191
192mod vec_token_tree {
193 use super::{TokenTree, TokenTreeDef};
194 use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
195
196 pub fn serialize<S>(value: &Vec<TokenTree>, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: Serializer,
199 {
200 #[derive(Serialize)]
201 struct Helper<'a>(#[serde(with = "TokenTreeDef")] &'a TokenTree);
202
203 let items: Vec<_> = value.iter().map(Helper).collect();
204 let mut seq = serializer.serialize_seq(Some(items.len()))?;
205 for element in items {
206 seq.serialize_element(&element)?;
207 }
208 seq.end()
209 }
210
211 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<TokenTree>, D::Error>
212 where
213 D: Deserializer<'de>,
214 {
215 #[derive(Deserialize)]
216 struct Helper(#[serde(with = "TokenTreeDef")] TokenTree);
217
218 let helper = Vec::deserialize(deserializer)?;
219 Ok(helper.into_iter().map(|Helper(external)| external).collect())
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226
227 fn fixture_token_tree() -> Subtree {
228 let mut subtree = Subtree::default();
229 subtree
230 .token_trees
231 .push(TokenTree::Leaf(Ident { text: "struct".into(), id: TokenId(0) }.into()));
232 subtree
233 .token_trees
234 .push(TokenTree::Leaf(Ident { text: "Foo".into(), id: TokenId(1) }.into()));
235 subtree.token_trees.push(TokenTree::Subtree(
236 Subtree {
237 delimiter: Some(Delimiter { id: TokenId(2), kind: DelimiterKind::Brace }),
238 token_trees: vec![],
239 }
240 .into(),
241 ));
242 subtree
243 }
244
245 #[test]
246 fn test_proc_macro_rpc_works() {
247 let tt = fixture_token_tree();
248 let task = ExpansionTask {
249 macro_body: tt.clone(),
250 macro_name: Default::default(),
251 attributes: None,
252 lib: Default::default(),
253 };
254
255 let json = serde_json::to_string(&task).unwrap();
256 let back: ExpansionTask = serde_json::from_str(&json).unwrap();
257
258 assert_eq!(task.macro_body, back.macro_body);
259
260 let result = ExpansionResult { expansion: tt.clone() };
261 let json = serde_json::to_string(&result).unwrap();
262 let back: ExpansionResult = serde_json::from_str(&json).unwrap();
263
264 assert_eq!(result, back);
265 }
266}
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
index cdcdd63c9..b10644b4b 100644
--- a/crates/ra_project_model/Cargo.toml
+++ b/crates/ra_project_model/Cargo.toml
@@ -16,7 +16,6 @@ cargo_metadata = "0.9.1"
16ra_arena = { path = "../ra_arena" } 16ra_arena = { path = "../ra_arena" }
17ra_db = { path = "../ra_db" } 17ra_db = { path = "../ra_db" }
18ra_cfg = { path = "../ra_cfg" } 18ra_cfg = { path = "../ra_cfg" }
19ra_cargo_watch = { path = "../ra_cargo_watch" }
20ra_proc_macro = { path = "../ra_proc_macro" } 19ra_proc_macro = { path = "../ra_proc_macro" }
21 20
22serde = { version = "1.0.104", features = ["derive"] } 21serde = { version = "1.0.104", features = ["derive"] }
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 291594e2a..f4fd6ad28 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,14 +1,16 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{
4 env,
5 ffi::OsStr,
4 ops, 6 ops,
5 path::{Path, PathBuf}, 7 path::{Path, PathBuf},
8 process::Command,
6}; 9};
7 10
8use anyhow::{Context, Result}; 11use anyhow::{Context, Result};
9use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; 12use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
10use ra_arena::{Arena, Idx}; 13use ra_arena::{Arena, Idx};
11use ra_cargo_watch::run_cargo;
12use ra_db::Edition; 14use ra_db::Edition;
13use rustc_hash::FxHashMap; 15use rustc_hash::FxHashMap;
14use serde::Deserialize; 16use serde::Deserialize;
@@ -75,6 +77,7 @@ pub type Target = Idx<TargetData>;
75 77
76#[derive(Debug, Clone)] 78#[derive(Debug, Clone)]
77pub struct PackageData { 79pub struct PackageData {
80 pub id: String,
78 pub name: String, 81 pub name: String,
79 pub manifest: PathBuf, 82 pub manifest: PathBuf,
80 pub targets: Vec<Target>, 83 pub targets: Vec<Target>,
@@ -161,7 +164,7 @@ impl CargoWorkspace {
161 let mut out_dir_by_id = FxHashMap::default(); 164 let mut out_dir_by_id = FxHashMap::default();
162 let mut proc_macro_dylib_paths = FxHashMap::default(); 165 let mut proc_macro_dylib_paths = FxHashMap::default();
163 if cargo_features.load_out_dirs_from_check { 166 if cargo_features.load_out_dirs_from_check {
164 let resources = load_extern_resources(cargo_toml, cargo_features); 167 let resources = load_extern_resources(cargo_toml, cargo_features)?;
165 out_dir_by_id = resources.out_dirs; 168 out_dir_by_id = resources.out_dirs;
166 proc_macro_dylib_paths = resources.proc_dylib_paths; 169 proc_macro_dylib_paths = resources.proc_dylib_paths;
167 } 170 }
@@ -180,6 +183,7 @@ impl CargoWorkspace {
180 .with_context(|| format!("Failed to parse edition {}", edition))?; 183 .with_context(|| format!("Failed to parse edition {}", edition))?;
181 let pkg = packages.alloc(PackageData { 184 let pkg = packages.alloc(PackageData {
182 name, 185 name,
186 id: id.to_string(),
183 manifest: manifest_path, 187 manifest: manifest_path,
184 targets: Vec::new(), 188 targets: Vec::new(),
185 is_member, 189 is_member,
@@ -249,6 +253,18 @@ impl CargoWorkspace {
249 pub fn workspace_root(&self) -> &Path { 253 pub fn workspace_root(&self) -> &Path {
250 &self.workspace_root 254 &self.workspace_root
251 } 255 }
256
257 pub fn package_flag(&self, package: &PackageData) -> String {
258 if self.is_unique(&*package.name) {
259 package.name.clone()
260 } else {
261 package.id.clone()
262 }
263 }
264
265 fn is_unique(&self, name: &str) -> bool {
266 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
267 }
252} 268}
253 269
254#[derive(Debug, Clone, Default)] 270#[derive(Debug, Clone, Default)]
@@ -257,48 +273,61 @@ pub struct ExternResources {
257 proc_dylib_paths: FxHashMap<PackageId, PathBuf>, 273 proc_dylib_paths: FxHashMap<PackageId, PathBuf>,
258} 274}
259 275
260pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { 276pub fn load_extern_resources(
261 let mut args: Vec<String> = vec![ 277 cargo_toml: &Path,
262 "check".to_string(), 278 cargo_features: &CargoFeatures,
263 "--message-format=json".to_string(), 279) -> Result<ExternResources> {
264 "--manifest-path".to_string(), 280 let mut cmd = Command::new(cargo_binary());
265 cargo_toml.display().to_string(), 281 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
266 ];
267
268 if cargo_features.all_features { 282 if cargo_features.all_features {
269 args.push("--all-features".to_string()); 283 cmd.arg("--all-features");
270 } else if cargo_features.no_default_features { 284 } else if cargo_features.no_default_features {
271 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures` 285 // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
272 // https://github.com/oli-obk/cargo_metadata/issues/79 286 // https://github.com/oli-obk/cargo_metadata/issues/79
273 args.push("--no-default-features".to_string()); 287 cmd.arg("--no-default-features");
274 } else { 288 } else {
275 args.extend(cargo_features.features.iter().cloned()); 289 cmd.args(&cargo_features.features);
276 } 290 }
277 291
278 let mut acc = ExternResources::default(); 292 let output = cmd.output()?;
279 let res = run_cargo(&args, cargo_toml.parent(), &mut |message| {
280 match message {
281 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
282 acc.out_dirs.insert(package_id, out_dir);
283 }
284 293
285 Message::CompilerArtifact(message) => { 294 let mut res = ExternResources::default();
286 if message.target.kind.contains(&"proc-macro".to_string()) { 295
287 let package_id = message.package_id; 296 let stdout = String::from_utf8(output.stdout)?;
288 if let Some(filename) = message.filenames.get(0) { 297 for line in stdout.lines() {
289 acc.proc_dylib_paths.insert(package_id, filename.clone()); 298 if let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(&line) {
299 match message {
300 Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => {
301 res.out_dirs.insert(package_id, out_dir);
302 }
303
304 Message::CompilerArtifact(message) => {
305 if message.target.kind.contains(&"proc-macro".to_string()) {
306 let package_id = message.package_id;
307 // Skip rmeta file
308 if let Some(filename) =
309 message.filenames.iter().filter(|name| is_dylib(name)).next()
310 {
311 res.proc_dylib_paths.insert(package_id, filename.clone());
312 }
290 } 313 }
291 } 314 }
315 Message::CompilerMessage(_) => (),
316 Message::Unknown => (),
292 } 317 }
293 Message::CompilerMessage(_) => (),
294 Message::Unknown => (),
295 } 318 }
296 true 319 }
297 }); 320 Ok(res)
321}
298 322
299 if let Err(err) = res { 323// FIXME: File a better way to know if it is a dylib
300 log::error!("Failed to load outdirs: {:?}", err); 324fn is_dylib(path: &Path) -> bool {
325 match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
326 None => false,
327 Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
301 } 328 }
329}
302 330
303 acc 331fn cargo_binary() -> String {
332 env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
304} 333}
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 3d023f189..b69cae234 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -369,22 +369,33 @@ impl ast::MatchArmList {
369 369
370 #[must_use] 370 #[must_use]
371 pub fn remove_placeholder(&self) -> ast::MatchArmList { 371 pub fn remove_placeholder(&self) -> ast::MatchArmList {
372 let placeholder = self.arms().find(|arm| { 372 let placeholder =
373 if let Some(ast::Pat::PlaceholderPat(_)) = arm.pat() { 373 self.arms().find(|arm| matches!(arm.pat(), Some(ast::Pat::PlaceholderPat(_))));
374 return true;
375 }
376 false
377 });
378 if let Some(placeholder) = placeholder { 374 if let Some(placeholder) = placeholder {
379 let s: SyntaxElement = placeholder.syntax().clone().into(); 375 self.remove_arm(&placeholder)
380 let e = s.clone();
381 self.replace_children(s..=e, &mut iter::empty())
382 } else { 376 } else {
383 self.clone() 377 self.clone()
384 } 378 }
385 } 379 }
386 380
387 #[must_use] 381 #[must_use]
382 fn remove_arm(&self, arm: &ast::MatchArm) -> ast::MatchArmList {
383 let start = arm.syntax().clone();
384 let end = if let Some(comma) = start
385 .siblings_with_tokens(Direction::Next)
386 .skip(1)
387 .skip_while(|it| it.kind().is_trivia())
388 .next()
389 .filter(|it| it.kind() == T![,])
390 {
391 comma
392 } else {
393 start.clone().into()
394 };
395 self.replace_children(start.into()..=end, None)
396 }
397
398 #[must_use]
388 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList { 399 pub fn append_arm(&self, item: ast::MatchArm) -> ast::MatchArmList {
389 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) { 400 let r_curly = match self.syntax().children_with_tokens().find(|it| it.kind() == T!['}']) {
390 Some(t) => t, 401 Some(t) => t,
diff --git a/crates/ra_syntax/src/parsing/text_tree_sink.rs b/crates/ra_syntax/src/parsing/text_tree_sink.rs
index dd202601d..87bb21cd9 100644
--- a/crates/ra_syntax/src/parsing/text_tree_sink.rs
+++ b/crates/ra_syntax/src/parsing/text_tree_sink.rs
@@ -149,10 +149,21 @@ fn n_attached_trivias<'a>(
149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF 149 MACRO_CALL | CONST_DEF | TYPE_ALIAS_DEF | STRUCT_DEF | ENUM_DEF | ENUM_VARIANT | FN_DEF
150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => { 150 | TRAIT_DEF | MODULE | RECORD_FIELD_DEF | STATIC_DEF => {
151 let mut res = 0; 151 let mut res = 0;
152 for (i, (kind, text)) in trivias.enumerate() { 152 let mut trivias = trivias.enumerate().peekable();
153
154 while let Some((i, (kind, text))) = trivias.next() {
153 match kind { 155 match kind {
154 WHITESPACE => { 156 WHITESPACE => {
155 if text.contains("\n\n") { 157 if text.contains("\n\n") {
158 // we check whether the next token is a doc-comment
159 // and skip the whitespace in this case
160 if let Some((peek_kind, peek_text)) =
161 trivias.peek().map(|(_, pair)| pair)
162 {
163 if *peek_kind == COMMENT && peek_text.starts_with("///") {
164 continue;
165 }
166 }
156 break; 167 break;
157 } 168 }
158 } 169 }
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
new file mode 100644
index 000000000..1fafe216b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.rs
@@ -0,0 +1,3 @@
1/// Example
2
3fn test() {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
new file mode 100644
index 000000000..91d0c3736
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0065_comment_newline.txt
@@ -0,0 +1,17 @@
1SOURCE_FILE@[0; 26)
2 FN_DEF@[0; 25)
3 COMMENT@[0; 11) "/// Example"
4 WHITESPACE@[11; 13) "\n\n"
5 FN_KW@[13; 15) "fn"
6 WHITESPACE@[15; 16) " "
7 NAME@[16; 20)
8 IDENT@[16; 20) "test"
9 PARAM_LIST@[20; 22)
10 L_PAREN@[20; 21) "("
11 R_PAREN@[21; 22) ")"
12 WHITESPACE@[22; 23) " "
13 BLOCK_EXPR@[23; 25)
14 BLOCK@[23; 25)
15 L_CURLY@[23; 24) "{"
16 R_CURLY@[24; 25) "}"
17 WHITESPACE@[25; 26) "\n"
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs
index 1015ce0a6..bd484aa30 100644
--- a/crates/ra_tt/src/lib.rs
+++ b/crates/ra_tt/src/lib.rs
@@ -189,7 +189,12 @@ impl Subtree {
189pub mod buffer; 189pub mod buffer;
190 190
191#[derive(Debug, PartialEq, Eq)] 191#[derive(Debug, PartialEq, Eq)]
192pub enum ExpansionError {} 192pub enum ExpansionError {
193 IOError(String),
194 JsonError(String),
195 Unknown(String),
196 ExpansionError(String),
197}
193 198
194pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { 199pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
195 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) 200 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8fe6799d2..f5f773432 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -33,7 +33,7 @@ threadpool = "1.7.1"
33stdx = { path = "../stdx" } 33stdx = { path = "../stdx" }
34 34
35lsp-server = "0.3.1" 35lsp-server = "0.3.1"
36ra_cargo_watch = { path = "../ra_cargo_watch" } 36ra_flycheck = { path = "../ra_flycheck" }
37ra_ide = { path = "../ra_ide" } 37ra_ide = { path = "../ra_ide" }
38ra_prof = { path = "../ra_prof" } 38ra_prof = { path = "../ra_prof" }
39ra_project_model = { path = "../ra_project_model" } 39ra_project_model = { path = "../ra_project_model" }
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index f87bdcec5..942c30328 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -77,7 +77,7 @@ impl CargoTargetSpec {
77 ProjectWorkspace::Cargo { cargo, .. } => { 77 ProjectWorkspace::Cargo { cargo, .. } => {
78 let tgt = cargo.target_by_root(&path)?; 78 let tgt = cargo.target_by_root(&path)?;
79 Some(CargoTargetSpec { 79 Some(CargoTargetSpec {
80 package: cargo[cargo[tgt].package].name.clone(), 80 package: cargo.package_flag(&cargo[cargo[tgt].package]),
81 target: cargo[tgt].name.clone(), 81 target: cargo[tgt].name.clone(),
82 target_kind: cargo[tgt].kind, 82 target_kind: cargo[tgt].kind,
83 }) 83 })
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs
index 7164b0ade..7667873d5 100644
--- a/crates/rust-analyzer/src/cli/analysis_bench.rs
+++ b/crates/rust-analyzer/src/cli/analysis_bench.rs
@@ -12,7 +12,7 @@ use ra_db::{
12 salsa::{Database, Durability}, 12 salsa::{Database, Durability},
13 FileId, SourceDatabaseExt, 13 FileId, SourceDatabaseExt,
14}; 14};
15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol}; 15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol};
16 16
17use crate::cli::{load_cargo::load_cargo, Verbosity}; 17use crate::cli::{load_cargo::load_cargo, Verbosity};
18 18
@@ -102,7 +102,7 @@ pub fn analysis_bench(
102 let file_position = FilePosition { file_id, offset }; 102 let file_position = FilePosition { file_id, offset };
103 103
104 if is_completion { 104 if is_completion {
105 let options = CompletionOptions::default(); 105 let options = CompletionConfig::default();
106 let res = do_work(&mut host, file_id, |analysis| { 106 let res = do_work(&mut host, file_id, |analysis| {
107 analysis.completions(file_position, &options) 107 analysis.completions(file_position, &options)
108 }); 108 });
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index 6edc03fe0..e8dc953c3 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -579,7 +579,7 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>)
579 .into_iter() 579 .into_iter()
580 .map(|nav| (file_id, RangeInfo::new(range, nav))) 580 .map(|nav| (file_id, RangeInfo::new(range, nav)))
581 .try_conv_with_to_vec(world)?; 581 .try_conv_with_to_vec(world)?;
582 if world.options.supports_location_link { 582 if world.config.supports_location_link {
583 Ok(links.into()) 583 Ok(links.into())
584 } else { 584 } else {
585 let locations: Vec<Location> = links 585 let locations: Vec<Location> = links
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 79ea90cc9..bb33fb27d 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -21,8 +21,8 @@ use lsp_types::{
21 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, 21 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
22 WorkDoneProgressReport, 22 WorkDoneProgressReport,
23}; 23};
24use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; 24use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask};
25use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; 25use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId};
26use ra_prof::profile; 26use ra_prof::profile;
27use ra_vfs::{VfsFile, VfsTask, Watch}; 27use ra_vfs::{VfsFile, VfsTask, Watch};
28use relative_path::RelativePathBuf; 28use relative_path::RelativePathBuf;
@@ -38,7 +38,7 @@ use crate::{
38 subscriptions::Subscriptions, 38 subscriptions::Subscriptions,
39 }, 39 },
40 req, 40 req,
41 world::{Options, WorldSnapshot, WorldState}, 41 world::{Config, WorldSnapshot, WorldState},
42 Result, ServerConfig, 42 Result, ServerConfig,
43}; 43};
44use req::ConfigurationParams; 44use req::ConfigurationParams;
@@ -81,11 +81,11 @@ fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureF
81 ff 81 ff
82} 82}
83 83
84fn get_options( 84fn get_config(
85 config: &ServerConfig, 85 config: &ServerConfig,
86 text_document_caps: Option<&TextDocumentClientCapabilities>, 86 text_document_caps: Option<&TextDocumentClientCapabilities>,
87) -> Options { 87) -> Config {
88 Options { 88 Config {
89 publish_decorations: config.publish_decorations, 89 publish_decorations: config.publish_decorations,
90 supports_location_link: text_document_caps 90 supports_location_link: text_document_caps
91 .and_then(|it| it.definition) 91 .and_then(|it| it.definition)
@@ -95,13 +95,13 @@ fn get_options(
95 .and_then(|it| it.folding_range.as_ref()) 95 .and_then(|it| it.folding_range.as_ref())
96 .and_then(|it| it.line_folding_only) 96 .and_then(|it| it.line_folding_only)
97 .unwrap_or(false), 97 .unwrap_or(false),
98 inlay_hints: InlayHintsOptions { 98 inlay_hints: InlayHintsConfig {
99 type_hints: config.inlay_hints_type, 99 type_hints: config.inlay_hints_type,
100 parameter_hints: config.inlay_hints_parameter, 100 parameter_hints: config.inlay_hints_parameter,
101 chaining_hints: config.inlay_hints_chaining, 101 chaining_hints: config.inlay_hints_chaining,
102 max_length: config.inlay_hints_max_length, 102 max_length: config.inlay_hints_max_length,
103 }, 103 },
104 cargo_watch: CheckOptions { 104 check: CheckConfig {
105 enable: config.cargo_watch_enable, 105 enable: config.cargo_watch_enable,
106 args: config.cargo_watch_args.clone(), 106 args: config.cargo_watch_args.clone(),
107 command: config.cargo_watch_command.clone(), 107 command: config.cargo_watch_command.clone(),
@@ -109,6 +109,7 @@ fn get_options(
109 }, 109 },
110 rustfmt_args: config.rustfmt_args.clone(), 110 rustfmt_args: config.rustfmt_args.clone(),
111 vscode_lldb: config.vscode_lldb, 111 vscode_lldb: config.vscode_lldb,
112 proc_macro_srv: None, // FIXME: get this from config
112 } 113 }
113} 114}
114 115
@@ -210,7 +211,7 @@ pub fn main_loop(
210 config.lru_capacity, 211 config.lru_capacity,
211 &globs, 212 &globs,
212 Watch(!config.use_client_watching), 213 Watch(!config.use_client_watching),
213 get_options(&config, text_document_caps), 214 get_config(&config, text_document_caps),
214 feature_flags, 215 feature_flags,
215 ) 216 )
216 }; 217 };
@@ -435,7 +436,7 @@ fn loop_turn(
435 .to_owned(); 436 .to_owned();
436 world_state.update_configuration( 437 world_state.update_configuration(
437 new_config.lru_capacity, 438 new_config.lru_capacity,
438 get_options(&new_config, text_document_caps), 439 get_config(&new_config, text_document_caps),
439 get_feature_flags(&new_config, connection), 440 get_feature_flags(&new_config, connection),
440 ); 441 );
441 } 442 }
@@ -498,7 +499,7 @@ fn loop_turn(
498 update_file_notifications_on_threadpool( 499 update_file_notifications_on_threadpool(
499 pool, 500 pool,
500 world_state.snapshot(), 501 world_state.snapshot(),
501 world_state.options.publish_decorations, 502 world_state.config.publish_decorations,
502 task_sender.clone(), 503 task_sender.clone(),
503 loop_state.subscriptions.subscriptions(), 504 loop_state.subscriptions.subscriptions(),
504 ) 505 )
@@ -808,14 +809,14 @@ fn send_startup_progress(sender: &Sender<Message>, loop_state: &mut LoopState) {
808 ), 809 ),
809 _ => {} 810 _ => {}
810 } 811 }
811}
812 812
813fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) { 813 fn send_startup_progress_notif(sender: &Sender<Message>, work_done_progress: WorkDoneProgress) {
814 let notif = notification_new::<req::Progress>(req::ProgressParams { 814 let notif = notification_new::<req::Progress>(req::ProgressParams {
815 token: req::ProgressToken::String("rustAnalyzer/startup".into()), 815 token: req::ProgressToken::String("rustAnalyzer/startup".into()),
816 value: req::ProgressParamsValue::WorkDone(work_done_progress), 816 value: req::ProgressParamsValue::WorkDone(work_done_progress),
817 }); 817 });
818 sender.send(notif.into()).unwrap(); 818 sender.send(notif.into()).unwrap();
819 }
819} 820}
820 821
821struct PoolDispatcher<'a> { 822struct PoolDispatcher<'a> {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index f60a3f0a0..d5cb5d137 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -19,7 +19,7 @@ use lsp_types::{
19 TextEdit, WorkspaceEdit, 19 TextEdit, WorkspaceEdit,
20}; 20};
21use ra_ide::{ 21use ra_ide::{
22 Assist, AssistId, CompletionOptions, FileId, FilePosition, FileRange, Query, RangeInfo, 22 Assist, AssistId, CompletionConfig, FileId, FilePosition, FileRange, Query, RangeInfo,
23 Runnable, RunnableKind, SearchScope, 23 Runnable, RunnableKind, SearchScope,
24}; 24};
25use ra_prof::profile; 25use ra_prof::profile;
@@ -425,7 +425,7 @@ pub fn handle_completion(
425 return Ok(None); 425 return Ok(None);
426 } 426 }
427 427
428 let options = CompletionOptions { 428 let config = CompletionConfig {
429 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"), 429 enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"),
430 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"), 430 add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"),
431 add_call_argument_snippets: world 431 add_call_argument_snippets: world
@@ -433,7 +433,7 @@ pub fn handle_completion(
433 .get("completion.insertion.add-argument-snippets"), 433 .get("completion.insertion.add-argument-snippets"),
434 }; 434 };
435 435
436 let items = match world.analysis().completions(position, &options)? { 436 let items = match world.analysis().completions(position, &config)? {
437 None => return Ok(None), 437 None => return Ok(None),
438 Some(items) => items, 438 Some(items) => items,
439 }; 439 };
@@ -457,7 +457,7 @@ pub fn handle_folding_range(
457 let ctx = FoldConvCtx { 457 let ctx = FoldConvCtx {
458 text: &text, 458 text: &text,
459 line_index: &line_index, 459 line_index: &line_index,
460 line_folding_only: world.options.line_folding_only, 460 line_folding_only: world.config.line_folding_only,
461 }; 461 };
462 let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); 462 let res = Some(folds.into_iter().map_conv_with(&ctx).collect());
463 Ok(res) 463 Ok(res)
@@ -611,7 +611,7 @@ pub fn handle_formatting(
611 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); 611 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
612 612
613 let mut rustfmt = process::Command::new("rustfmt"); 613 let mut rustfmt = process::Command::new("rustfmt");
614 rustfmt.args(&world.options.rustfmt_args); 614 rustfmt.args(&world.config.rustfmt_args);
615 if let Some(&crate_id) = crate_ids.first() { 615 if let Some(&crate_id) = crate_ids.first() {
616 // Assume all crates are in the same edition 616 // Assume all crates are in the same edition
617 let edition = world.analysis().crate_edition(crate_id)?; 617 let edition = world.analysis().crate_edition(crate_id)?;
@@ -815,7 +815,7 @@ pub fn handle_code_lens(
815 }; 815 };
816 lenses.push(lens); 816 lenses.push(lens);
817 817
818 if world.options.vscode_lldb { 818 if world.config.vscode_lldb {
819 if r.args[0] == "run" { 819 if r.args[0] == "run" {
820 r.args[0] = "build".into(); 820 r.args[0] = "build".into();
821 } else { 821 } else {
@@ -1028,7 +1028,7 @@ pub fn handle_inlay_hints(
1028 let analysis = world.analysis(); 1028 let analysis = world.analysis();
1029 let line_index = analysis.file_line_index(file_id)?; 1029 let line_index = analysis.file_line_index(file_id)?;
1030 Ok(analysis 1030 Ok(analysis
1031 .inlay_hints(file_id, &world.options.inlay_hints)? 1031 .inlay_hints(file_id, &world.config.inlay_hints)?
1032 .into_iter() 1032 .into_iter()
1033 .map_conv_with(&line_index) 1033 .map_conv_with(&line_index)
1034 .collect()) 1034 .collect())
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index ad096a1d8..acb729bae 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -11,9 +11,9 @@ use std::{
11use crossbeam_channel::{unbounded, Receiver}; 11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; 14use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckWatcher};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData,
17 SourceRootId, 17 SourceRootId,
18}; 18};
19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; 19use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace};
@@ -31,7 +31,7 @@ use crate::{
31use ra_db::ExternSourceId; 31use ra_db::ExternSourceId;
32use rustc_hash::{FxHashMap, FxHashSet}; 32use rustc_hash::{FxHashMap, FxHashSet};
33 33
34fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> Option<CheckWatcher> { 34fn create_watcher(workspaces: &[ProjectWorkspace], config: &Config) -> Option<CheckWatcher> {
35 // FIXME: Figure out the multi-workspace situation 35 // FIXME: Figure out the multi-workspace situation
36 workspaces 36 workspaces
37 .iter() 37 .iter()
@@ -41,7 +41,7 @@ fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> Option<
41 }) 41 })
42 .map(|cargo| { 42 .map(|cargo| {
43 let cargo_project_root = cargo.workspace_root().to_path_buf(); 43 let cargo_project_root = cargo.workspace_root().to_path_buf();
44 Some(CheckWatcher::new(&options.cargo_watch, cargo_project_root)) 44 Some(CheckWatcher::new(config.check.clone(), cargo_project_root))
45 }) 45 })
46 .unwrap_or_else(|| { 46 .unwrap_or_else(|| {
47 log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); 47 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
@@ -50,14 +50,15 @@ fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> Option<
50} 50}
51 51
52#[derive(Debug, Clone)] 52#[derive(Debug, Clone)]
53pub struct Options { 53pub struct Config {
54 pub publish_decorations: bool, 54 pub publish_decorations: bool,
55 pub supports_location_link: bool, 55 pub supports_location_link: bool,
56 pub line_folding_only: bool, 56 pub line_folding_only: bool,
57 pub inlay_hints: InlayHintsOptions, 57 pub inlay_hints: InlayHintsConfig,
58 pub rustfmt_args: Vec<String>, 58 pub rustfmt_args: Vec<String>,
59 pub cargo_watch: CheckOptions, 59 pub check: CheckConfig,
60 pub vscode_lldb: bool, 60 pub vscode_lldb: bool,
61 pub proc_macro_srv: Option<String>,
61} 62}
62 63
63/// `WorldState` is the primary mutable state of the language server 64/// `WorldState` is the primary mutable state of the language server
@@ -67,7 +68,7 @@ pub struct Options {
67/// incremental salsa database. 68/// incremental salsa database.
68#[derive(Debug)] 69#[derive(Debug)]
69pub struct WorldState { 70pub struct WorldState {
70 pub options: Options, 71 pub config: Config,
71 pub feature_flags: Arc<FeatureFlags>, 72 pub feature_flags: Arc<FeatureFlags>,
72 pub roots: Vec<PathBuf>, 73 pub roots: Vec<PathBuf>,
73 pub workspaces: Arc<Vec<ProjectWorkspace>>, 74 pub workspaces: Arc<Vec<ProjectWorkspace>>,
@@ -81,7 +82,7 @@ pub struct WorldState {
81 82
82/// An immutable snapshot of the world's state at a point in time. 83/// An immutable snapshot of the world's state at a point in time.
83pub struct WorldSnapshot { 84pub struct WorldSnapshot {
84 pub options: Options, 85 pub config: Config,
85 pub feature_flags: Arc<FeatureFlags>, 86 pub feature_flags: Arc<FeatureFlags>,
86 pub workspaces: Arc<Vec<ProjectWorkspace>>, 87 pub workspaces: Arc<Vec<ProjectWorkspace>>,
87 pub analysis: Analysis, 88 pub analysis: Analysis,
@@ -97,7 +98,7 @@ impl WorldState {
97 lru_capacity: Option<usize>, 98 lru_capacity: Option<usize>,
98 exclude_globs: &[Glob], 99 exclude_globs: &[Glob],
99 watch: Watch, 100 watch: Watch,
100 options: Options, 101 config: Config,
101 feature_flags: FeatureFlags, 102 feature_flags: FeatureFlags,
102 ) -> WorldState { 103 ) -> WorldState {
103 let mut change = AnalysisChange::new(); 104 let mut change = AnalysisChange::new();
@@ -167,8 +168,23 @@ impl WorldState {
167 vfs_file.map(|f| FileId(f.0)) 168 vfs_file.map(|f| FileId(f.0))
168 }; 169 };
169 170
170 let proc_macro_client = 171 let proc_macro_client = match &config.proc_macro_srv {
171 ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv")); 172 None => ProcMacroClient::dummy(),
173 Some(srv) => {
174 let path = Path::new(&srv);
175 match ProcMacroClient::extern_process(path) {
176 Ok(it) => it,
177 Err(err) => {
178 log::error!(
179 "Fail to run ra_proc_macro_srv from path {}, error : {}",
180 path.to_string_lossy(),
181 err
182 );
183 ProcMacroClient::dummy()
184 }
185 }
186 }
187 };
172 188
173 workspaces 189 workspaces
174 .iter() 190 .iter()
@@ -185,12 +201,12 @@ impl WorldState {
185 }); 201 });
186 change.set_crate_graph(crate_graph); 202 change.set_crate_graph(crate_graph);
187 203
188 let check_watcher = create_watcher(&workspaces, &options); 204 let check_watcher = create_watcher(&workspaces, &config);
189 205
190 let mut analysis_host = AnalysisHost::new(lru_capacity); 206 let mut analysis_host = AnalysisHost::new(lru_capacity);
191 analysis_host.apply_change(change); 207 analysis_host.apply_change(change);
192 WorldState { 208 WorldState {
193 options, 209 config: config,
194 feature_flags: Arc::new(feature_flags), 210 feature_flags: Arc::new(feature_flags),
195 roots: folder_roots, 211 roots: folder_roots,
196 workspaces: Arc::new(workspaces), 212 workspaces: Arc::new(workspaces),
@@ -206,13 +222,13 @@ impl WorldState {
206 pub fn update_configuration( 222 pub fn update_configuration(
207 &mut self, 223 &mut self,
208 lru_capacity: Option<usize>, 224 lru_capacity: Option<usize>,
209 options: Options, 225 config: Config,
210 feature_flags: FeatureFlags, 226 feature_flags: FeatureFlags,
211 ) { 227 ) {
212 self.feature_flags = Arc::new(feature_flags); 228 self.feature_flags = Arc::new(feature_flags);
213 self.analysis_host.update_lru_capacity(lru_capacity); 229 self.analysis_host.update_lru_capacity(lru_capacity);
214 self.check_watcher = create_watcher(&self.workspaces, &options); 230 self.check_watcher = create_watcher(&self.workspaces, &config);
215 self.options = options; 231 self.config = config;
216 } 232 }
217 233
218 /// Returns a vec of libraries 234 /// Returns a vec of libraries
@@ -268,7 +284,7 @@ impl WorldState {
268 284
269 pub fn snapshot(&self) -> WorldSnapshot { 285 pub fn snapshot(&self) -> WorldSnapshot {
270 WorldSnapshot { 286 WorldSnapshot {
271 options: self.options.clone(), 287 config: self.config.clone(),
272 feature_flags: Arc::clone(&self.feature_flags), 288 feature_flags: Arc::clone(&self.feature_flags),
273 workspaces: Arc::clone(&self.workspaces), 289 workspaces: Arc::clone(&self.workspaces),
274 analysis: self.analysis_host.analysis(), 290 analysis: self.analysis_host.analysis(),
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 145429571..5af5eaad2 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -9,7 +9,7 @@ use lsp_types::{
9}; 9};
10use rust_analyzer::req::{ 10use rust_analyzer::req::{
11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument, 11 CodeActionParams, CodeActionRequest, Completion, CompletionParams, DidOpenTextDocument,
12 Formatting, OnEnter, Runnables, RunnablesParams, 12 Formatting, GotoDefinition, OnEnter, Runnables, RunnablesParams,
13}; 13};
14use serde_json::json; 14use serde_json::json;
15use tempfile::TempDir; 15use tempfile::TempDir;
@@ -581,3 +581,47 @@ version = \"0.0.0\"
581 }), 581 }),
582 ); 582 );
583} 583}
584
585#[test]
586fn resolve_include_concat_env() {
587 if skip_slow_tests() {
588 return;
589 }
590
591 let server = Project::with_fixture(
592 r###"
593//- Cargo.toml
594[package]
595name = "foo"
596version = "0.0.0"
597
598//- build.rs
599use std::{env, fs, path::Path};
600
601fn main() {
602 let out_dir = env::var_os("OUT_DIR").unwrap();
603 let dest_path = Path::new(&out_dir).join("hello.rs");
604 fs::write(
605 &dest_path,
606 r#"pub fn message() -> &'static str { "Hello, World!" }"#,
607 )
608 .unwrap();
609 println!("cargo:rerun-if-changed=build.rs");
610}
611//- src/main.rs
612include!(concat!(env!("OUT_DIR"), "/hello.rs"));
613
614fn main() { message(); }
615"###,
616 )
617 .with_config(|config| {
618 config.cargo_features.load_out_dirs_from_check = true;
619 })
620 .server();
621 server.wait_until_workspace_is_loaded();
622 let res = server.send_request::<GotoDefinition>(TextDocumentPositionParams::new(
623 server.doc_id("src/main.rs"),
624 Position::new(2, 15),
625 ));
626 assert!(format!("{}", res).contains("hello.rs"));
627}
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs
index 67f3c9332..d8bed6d7f 100644
--- a/crates/rust-analyzer/tests/heavy_tests/support.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/support.rs
@@ -27,11 +27,12 @@ pub struct Project<'a> {
27 with_sysroot: bool, 27 with_sysroot: bool,
28 tmp_dir: Option<TempDir>, 28 tmp_dir: Option<TempDir>,
29 roots: Vec<PathBuf>, 29 roots: Vec<PathBuf>,
30 config: Option<Box<dyn Fn(&mut ServerConfig)>>,
30} 31}
31 32
32impl<'a> Project<'a> { 33impl<'a> Project<'a> {
33 pub fn with_fixture(fixture: &str) -> Project { 34 pub fn with_fixture(fixture: &str) -> Project {
34 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false } 35 Project { fixture, tmp_dir: None, roots: vec![], with_sysroot: false, config: None }
35 } 36 }
36 37
37 pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> { 38 pub fn tmp_dir(mut self, tmp_dir: TempDir) -> Project<'a> {
@@ -49,6 +50,11 @@ impl<'a> Project<'a> {
49 self 50 self
50 } 51 }
51 52
53 pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> {
54 self.config = Some(Box::new(config));
55 self
56 }
57
52 pub fn server(self) -> Server { 58 pub fn server(self) -> Server {
53 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap()); 59 let tmp_dir = self.tmp_dir.unwrap_or_else(|| TempDir::new().unwrap());
54 static INIT: Once = Once::new(); 60 static INIT: Once = Once::new();
@@ -72,7 +78,14 @@ impl<'a> Project<'a> {
72 78
73 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); 79 let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect();
74 80
75 Server::new(tmp_dir, self.with_sysroot, roots, paths) 81 let mut config =
82 ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() };
83
84 if let Some(f) = &self.config {
85 f(&mut config)
86 }
87
88 Server::new(tmp_dir, config, roots, paths)
76 } 89 }
77} 90}
78 91
@@ -92,7 +105,7 @@ pub struct Server {
92impl Server { 105impl Server {
93 fn new( 106 fn new(
94 dir: TempDir, 107 dir: TempDir,
95 with_sysroot: bool, 108 config: ServerConfig,
96 roots: Vec<PathBuf>, 109 roots: Vec<PathBuf>,
97 files: Vec<(PathBuf, String)>, 110 files: Vec<(PathBuf, String)>,
98 ) -> Server { 111 ) -> Server {
@@ -118,7 +131,7 @@ impl Server {
118 window: None, 131 window: None,
119 experimental: None, 132 experimental: None,
120 }, 133 },
121 ServerConfig { with_sysroot, ..ServerConfig::default() }, 134 config,
122 connection, 135 connection,
123 ) 136 )
124 .unwrap() 137 .unwrap()
@@ -188,6 +201,7 @@ impl Server {
188 self.client.sender.send(r.into()).unwrap(); 201 self.client.sender.send(r.into()).unwrap();
189 while let Some(msg) = self.recv() { 202 while let Some(msg) = self.recv() {
190 match msg { 203 match msg {
204 Message::Request(req) if req.method == "window/workDoneProgress/create" => (),
191 Message::Request(req) => panic!("unexpected request: {:?}", req), 205 Message::Request(req) => panic!("unexpected request: {:?}", req),
192 Message::Notification(_) => (), 206 Message::Notification(_) => (),
193 Message::Response(res) => { 207 Message::Response(res) => {
diff --git a/docs/user/features.md b/docs/user/features.md
index 56d2969fd..8aeec2e81 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -81,6 +81,12 @@ Join selected lines into one, smartly fixing up whitespace and trailing commas.
81Shows the parse tree of the current file. It exists mostly for debugging 81Shows the parse tree of the current file. It exists mostly for debugging
82rust-analyzer itself. 82rust-analyzer itself.
83 83
84You can hover over syntax nodes in the opened text file to see the appropriate
85rust code that it refers to and the rust editor will also highlight the proper
86text range.
87
88<img src="https://user-images.githubusercontent.com/36276403/78043783-7425e180-737c-11ea-8653-b02b773c5aa1.png" alt="demo" height="200px" >
89
84#### Expand Macro Recursively 90#### Expand Macro Recursively
85 91
86Shows the full macro expansion of the macro at current cursor. 92Shows the full macro expansion of the macro at current cursor.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index b7220d8e2..955a05066 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -84,9 +84,9 @@
84 "dev": true 84 "dev": true
85 }, 85 },
86 "@types/node": { 86 "@types/node": {
87 "version": "12.12.30", 87 "version": "12.12.34",
88 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.30.tgz", 88 "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.34.tgz",
89 "integrity": "sha512-sz9MF/zk6qVr3pAnM0BSQvYIBK44tS75QC5N+VbWSE4DjCV/pJ+UzCW/F+vVnl7TkOPcuwQureKNtSSwjBTaMg==", 89 "integrity": "sha512-BneGN0J9ke24lBRn44hVHNeDlrXRYF+VRp0HbSUNnEZahXGAysHZIqnf/hER6aabdBgzM4YOV4jrR8gj4Zfi0g==",
90 "dev": true 90 "dev": true
91 }, 91 },
92 "@types/node-fetch": { 92 "@types/node-fetch": {
@@ -115,45 +115,56 @@
115 "dev": true 115 "dev": true
116 }, 116 },
117 "@typescript-eslint/eslint-plugin": { 117 "@typescript-eslint/eslint-plugin": {
118 "version": "2.24.0", 118 "version": "2.26.0",
119 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.24.0.tgz", 119 "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz",
120 "integrity": "sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA==", 120 "integrity": "sha512-4yUnLv40bzfzsXcTAtZyTjbiGUXMrcIJcIMioI22tSOyAxpdXiZ4r7YQUU8Jj6XXrLz9d5aMHPQf5JFR7h27Nw==",
121 "dev": true, 121 "dev": true,
122 "requires": { 122 "requires": {
123 "@typescript-eslint/experimental-utils": "2.24.0", 123 "@typescript-eslint/experimental-utils": "2.26.0",
124 "eslint-utils": "^1.4.3",
125 "functional-red-black-tree": "^1.0.1", 124 "functional-red-black-tree": "^1.0.1",
126 "regexpp": "^3.0.0", 125 "regexpp": "^3.0.0",
127 "tsutils": "^3.17.1" 126 "tsutils": "^3.17.1"
128 } 127 }
129 }, 128 },
130 "@typescript-eslint/experimental-utils": { 129 "@typescript-eslint/experimental-utils": {
131 "version": "2.24.0", 130 "version": "2.26.0",
132 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.24.0.tgz", 131 "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.26.0.tgz",
133 "integrity": "sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw==", 132 "integrity": "sha512-RELVoH5EYd+JlGprEyojUv9HeKcZqF7nZUGSblyAw1FwOGNnmQIU8kxJ69fttQvEwCsX5D6ECJT8GTozxrDKVQ==",
134 "dev": true, 133 "dev": true,
135 "requires": { 134 "requires": {
136 "@types/json-schema": "^7.0.3", 135 "@types/json-schema": "^7.0.3",
137 "@typescript-eslint/typescript-estree": "2.24.0", 136 "@typescript-eslint/typescript-estree": "2.26.0",
138 "eslint-scope": "^5.0.0" 137 "eslint-scope": "^5.0.0",
138 "eslint-utils": "^2.0.0"
139 },
140 "dependencies": {
141 "eslint-utils": {
142 "version": "2.0.0",
143 "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
144 "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
145 "dev": true,
146 "requires": {
147 "eslint-visitor-keys": "^1.1.0"
148 }
149 }
139 } 150 }
140 }, 151 },
141 "@typescript-eslint/parser": { 152 "@typescript-eslint/parser": {
142 "version": "2.24.0", 153 "version": "2.26.0",
143 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.24.0.tgz", 154 "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.26.0.tgz",
144 "integrity": "sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw==", 155 "integrity": "sha512-+Xj5fucDtdKEVGSh9353wcnseMRkPpEAOY96EEenN7kJVrLqy/EVwtIh3mxcUz8lsFXW1mT5nN5vvEam/a5HiQ==",
145 "dev": true, 156 "dev": true,
146 "requires": { 157 "requires": {
147 "@types/eslint-visitor-keys": "^1.0.0", 158 "@types/eslint-visitor-keys": "^1.0.0",
148 "@typescript-eslint/experimental-utils": "2.24.0", 159 "@typescript-eslint/experimental-utils": "2.26.0",
149 "@typescript-eslint/typescript-estree": "2.24.0", 160 "@typescript-eslint/typescript-estree": "2.26.0",
150 "eslint-visitor-keys": "^1.1.0" 161 "eslint-visitor-keys": "^1.1.0"
151 } 162 }
152 }, 163 },
153 "@typescript-eslint/typescript-estree": { 164 "@typescript-eslint/typescript-estree": {
154 "version": "2.24.0", 165 "version": "2.26.0",
155 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.24.0.tgz", 166 "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.26.0.tgz",
156 "integrity": "sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA==", 167 "integrity": "sha512-3x4SyZCLB4zsKsjuhxDLeVJN6W29VwBnYpCsZ7vIdPel9ZqLfIZJgJXO47MNUkurGpQuIBALdPQKtsSnWpE1Yg==",
157 "dev": true, 168 "dev": true,
158 "requires": { 169 "requires": {
159 "debug": "^4.1.1", 170 "debug": "^4.1.1",
@@ -1388,9 +1399,9 @@
1388 } 1399 }
1389 }, 1400 },
1390 "rollup": { 1401 "rollup": {
1391 "version": "2.1.0", 1402 "version": "2.3.1",
1392 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz", 1403 "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.3.1.tgz",
1393 "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==", 1404 "integrity": "sha512-BRjzOauORe+R0U0I6SkMTSG22nYmtztR/TaBl0SvbXgc3VAxBDrZoB6HROiK0S5px1pUBnLnjBkbzmVuwC9Q1Q==",
1394 "dev": true, 1405 "dev": true,
1395 "requires": { 1406 "requires": {
1396 "fsevents": "~2.1.2" 1407 "fsevents": "~2.1.2"
diff --git a/editors/code/package.json b/editors/code/package.json
index 37e083220..ba31c4e63 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -39,13 +39,13 @@
39 "devDependencies": { 39 "devDependencies": {
40 "@rollup/plugin-commonjs": "^11.0.2", 40 "@rollup/plugin-commonjs": "^11.0.2",
41 "@rollup/plugin-node-resolve": "^7.1.1", 41 "@rollup/plugin-node-resolve": "^7.1.1",
42 "@types/node": "^12.12.30", 42 "@types/node": "^12.12.34",
43 "@types/node-fetch": "^2.5.5", 43 "@types/node-fetch": "^2.5.5",
44 "@types/vscode": "^1.43.0", 44 "@types/vscode": "^1.43.0",
45 "@typescript-eslint/eslint-plugin": "^2.24.0", 45 "@typescript-eslint/eslint-plugin": "^2.26.0",
46 "@typescript-eslint/parser": "^2.24.0", 46 "@typescript-eslint/parser": "^2.26.0",
47 "eslint": "^6.8.0", 47 "eslint": "^6.8.0",
48 "rollup": "^2.1.0", 48 "rollup": "^2.3.1",
49 "tslib": "^1.11.1", 49 "tslib": "^1.11.1",
50 "typescript": "^3.8.3", 50 "typescript": "^3.8.3",
51 "typescript-formatter": "^7.2.2", 51 "typescript-formatter": "^7.2.2",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index d72ecc58f..f909f8db2 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -30,15 +30,14 @@ export function configToServerOptions(config: Config) {
30 }; 30 };
31} 31}
32 32
33export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> { 33export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> {
34 // '.' Is the fallback if no folder is open 34 // '.' Is the fallback if no folder is open
35 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 35 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
36 // It might be a good idea to test if the uri points to a file. 36 // It might be a good idea to test if the uri points to a file.
37 const workspaceFolderPath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '.';
38 37
39 const run: lc.Executable = { 38 const run: lc.Executable = {
40 command: serverPath, 39 command: serverPath,
41 options: { cwd: workspaceFolderPath }, 40 options: { cwd },
42 }; 41 };
43 const serverOptions: lc.ServerOptions = { 42 const serverOptions: lc.ServerOptions = {
44 run, 43 run,
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 2e08e8f11..996c7a716 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -1,8 +1,10 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as ra from '../rust-analyzer-api'; 2import * as ra from '../rust-analyzer-api';
3 3
4import { Ctx, Cmd } from '../ctx'; 4import { Ctx, Cmd, Disposable } from '../ctx';
5import { isRustDocument } from '../util'; 5import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util';
6
7const AST_FILE_SCHEME = "rust-analyzer";
6 8
7// Opens the virtual file that will show the syntax tree 9// Opens the virtual file that will show the syntax tree
8// 10//
@@ -10,35 +12,13 @@ import { isRustDocument } from '../util';
10export function syntaxTree(ctx: Ctx): Cmd { 12export function syntaxTree(ctx: Ctx): Cmd {
11 const tdcp = new TextDocumentContentProvider(ctx); 13 const tdcp = new TextDocumentContentProvider(ctx);
12 14
13 ctx.pushCleanup( 15 void new AstInspector(ctx);
14 vscode.workspace.registerTextDocumentContentProvider( 16
15 'rust-analyzer', 17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
16 tdcp,
17 ),
18 );
19
20 vscode.workspace.onDidChangeTextDocument(
21 (event: vscode.TextDocumentChangeEvent) => {
22 const doc = event.document;
23 if (!isRustDocument(doc)) return;
24 afterLs(() => tdcp.eventEmitter.fire(tdcp.uri));
25 },
26 null,
27 ctx.subscriptions,
28 );
29
30 vscode.window.onDidChangeActiveTextEditor(
31 (editor: vscode.TextEditor | undefined) => {
32 if (!editor || !isRustDocument(editor.document)) return;
33 tdcp.eventEmitter.fire(tdcp.uri);
34 },
35 null,
36 ctx.subscriptions,
37 );
38 18
39 return async () => { 19 return async () => {
40 const editor = vscode.window.activeTextEditor; 20 const editor = vscode.window.activeTextEditor;
41 const rangeEnabled = !!(editor && !editor.selection.isEmpty); 21 const rangeEnabled = !!editor && !editor.selection.isEmpty;
42 22
43 const uri = rangeEnabled 23 const uri = rangeEnabled
44 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`) 24 ? vscode.Uri.parse(`${tdcp.uri.toString()}?range=true`)
@@ -48,45 +28,126 @@ export function syntaxTree(ctx: Ctx): Cmd {
48 28
49 tdcp.eventEmitter.fire(uri); 29 tdcp.eventEmitter.fire(uri);
50 30
51 return vscode.window.showTextDocument( 31 void await vscode.window.showTextDocument(document, {
52 document, 32 viewColumn: vscode.ViewColumn.Two,
53 vscode.ViewColumn.Two, 33 preserveFocus: true
54 true, 34 });
55 );
56 }; 35 };
57} 36}
58 37
59// We need to order this after LS updates, but there's no API for that.
60// Hence, good old setTimeout.
61function afterLs(f: () => void) {
62 setTimeout(f, 10);
63}
64
65
66class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { 38class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
67 uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 39 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree');
68 eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 40 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
41
69 42
70 constructor(private readonly ctx: Ctx) { 43 constructor(private readonly ctx: Ctx) {
44 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
45 vscode.window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, ctx.subscriptions);
71 } 46 }
72 47
73 provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> { 48 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
74 const editor = vscode.window.activeTextEditor; 49 if (isRustDocument(event.document)) {
75 const client = this.ctx.client; 50 // We need to order this after language server updates, but there's no API for that.
76 if (!editor || !client) return ''; 51 // Hence, good old sleep().
52 void sleep(10).then(() => this.eventEmitter.fire(this.uri));
53 }
54 }
55 private onDidChangeActiveTextEditor(editor: vscode.TextEditor | undefined) {
56 if (editor && isRustEditor(editor)) {
57 this.eventEmitter.fire(this.uri);
58 }
59 }
60
61 provideTextDocumentContent(uri: vscode.Uri, ct: vscode.CancellationToken): vscode.ProviderResult<string> {
62 const rustEditor = this.ctx.activeRustEditor;
63 if (!rustEditor) return '';
77 64
78 // When the range based query is enabled we take the range of the selection 65 // When the range based query is enabled we take the range of the selection
79 const range = uri.query === 'range=true' && !editor.selection.isEmpty 66 const range = uri.query === 'range=true' && !rustEditor.selection.isEmpty
80 ? client.code2ProtocolConverter.asRange(editor.selection) 67 ? this.ctx.client.code2ProtocolConverter.asRange(rustEditor.selection)
81 : null; 68 : null;
82 69
83 return client.sendRequest(ra.syntaxTree, { 70 const params = { textDocument: { uri: rustEditor.document.uri.toString() }, range, };
84 textDocument: { uri: editor.document.uri.toString() }, 71 return this.ctx.client.sendRequest(ra.syntaxTree, params, ct);
85 range,
86 });
87 } 72 }
88 73
89 get onDidChange(): vscode.Event<vscode.Uri> { 74 get onDidChange(): vscode.Event<vscode.Uri> {
90 return this.eventEmitter.event; 75 return this.eventEmitter.event;
91 } 76 }
92} 77}
78
79
80// FIXME: consider implementing this via the Tree View API?
81// https://code.visualstudio.com/api/extension-guides/tree-view
82class AstInspector implements vscode.HoverProvider, Disposable {
83 private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({
84 fontStyle: "normal",
85 border: "#ffffff 1px solid",
86 });
87 private rustEditor: undefined | RustEditor;
88
89 constructor(ctx: Ctx) {
90 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
91 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
92 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
93
94 ctx.pushCleanup(this);
95 }
96 dispose() {
97 this.setRustEditor(undefined);
98 }
99
100 private onDidCloseTextDocument(doc: vscode.TextDocument) {
101 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
102 this.setRustEditor(undefined);
103 }
104 }
105
106 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
107 if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) {
108 this.setRustEditor(undefined);
109 return;
110 }
111 this.setRustEditor(editors.find(isRustEditor));
112 }
113
114 private setRustEditor(newRustEditor: undefined | RustEditor) {
115 if (newRustEditor !== this.rustEditor) {
116 this.rustEditor?.setDecorations(AstInspector.astDecorationType, []);
117 }
118 this.rustEditor = newRustEditor;
119 }
120
121 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
122 if (!this.rustEditor) return;
123
124 const astTextLine = doc.lineAt(hoverPosition.line);
125
126 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text);
127 if (!rustTextRange) return;
128
129 this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]);
130 this.rustEditor.revealRange(rustTextRange);
131
132 const rustSourceCode = this.rustEditor.document.getText(rustTextRange);
133 const astTextRange = this.findAstRange(astTextLine);
134
135 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange);
136 }
137
138 private findAstRange(astLine: vscode.TextLine) {
139 const lineOffset = astLine.range.start;
140 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
141 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
142 return new vscode.Range(begin, end);
143 }
144
145 private parseRustTextRange(doc: vscode.TextDocument, astLine: string): undefined | vscode.Range {
146 const parsedRange = /\[(\d+); (\d+)\)/.exec(astLine);
147 if (!parsedRange) return;
148
149 const [begin, end] = parsedRange.slice(1).map(off => doc.positionAt(+off));
150
151 return new vscode.Range(begin, end);
152 }
153}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index d2f49cd23..86b5f3629 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -15,8 +15,13 @@ export class Ctx {
15 15
16 } 16 }
17 17
18 static async create(config: Config, extCtx: vscode.ExtensionContext, serverPath: string): Promise<Ctx> { 18 static async create(
19 const client = await createClient(config, serverPath); 19 config: Config,
20 extCtx: vscode.ExtensionContext,
21 serverPath: string,
22 cwd: string,
23 ): Promise<Ctx> {
24 const client = await createClient(config, serverPath, cwd);
20 const res = new Ctx(config, extCtx, client, serverPath); 25 const res = new Ctx(config, extCtx, client, serverPath);
21 res.pushCleanup(client.start()); 26 res.pushCleanup(client.start());
22 await client.onReady(); 27 await client.onReady();
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index a46dbde33..7ba16120c 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -13,6 +13,7 @@ import { log, assert } from './util';
13import { PersistentState } from './persistent_state'; 13import { PersistentState } from './persistent_state';
14import { fetchRelease, download } from './net'; 14import { fetchRelease, download } from './net';
15import { spawnSync } from 'child_process'; 15import { spawnSync } from 'child_process';
16import { activateTaskProvider } from './tasks';
16 17
17let ctx: Ctx | undefined; 18let ctx: Ctx | undefined;
18 19
@@ -41,11 +42,18 @@ export async function activate(context: vscode.ExtensionContext) {
41 const state = new PersistentState(context.globalState); 42 const state = new PersistentState(context.globalState);
42 const serverPath = await bootstrap(config, state); 43 const serverPath = await bootstrap(config, state);
43 44
45 const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
46 if (workspaceFolder === undefined) {
47 const err = "Cannot activate rust-analyzer when no folder is opened";
48 void vscode.window.showErrorMessage(err);
49 throw new Error(err);
50 }
51
44 // Note: we try to start the server before we activate type hints so that it 52 // Note: we try to start the server before we activate type hints so that it
45 // registers its `onDidChangeDocument` handler before us. 53 // registers its `onDidChangeDocument` handler before us.
46 // 54 //
47 // This a horribly, horribly wrong way to deal with this problem. 55 // This a horribly, horribly wrong way to deal with this problem.
48 ctx = await Ctx.create(config, context, serverPath); 56 ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath);
49 57
50 // Commands which invokes manually via command palette, shortcut, etc. 58 // Commands which invokes manually via command palette, shortcut, etc.
51 59
@@ -85,6 +93,8 @@ export async function activate(context: vscode.ExtensionContext) {
85 ctx.registerCommand('applySourceChange', commands.applySourceChange); 93 ctx.registerCommand('applySourceChange', commands.applySourceChange);
86 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange); 94 ctx.registerCommand('selectAndApplySourceChange', commands.selectAndApplySourceChange);
87 95
96 ctx.pushCleanup(activateTaskProvider(workspaceFolder));
97
88 activateStatusDisplay(ctx); 98 activateStatusDisplay(ctx);
89 99
90 if (!ctx.config.highlightingSemanticTokens) { 100 if (!ctx.config.highlightingSemanticTokens) {
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
new file mode 100644
index 000000000..fa1c4a951
--- /dev/null
+++ b/editors/code/src/tasks.ts
@@ -0,0 +1,52 @@
1import * as vscode from 'vscode';
2
3// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
4// our configuration should be compatible with it so use the same key.
5const TASK_TYPE = 'cargo';
6
7export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable {
8 const provider: vscode.TaskProvider = {
9 // Detect Rust tasks. Currently we do not do any actual detection
10 // of tasks (e.g. aliases in .cargo/config) and just return a fixed
11 // set of tasks that always exist. These tasks cannot be removed in
12 // tasks.json - only tweaked.
13 provideTasks: () => getStandardCargoTasks(target),
14
15 // We don't need to implement this.
16 resolveTask: () => undefined,
17 };
18
19 return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
20}
21
22function getStandardCargoTasks(target: vscode.WorkspaceFolder): vscode.Task[] {
23 return [
24 { command: 'build', group: vscode.TaskGroup.Build },
25 { command: 'check', group: vscode.TaskGroup.Build },
26 { command: 'test', group: vscode.TaskGroup.Test },
27 { command: 'clean', group: vscode.TaskGroup.Clean },
28 { command: 'run', group: undefined },
29 ]
30 .map(({ command, group }) => {
31 const vscodeTask = new vscode.Task(
32 // The contents of this object end up in the tasks.json entries.
33 {
34 type: TASK_TYPE,
35 command,
36 },
37 // The scope of the task - workspace or specific folder (global
38 // is not supported).
39 target,
40 // The task name, and task source. These are shown in the UI as
41 // `${source}: ${name}`, e.g. `rust: cargo build`.
42 `cargo ${command}`,
43 'rust',
44 // What to do when this command is executed.
45 new vscode.ShellExecution('cargo', [command]),
46 // Problem matchers.
47 ['$rustc'],
48 );
49 vscodeTask.group = group;
50 return vscodeTask;
51 });
52}
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts
index 978a31751..6f91f81d6 100644
--- a/editors/code/src/util.ts
+++ b/editors/code/src/util.ts
@@ -65,12 +65,12 @@ export async function sendRequestWithRetry<TParam, TRet>(
65 throw 'unreachable'; 65 throw 'unreachable';
66} 66}
67 67
68function sleep(ms: number) { 68export function sleep(ms: number) {
69 return new Promise(resolve => setTimeout(resolve, ms)); 69 return new Promise(resolve => setTimeout(resolve, ms));
70} 70}
71 71
72export type RustDocument = vscode.TextDocument & { languageId: "rust" }; 72export type RustDocument = vscode.TextDocument & { languageId: "rust" };
73export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string }; 73export type RustEditor = vscode.TextEditor & { document: RustDocument };
74 74
75export function isRustDocument(document: vscode.TextDocument): document is RustDocument { 75export function isRustDocument(document: vscode.TextDocument): document is RustDocument {
76 return document.languageId === 'rust' 76 return document.languageId === 'rust'