diff options
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]] |
13 | name = "anyhow" | 13 | name = "anyhow" |
14 | version = "1.0.27" | 14 | version = "1.0.28" |
15 | source = "registry+https://github.com/rust-lang/crates.io-index" | 15 | source = "registry+https://github.com/rust-lang/crates.io-index" |
16 | checksum = "013a6e0a2cbe3d20f9c60b65458f7a7f7a5e636c5d0f45a5a6aee5d4b1f01785" | 16 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" |
17 | 17 | ||
18 | [[package]] | 18 | [[package]] |
19 | name = "anymap" | 19 | name = "anymap" |
@@ -435,9 +435,9 @@ dependencies = [ | |||
435 | 435 | ||
436 | [[package]] | 436 | [[package]] |
437 | name = "hermit-abi" | 437 | name = "hermit-abi" |
438 | version = "0.1.8" | 438 | version = "0.1.10" |
439 | source = "registry+https://github.com/rust-lang/crates.io-index" | 439 | source = "registry+https://github.com/rust-lang/crates.io-index" |
440 | checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" | 440 | checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" |
441 | dependencies = [ | 441 | dependencies = [ |
442 | "libc", | 442 | "libc", |
443 | ] | 443 | ] |
@@ -792,9 +792,9 @@ dependencies = [ | |||
792 | 792 | ||
793 | [[package]] | 793 | [[package]] |
794 | name = "paste" | 794 | name = "paste" |
795 | version = "0.1.8" | 795 | version = "0.1.9" |
796 | source = "registry+https://github.com/rust-lang/crates.io-index" | 796 | source = "registry+https://github.com/rust-lang/crates.io-index" |
797 | checksum = "8292c1e1e81ddb552c4c90c36af201a0ce7e34995f55f0480f01052f242811c9" | 797 | checksum = "092d791bf7847f70bbd49085489fba25fc2c193571752bff9e36e74e72403932" |
798 | dependencies = [ | 798 | dependencies = [ |
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]] |
804 | name = "paste-impl" | 804 | name = "paste-impl" |
805 | version = "0.1.8" | 805 | version = "0.1.9" |
806 | source = "registry+https://github.com/rust-lang/crates.io-index" | 806 | source = "registry+https://github.com/rust-lang/crates.io-index" |
807 | checksum = "5e9c43f2645f06ee452544ad032886a75f3d1797b9487dcadcae9100ba58a51c" | 807 | checksum = "406c23fb4c45cc6f68a9bbabb8ec7bd6f8cfcbd17e9e8f72c2460282f8325729" |
808 | dependencies = [ | 808 | dependencies = [ |
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]] |
850 | name = "proc-macro2" | 850 | name = "proc-macro2" |
851 | version = "1.0.9" | 851 | version = "1.0.10" |
852 | source = "registry+https://github.com/rust-lang/crates.io-index" | 852 | source = "registry+https://github.com/rust-lang/crates.io-index" |
853 | checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" | 853 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" |
854 | dependencies = [ | 854 | dependencies = [ |
855 | "unicode-xid", | 855 | "unicode-xid", |
856 | ] | 856 | ] |
@@ -887,19 +887,6 @@ dependencies = [ | |||
887 | ] | 887 | ] |
888 | 888 | ||
889 | [[package]] | 889 | [[package]] |
890 | name = "ra_cargo_watch" | ||
891 | version = "0.1.0" | ||
892 | dependencies = [ | ||
893 | "cargo_metadata", | ||
894 | "crossbeam-channel", | ||
895 | "insta", | ||
896 | "jod-thread", | ||
897 | "log", | ||
898 | "lsp-types", | ||
899 | "serde_json", | ||
900 | ] | ||
901 | |||
902 | [[package]] | ||
903 | name = "ra_cfg" | 890 | name = "ra_cfg" |
904 | version = "0.1.0" | 891 | version = "0.1.0" |
905 | dependencies = [ | 892 | dependencies = [ |
@@ -924,6 +911,19 @@ dependencies = [ | |||
924 | ] | 911 | ] |
925 | 912 | ||
926 | [[package]] | 913 | [[package]] |
914 | name = "ra_flycheck" | ||
915 | version = "0.1.0" | ||
916 | dependencies = [ | ||
917 | "cargo_metadata", | ||
918 | "crossbeam-channel", | ||
919 | "insta", | ||
920 | "jod-thread", | ||
921 | "log", | ||
922 | "lsp-types", | ||
923 | "serde_json", | ||
924 | ] | ||
925 | |||
926 | [[package]] | ||
927 | name = "ra_fmt" | 927 | name = "ra_fmt" |
928 | version = "0.1.0" | 928 | version = "0.1.0" |
929 | dependencies = [ | 929 | dependencies = [ |
@@ -1075,7 +1075,12 @@ dependencies = [ | |||
1075 | name = "ra_proc_macro" | 1075 | name = "ra_proc_macro" |
1076 | version = "0.1.0" | 1076 | version = "0.1.0" |
1077 | dependencies = [ | 1077 | dependencies = [ |
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]] |
1428 | name = "serde_json" | 1432 | name = "serde_json" |
1429 | version = "1.0.48" | 1433 | version = "1.0.50" |
1430 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1434 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1431 | checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" | 1435 | checksum = "78a7a12c167809363ec3bd7329fc0a3369056996de43c4b37ef3cd54a6ce4867" |
1432 | dependencies = [ | 1436 | dependencies = [ |
1433 | "itoa", | 1437 | "itoa", |
1434 | "ryu", | 1438 | "ryu", |
@@ -1651,9 +1655,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | |||
1651 | 1655 | ||
1652 | [[package]] | 1656 | [[package]] |
1653 | name = "winapi-util" | 1657 | name = "winapi-util" |
1654 | version = "0.1.3" | 1658 | version = "0.1.4" |
1655 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1659 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1656 | checksum = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" | 1660 | checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" |
1657 | dependencies = [ | 1661 | dependencies = [ |
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> { | |||
14 | impl<T, V> ArenaMap<Idx<T>, V> { | 14 | impl<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 | |||
3 | use std::iter; | 1 | use std::iter; |
4 | 2 | ||
5 | use hir::{Adt, HasSource, ModuleDef, Semantics}; | 3 | use hir::{Adt, HasSource, ModuleDef, Semantics}; |
6 | use itertools::Itertools; | 4 | use itertools::Itertools; |
7 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | ||
8 | 7 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{Assist, AssistCtx, AssistId}; |
10 | use ra_syntax::ast::{self, make, AstNode, NameOwner}; | ||
11 | |||
12 | use 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] |
2 | edition = "2018" | 2 | edition = "2018" |
3 | name = "ra_cargo_watch" | 3 | name = "ra_flycheck" |
4 | version = "0.1.0" | 4 | version = "0.1.0" |
5 | authors = ["rust-analyzer developers"] | 5 | authors = ["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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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 | --- |
2 | source: crates/ra_cargo_watch/src/conv/test.rs | 2 | source: crates/ra_flycheck/src/conv/test.rs |
3 | expression: diag | 3 | expression: 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}; | |||
22 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 22 | pub use crate::conv::url_from_path_with_drive_lowercasing; |
23 | 23 | ||
24 | #[derive(Clone, Debug)] | 24 | #[derive(Clone, Debug)] |
25 | pub struct CheckOptions { | 25 | pub 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 | ||
44 | impl CheckWatcher { | 44 | impl 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 | ||
80 | struct CheckWatcherThread { | 78 | struct 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 | ||
87 | impl CheckWatcherThread { | 85 | impl 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 | } |
246 | impl error::Error for CargoError {} | 244 | impl error::Error for CargoError {} |
247 | 245 | ||
248 | pub fn run_cargo( | 246 | fn 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 | ||
12 | macro_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 | |||
12 | impl ProcMacroExpander { | 21 | impl 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 | |||
46 | fn 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)] |
37 | pub struct CompletionOptions { | 37 | pub 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 | ||
43 | impl Default for CompletionOptions { | 43 | impl 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 { | |||
75 | pub(crate) fn completions( | 75 | pub(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 | ||
17 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 17 | pub(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 | }; |
12 | use ra_text_edit::AtomTextEdit; | 12 | use ra_text_edit::AtomTextEdit; |
13 | 13 | ||
14 | use crate::{completion::CompletionOptions, FilePosition}; | 14 | use 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}; | |||
19 | pub(crate) struct CompletionContext<'a> { | 19 | pub(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 | ||
3 | use crate::{ | 3 | use 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 | ||
9 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | 9 | pub(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 | ||
13 | pub(crate) fn do_completion_with_options( | 13 | pub(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::{ | |||
11 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
12 | 12 | ||
13 | #[derive(Clone, Debug, PartialEq, Eq)] | 13 | #[derive(Clone, Debug, PartialEq, Eq)] |
14 | pub struct InlayHintsOptions { | 14 | pub 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 | ||
21 | impl Default for InlayHintsOptions { | 21 | impl 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 { | |||
41 | pub(crate) fn inlay_hints( | 41 | pub(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( | |||
68 | fn get_chaining_hints( | 68 | fn 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( | |||
108 | fn get_param_name_hints( | 108 | fn 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( | |||
148 | fn get_bind_pat_hints( | 148 | fn 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)] |
272 | mod tests { | 272 | mod 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; | |||
62 | pub use crate::{ | 62 | pub 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] |
12 | ra_tt = { path = "../ra_tt" } | 12 | ra_tt = { path = "../ra_tt" } |
13 | serde = { version = "1.0", features = ["derive"] } | ||
14 | serde_json = "1.0" | ||
15 | log = "0.4.8" | ||
16 | crossbeam-channel = "0.4.0" | ||
17 | jod-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 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | ||
11 | |||
12 | use process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | ||
8 | use ra_tt::{SmolStr, Subtree}; | 13 | use ra_tt::{SmolStr, Subtree}; |
14 | use rpc::ProcMacroKind; | ||
9 | use std::{ | 15 | use 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)] | 20 | pub use rpc::{ExpansionResult, ExpansionTask}; |
21 | |||
22 | #[derive(Debug, Clone)] | ||
15 | pub struct ProcMacroProcessExpander { | 23 | pub struct ProcMacroProcessExpander { |
16 | process: Arc<ProcMacroProcessSrv>, | 24 | process: Arc<ProcMacroProcessSrv>, |
25 | dylib_path: PathBuf, | ||
17 | name: SmolStr, | 26 | name: SmolStr, |
18 | } | 27 | } |
19 | 28 | ||
29 | impl Eq for ProcMacroProcessExpander {} | ||
30 | impl 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 | |||
20 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { | 38 | impl 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)] |
32 | pub struct ProcMacroProcessSrv { | 49 | enum 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)] |
37 | pub enum ProcMacroClient { | 55 | pub struct ProcMacroClient { |
38 | Process { process: Arc<ProcMacroProcessSrv> }, | 56 | kind: ProcMacroClientKind, |
39 | Dummy, | ||
40 | } | 57 | } |
41 | 58 | ||
42 | impl ProcMacroClient { | 59 | impl 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 | |||
3 | use std::{ | ||
4 | convert::TryFrom, | ||
5 | io::{self, BufRead, Write}, | ||
6 | }; | ||
7 | |||
8 | use crate::{ | ||
9 | rpc::{ListMacrosResult, ListMacrosTask}, | ||
10 | ExpansionResult, ExpansionTask, | ||
11 | }; | ||
12 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; | ||
13 | |||
14 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
15 | pub enum Request { | ||
16 | ListMacro(ListMacrosTask), | ||
17 | ExpansionMacro(ExpansionTask), | ||
18 | } | ||
19 | |||
20 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
21 | pub enum Response { | ||
22 | Error(ResponseError), | ||
23 | ListMacro(ListMacrosResult), | ||
24 | ExpansionMacro(ExpansionResult), | ||
25 | } | ||
26 | |||
27 | macro_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 | |||
41 | impl_try_from_response!(ListMacrosResult, ListMacro); | ||
42 | impl_try_from_response!(ExpansionResult, ExpansionMacro); | ||
43 | |||
44 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
45 | pub struct ResponseError { | ||
46 | pub code: ErrorCode, | ||
47 | pub message: String, | ||
48 | } | ||
49 | |||
50 | #[derive(Debug, Serialize, Deserialize, Clone)] | ||
51 | pub enum ErrorCode { | ||
52 | ServerErrorEnd, | ||
53 | ExpansionError, | ||
54 | } | ||
55 | |||
56 | pub 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 | |||
71 | impl Message for Request {} | ||
72 | impl Message for Response {} | ||
73 | |||
74 | fn 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 | |||
87 | fn 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 | |||
3 | use crossbeam_channel::{bounded, Receiver, Sender}; | ||
4 | use ra_tt::Subtree; | ||
5 | |||
6 | use crate::msg::{ErrorCode, Message, Request, Response, ResponseError}; | ||
7 | use crate::rpc::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask, ProcMacroKind}; | ||
8 | |||
9 | use io::{BufRead, BufReader}; | ||
10 | use 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)] | ||
19 | pub(crate) struct ProcMacroProcessSrv { | ||
20 | inner: Option<Weak<Sender<Task>>>, | ||
21 | } | ||
22 | |||
23 | #[derive(Debug)] | ||
24 | pub(crate) struct ProcMacroProcessThread { | ||
25 | // XXX: drop order is significant | ||
26 | sender: Arc<Sender<Task>>, | ||
27 | handle: jod_thread::JoinHandle<()>, | ||
28 | } | ||
29 | |||
30 | struct Task { | ||
31 | req: Request, | ||
32 | result_tx: Sender<Option<Response>>, | ||
33 | } | ||
34 | |||
35 | struct Process { | ||
36 | path: PathBuf, | ||
37 | child: Child, | ||
38 | } | ||
39 | |||
40 | impl Drop for Process { | ||
41 | fn drop(&mut self) { | ||
42 | let _ = self.child.kill(); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | impl 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 | |||
76 | impl 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 | |||
157 | fn 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 | |||
189 | fn 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 | |||
9 | use ra_tt::{ | ||
10 | Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, SmolStr, Spacing, Subtree, TokenId, | ||
11 | TokenTree, | ||
12 | }; | ||
13 | use serde::{Deserialize, Serialize}; | ||
14 | use std::path::PathBuf; | ||
15 | |||
16 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
17 | pub struct ListMacrosTask { | ||
18 | pub lib: PathBuf, | ||
19 | } | ||
20 | |||
21 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
22 | pub enum ProcMacroKind { | ||
23 | CustomDerive, | ||
24 | FuncLike, | ||
25 | Attr, | ||
26 | } | ||
27 | |||
28 | #[derive(Clone, Eq, PartialEq, Debug, Default, Serialize, Deserialize)] | ||
29 | pub struct ListMacrosResult { | ||
30 | pub macros: Vec<(String, ProcMacroKind)>, | ||
31 | } | ||
32 | |||
33 | #[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)] | ||
34 | pub 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)] | ||
56 | pub struct ExpansionResult { | ||
57 | #[serde(with = "SubtreeDef")] | ||
58 | pub expansion: Subtree, | ||
59 | } | ||
60 | |||
61 | #[derive(Serialize, Deserialize)] | ||
62 | #[serde(remote = "DelimiterKind")] | ||
63 | enum DelimiterKindDef { | ||
64 | Parenthesis, | ||
65 | Brace, | ||
66 | Bracket, | ||
67 | } | ||
68 | |||
69 | #[derive(Serialize, Deserialize)] | ||
70 | #[serde(remote = "TokenId")] | ||
71 | struct TokenIdDef(u32); | ||
72 | |||
73 | #[derive(Serialize, Deserialize)] | ||
74 | #[serde(remote = "Delimiter")] | ||
75 | struct 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")] | ||
84 | struct 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")] | ||
93 | enum 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")] | ||
102 | enum 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")] | ||
113 | struct LiteralDef { | ||
114 | pub text: SmolStr, | ||
115 | #[serde(with = "TokenIdDef")] | ||
116 | pub id: TokenId, | ||
117 | } | ||
118 | |||
119 | #[derive(Serialize, Deserialize)] | ||
120 | #[serde(remote = "Punct")] | ||
121 | struct 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")] | ||
131 | enum SpacingDef { | ||
132 | Alone, | ||
133 | Joint, | ||
134 | } | ||
135 | |||
136 | #[derive(Serialize, Deserialize)] | ||
137 | #[serde(remote = "Ident")] | ||
138 | struct IdentDef { | ||
139 | pub text: SmolStr, | ||
140 | #[serde(with = "TokenIdDef")] | ||
141 | pub id: TokenId, | ||
142 | } | ||
143 | |||
144 | mod 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 | |||
168 | mod 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 | |||
192 | mod 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)] | ||
224 | mod 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" | |||
16 | ra_arena = { path = "../ra_arena" } | 16 | ra_arena = { path = "../ra_arena" } |
17 | ra_db = { path = "../ra_db" } | 17 | ra_db = { path = "../ra_db" } |
18 | ra_cfg = { path = "../ra_cfg" } | 18 | ra_cfg = { path = "../ra_cfg" } |
19 | ra_cargo_watch = { path = "../ra_cargo_watch" } | ||
20 | ra_proc_macro = { path = "../ra_proc_macro" } | 19 | ra_proc_macro = { path = "../ra_proc_macro" } |
21 | 20 | ||
22 | serde = { version = "1.0.104", features = ["derive"] } | 21 | serde = { 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 | ||
3 | use std::{ | 3 | use 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 | ||
8 | use anyhow::{Context, Result}; | 11 | use anyhow::{Context, Result}; |
9 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; | 12 | use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; |
10 | use ra_arena::{Arena, Idx}; | 13 | use ra_arena::{Arena, Idx}; |
11 | use ra_cargo_watch::run_cargo; | ||
12 | use ra_db::Edition; | 14 | use ra_db::Edition; |
13 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
14 | use serde::Deserialize; | 16 | use serde::Deserialize; |
@@ -75,6 +77,7 @@ pub type Target = Idx<TargetData>; | |||
75 | 77 | ||
76 | #[derive(Debug, Clone)] | 78 | #[derive(Debug, Clone)] |
77 | pub struct PackageData { | 79 | pub 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 | ||
260 | pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { | 276 | pub 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); | 324 | fn 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 | 331 | fn 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 | |||
3 | fn 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 @@ | |||
1 | SOURCE_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 { | |||
189 | pub mod buffer; | 189 | pub mod buffer; |
190 | 190 | ||
191 | #[derive(Debug, PartialEq, Eq)] | 191 | #[derive(Debug, PartialEq, Eq)] |
192 | pub enum ExpansionError {} | 192 | pub enum ExpansionError { |
193 | IOError(String), | ||
194 | JsonError(String), | ||
195 | Unknown(String), | ||
196 | ExpansionError(String), | ||
197 | } | ||
193 | 198 | ||
194 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | 199 | pub 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" | |||
33 | stdx = { path = "../stdx" } | 33 | stdx = { path = "../stdx" } |
34 | 34 | ||
35 | lsp-server = "0.3.1" | 35 | lsp-server = "0.3.1" |
36 | ra_cargo_watch = { path = "../ra_cargo_watch" } | 36 | ra_flycheck = { path = "../ra_flycheck" } |
37 | ra_ide = { path = "../ra_ide" } | 37 | ra_ide = { path = "../ra_ide" } |
38 | ra_prof = { path = "../ra_prof" } | 38 | ra_prof = { path = "../ra_prof" } |
39 | ra_project_model = { path = "../ra_project_model" } | 39 | ra_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 | }; |
15 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionOptions, FilePosition, LineCol}; | 15 | use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CompletionConfig, FilePosition, LineCol}; |
16 | 16 | ||
17 | use crate::cli::{load_cargo::load_cargo, Verbosity}; | 17 | use 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 | }; |
24 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckTask}; | 24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckTask}; |
25 | use ra_ide::{Canceled, FileId, InlayHintsOptions, LibraryData, SourceRootId}; | 25 | use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; |
26 | use ra_prof::profile; | 26 | use ra_prof::profile; |
27 | use ra_vfs::{VfsFile, VfsTask, Watch}; | 27 | use ra_vfs::{VfsFile, VfsTask, Watch}; |
28 | use relative_path::RelativePathBuf; | 28 | use 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 | }; |
44 | use req::ConfigurationParams; | 44 | use req::ConfigurationParams; |
@@ -81,11 +81,11 @@ fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureF | |||
81 | ff | 81 | ff |
82 | } | 82 | } |
83 | 83 | ||
84 | fn get_options( | 84 | fn 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 | ||
813 | fn 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 | ||
821 | struct PoolDispatcher<'a> { | 822 | struct 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 | }; |
21 | use ra_ide::{ | 21 | use 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 | }; |
25 | use ra_prof::profile; | 25 | use 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::{ | |||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_types::Url; | 12 | use lsp_types::Url; |
13 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
14 | use ra_cargo_watch::{url_from_path_with_drive_lowercasing, CheckOptions, CheckWatcher}; | 14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckConfig, CheckWatcher}; |
15 | use ra_ide::{ | 15 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsConfig, LibraryData, |
17 | SourceRootId, | 17 | SourceRootId, |
18 | }; | 18 | }; |
19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; | 19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; |
@@ -31,7 +31,7 @@ use crate::{ | |||
31 | use ra_db::ExternSourceId; | 31 | use ra_db::ExternSourceId; |
32 | use rustc_hash::{FxHashMap, FxHashSet}; | 32 | use rustc_hash::{FxHashMap, FxHashSet}; |
33 | 33 | ||
34 | fn create_watcher(workspaces: &[ProjectWorkspace], options: &Options) -> Option<CheckWatcher> { | 34 | fn 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)] |
53 | pub struct Options { | 53 | pub 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)] |
69 | pub struct WorldState { | 70 | pub 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. |
83 | pub struct WorldSnapshot { | 84 | pub 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 | }; |
10 | use rust_analyzer::req::{ | 10 | use 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 | }; |
14 | use serde_json::json; | 14 | use serde_json::json; |
15 | use tempfile::TempDir; | 15 | use tempfile::TempDir; |
@@ -581,3 +581,47 @@ version = \"0.0.0\" | |||
581 | }), | 581 | }), |
582 | ); | 582 | ); |
583 | } | 583 | } |
584 | |||
585 | #[test] | ||
586 | fn 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] | ||
595 | name = "foo" | ||
596 | version = "0.0.0" | ||
597 | |||
598 | //- build.rs | ||
599 | use std::{env, fs, path::Path}; | ||
600 | |||
601 | fn 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 | ||
612 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); | ||
613 | |||
614 | fn 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 | ||
32 | impl<'a> Project<'a> { | 33 | impl<'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 { | |||
92 | impl Server { | 105 | impl 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. | |||
81 | Shows the parse tree of the current file. It exists mostly for debugging | 81 | Shows the parse tree of the current file. It exists mostly for debugging |
82 | rust-analyzer itself. | 82 | rust-analyzer itself. |
83 | 83 | ||
84 | You can hover over syntax nodes in the opened text file to see the appropriate | ||
85 | rust code that it refers to and the rust editor will also highlight the proper | ||
86 | text 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 | ||
86 | Shows the full macro expansion of the macro at current cursor. | 92 | Shows 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 | ||
33 | export async function createClient(config: Config, serverPath: string): Promise<lc.LanguageClient> { | 33 | export 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 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as ra from '../rust-analyzer-api'; | 2 | import * as ra from '../rust-analyzer-api'; |
3 | 3 | ||
4 | import { Ctx, Cmd } from '../ctx'; | 4 | import { Ctx, Cmd, Disposable } from '../ctx'; |
5 | import { isRustDocument } from '../util'; | 5 | import { isRustDocument, RustEditor, isRustEditor, sleep } from '../util'; |
6 | |||
7 | const 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'; | |||
10 | export function syntaxTree(ctx: Ctx): Cmd { | 12 | export 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. | ||
61 | function afterLs(f: () => void) { | ||
62 | setTimeout(f, 10); | ||
63 | } | ||
64 | |||
65 | |||
66 | class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { | 38 | class 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 | ||
82 | class 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'; | |||
13 | import { PersistentState } from './persistent_state'; | 13 | import { PersistentState } from './persistent_state'; |
14 | import { fetchRelease, download } from './net'; | 14 | import { fetchRelease, download } from './net'; |
15 | import { spawnSync } from 'child_process'; | 15 | import { spawnSync } from 'child_process'; |
16 | import { activateTaskProvider } from './tasks'; | ||
16 | 17 | ||
17 | let ctx: Ctx | undefined; | 18 | let 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 @@ | |||
1 | import * 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. | ||
5 | const TASK_TYPE = 'cargo'; | ||
6 | |||
7 | export 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 | |||
22 | function 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 | ||
68 | function sleep(ms: number) { | 68 | export function sleep(ms: number) { |
69 | return new Promise(resolve => setTimeout(resolve, ms)); | 69 | return new Promise(resolve => setTimeout(resolve, ms)); |
70 | } | 70 | } |
71 | 71 | ||
72 | export type RustDocument = vscode.TextDocument & { languageId: "rust" }; | 72 | export type RustDocument = vscode.TextDocument & { languageId: "rust" }; |
73 | export type RustEditor = vscode.TextEditor & { document: RustDocument; id: string }; | 73 | export type RustEditor = vscode.TextEditor & { document: RustDocument }; |
74 | 74 | ||
75 | export function isRustDocument(document: vscode.TextDocument): document is RustDocument { | 75 | export function isRustDocument(document: vscode.TextDocument): document is RustDocument { |
76 | return document.languageId === 'rust' | 76 | return document.languageId === 'rust' |