diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/handlers/auto_import.rs | 140 | ||||
-rw-r--r-- | crates/ra_hir_def/src/import_map.rs | 76 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 14 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/global_state.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 18 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 71 | ||||
-rw-r--r-- | crates/rust-analyzer/src/reload.rs | 82 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 17 |
11 files changed, 350 insertions, 77 deletions
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 7b6499a08..4cd77adbf 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -811,6 +811,146 @@ fn main() { | |||
811 | } | 811 | } |
812 | 812 | ||
813 | #[test] | 813 | #[test] |
814 | fn trait_method_cross_crate() { | ||
815 | check_assist( | ||
816 | auto_import, | ||
817 | r" | ||
818 | //- /main.rs crate:main deps:dep | ||
819 | fn main() { | ||
820 | let test_struct = dep::test_mod::TestStruct {}; | ||
821 | test_struct.test_meth<|>od() | ||
822 | } | ||
823 | //- /dep.rs crate:dep | ||
824 | pub mod test_mod { | ||
825 | pub trait TestTrait { | ||
826 | fn test_method(&self); | ||
827 | } | ||
828 | pub struct TestStruct {} | ||
829 | impl TestTrait for TestStruct { | ||
830 | fn test_method(&self) {} | ||
831 | } | ||
832 | } | ||
833 | ", | ||
834 | r" | ||
835 | use dep::test_mod::TestTrait; | ||
836 | |||
837 | fn main() { | ||
838 | let test_struct = dep::test_mod::TestStruct {}; | ||
839 | test_struct.test_method() | ||
840 | } | ||
841 | ", | ||
842 | ); | ||
843 | } | ||
844 | |||
845 | #[test] | ||
846 | fn assoc_fn_cross_crate() { | ||
847 | check_assist( | ||
848 | auto_import, | ||
849 | r" | ||
850 | //- /main.rs crate:main deps:dep | ||
851 | fn main() { | ||
852 | dep::test_mod::TestStruct::test_func<|>tion | ||
853 | } | ||
854 | //- /dep.rs crate:dep | ||
855 | pub mod test_mod { | ||
856 | pub trait TestTrait { | ||
857 | fn test_function(); | ||
858 | } | ||
859 | pub struct TestStruct {} | ||
860 | impl TestTrait for TestStruct { | ||
861 | fn test_function() {} | ||
862 | } | ||
863 | } | ||
864 | ", | ||
865 | r" | ||
866 | use dep::test_mod::TestTrait; | ||
867 | |||
868 | fn main() { | ||
869 | dep::test_mod::TestStruct::test_function | ||
870 | } | ||
871 | ", | ||
872 | ); | ||
873 | } | ||
874 | |||
875 | #[test] | ||
876 | fn assoc_const_cross_crate() { | ||
877 | check_assist( | ||
878 | auto_import, | ||
879 | r" | ||
880 | //- /main.rs crate:main deps:dep | ||
881 | fn main() { | ||
882 | dep::test_mod::TestStruct::CONST<|> | ||
883 | } | ||
884 | //- /dep.rs crate:dep | ||
885 | pub mod test_mod { | ||
886 | pub trait TestTrait { | ||
887 | const CONST: bool; | ||
888 | } | ||
889 | pub struct TestStruct {} | ||
890 | impl TestTrait for TestStruct { | ||
891 | const CONST: bool = true; | ||
892 | } | ||
893 | } | ||
894 | ", | ||
895 | r" | ||
896 | use dep::test_mod::TestTrait; | ||
897 | |||
898 | fn main() { | ||
899 | dep::test_mod::TestStruct::CONST | ||
900 | } | ||
901 | ", | ||
902 | ); | ||
903 | } | ||
904 | |||
905 | #[test] | ||
906 | fn assoc_fn_as_method_cross_crate() { | ||
907 | check_assist_not_applicable( | ||
908 | auto_import, | ||
909 | r" | ||
910 | //- /main.rs crate:main deps:dep | ||
911 | fn main() { | ||
912 | let test_struct = dep::test_mod::TestStruct {}; | ||
913 | test_struct.test_func<|>tion() | ||
914 | } | ||
915 | //- /dep.rs crate:dep | ||
916 | pub mod test_mod { | ||
917 | pub trait TestTrait { | ||
918 | fn test_function(); | ||
919 | } | ||
920 | pub struct TestStruct {} | ||
921 | impl TestTrait for TestStruct { | ||
922 | fn test_function() {} | ||
923 | } | ||
924 | } | ||
925 | ", | ||
926 | ); | ||
927 | } | ||
928 | |||
929 | #[test] | ||
930 | fn private_trait_cross_crate() { | ||
931 | check_assist_not_applicable( | ||
932 | auto_import, | ||
933 | r" | ||
934 | //- /main.rs crate:main deps:dep | ||
935 | fn main() { | ||
936 | let test_struct = dep::test_mod::TestStruct {}; | ||
937 | test_struct.test_meth<|>od() | ||
938 | } | ||
939 | //- /dep.rs crate:dep | ||
940 | pub mod test_mod { | ||
941 | trait TestTrait { | ||
942 | fn test_method(&self); | ||
943 | } | ||
944 | pub struct TestStruct {} | ||
945 | impl TestTrait for TestStruct { | ||
946 | fn test_method(&self) {} | ||
947 | } | ||
948 | } | ||
949 | ", | ||
950 | ); | ||
951 | } | ||
952 | |||
953 | #[test] | ||
814 | fn not_applicable_for_imported_trait_for_method() { | 954 | fn not_applicable_for_imported_trait_for_method() { |
815 | check_assist_not_applicable( | 955 | check_assist_not_applicable( |
816 | auto_import, | 956 | auto_import, |
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs index 68e20d06b..869f3ca5a 100644 --- a/crates/ra_hir_def/src/import_map.rs +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -5,14 +5,16 @@ use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | |||
5 | use fst::{self, Streamer}; | 5 | use fst::{self, Streamer}; |
6 | use indexmap::{map::Entry, IndexMap}; | 6 | use indexmap::{map::Entry, IndexMap}; |
7 | use ra_db::CrateId; | 7 | use ra_db::CrateId; |
8 | use rustc_hash::FxHasher; | 8 | use ra_syntax::SmolStr; |
9 | use rustc_hash::{FxHashMap, FxHasher}; | ||
10 | use smallvec::SmallVec; | ||
9 | 11 | ||
10 | use crate::{ | 12 | use crate::{ |
11 | db::DefDatabase, | 13 | db::DefDatabase, |
12 | item_scope::ItemInNs, | 14 | item_scope::ItemInNs, |
13 | path::{ModPath, PathKind}, | 15 | path::{ModPath, PathKind}, |
14 | visibility::Visibility, | 16 | visibility::Visibility, |
15 | ModuleDefId, ModuleId, | 17 | AssocItemId, ModuleDefId, ModuleId, TraitId, |
16 | }; | 18 | }; |
17 | 19 | ||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | 20 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; |
@@ -34,6 +36,7 @@ pub struct ImportInfo { | |||
34 | /// | 36 | /// |
35 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | 37 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs |
36 | /// to be prepended to the `ModPath` before the path is valid. | 38 | /// to be prepended to the `ModPath` before the path is valid. |
39 | #[derive(Default)] | ||
37 | pub struct ImportMap { | 40 | pub struct ImportMap { |
38 | map: FxIndexMap<ItemInNs, ImportInfo>, | 41 | map: FxIndexMap<ItemInNs, ImportInfo>, |
39 | 42 | ||
@@ -45,13 +48,17 @@ pub struct ImportMap { | |||
45 | /// the index of the first one. | 48 | /// the index of the first one. |
46 | importables: Vec<ItemInNs>, | 49 | importables: Vec<ItemInNs>, |
47 | fst: fst::Map<Vec<u8>>, | 50 | fst: fst::Map<Vec<u8>>, |
51 | |||
52 | /// Maps names of associated items to the item's ID. Only includes items whose defining trait is | ||
53 | /// exported. | ||
54 | assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>, | ||
48 | } | 55 | } |
49 | 56 | ||
50 | impl ImportMap { | 57 | impl ImportMap { |
51 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | 58 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { |
52 | let _p = ra_prof::profile("import_map_query"); | 59 | let _p = ra_prof::profile("import_map_query"); |
53 | let def_map = db.crate_def_map(krate); | 60 | let def_map = db.crate_def_map(krate); |
54 | let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); | 61 | let mut import_map = Self::default(); |
55 | 62 | ||
56 | // We look only into modules that are public(ly reexported), starting with the crate root. | 63 | // We look only into modules that are public(ly reexported), starting with the crate root. |
57 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | 64 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; |
@@ -85,7 +92,7 @@ impl ImportMap { | |||
85 | 92 | ||
86 | for item in per_ns.iter_items() { | 93 | for item in per_ns.iter_items() { |
87 | let path = mk_path(); | 94 | let path = mk_path(); |
88 | match import_map.entry(item) { | 95 | match import_map.map.entry(item) { |
89 | Entry::Vacant(entry) => { | 96 | Entry::Vacant(entry) => { |
90 | entry.insert(ImportInfo { path, container: module }); | 97 | entry.insert(ImportInfo { path, container: module }); |
91 | } | 98 | } |
@@ -105,11 +112,16 @@ impl ImportMap { | |||
105 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | 112 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { |
106 | worklist.push((mod_id, mk_path())); | 113 | worklist.push((mod_id, mk_path())); |
107 | } | 114 | } |
115 | |||
116 | // If we've added a path to a trait, add the trait's methods to the method map. | ||
117 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
118 | import_map.collect_trait_methods(db, tr); | ||
119 | } | ||
108 | } | 120 | } |
109 | } | 121 | } |
110 | } | 122 | } |
111 | 123 | ||
112 | let mut importables = import_map.iter().collect::<Vec<_>>(); | 124 | let mut importables = import_map.map.iter().collect::<Vec<_>>(); |
113 | 125 | ||
114 | importables.sort_by(cmp); | 126 | importables.sort_by(cmp); |
115 | 127 | ||
@@ -133,10 +145,10 @@ impl ImportMap { | |||
133 | builder.insert(key, start as u64).unwrap(); | 145 | builder.insert(key, start as u64).unwrap(); |
134 | } | 146 | } |
135 | 147 | ||
136 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | 148 | import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
137 | let importables = importables.iter().map(|(item, _)| **item).collect(); | 149 | import_map.importables = importables.iter().map(|(item, _)| **item).collect(); |
138 | 150 | ||
139 | Arc::new(Self { map: import_map, fst, importables }) | 151 | Arc::new(import_map) |
140 | } | 152 | } |
141 | 153 | ||
142 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | 154 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. |
@@ -147,6 +159,13 @@ impl ImportMap { | |||
147 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | 159 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { |
148 | self.map.get(&item) | 160 | self.map.get(&item) |
149 | } | 161 | } |
162 | |||
163 | fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { | ||
164 | let data = db.trait_data(tr); | ||
165 | for (name, item) in data.items.iter() { | ||
166 | self.assoc_map.entry(name.to_string().into()).or_default().push(*item); | ||
167 | } | ||
168 | } | ||
150 | } | 169 | } |
151 | 170 | ||
152 | impl PartialEq for ImportMap { | 171 | impl PartialEq for ImportMap { |
@@ -290,13 +309,26 @@ pub fn search_dependencies<'a>( | |||
290 | } | 309 | } |
291 | } | 310 | } |
292 | 311 | ||
312 | // Add all exported associated items whose names match the query (exactly). | ||
313 | for map in &import_maps { | ||
314 | if let Some(v) = map.assoc_map.get(&*query.query) { | ||
315 | res.extend(v.iter().map(|&assoc| { | ||
316 | ItemInNs::Types(match assoc { | ||
317 | AssocItemId::FunctionId(it) => it.into(), | ||
318 | AssocItemId::ConstId(it) => it.into(), | ||
319 | AssocItemId::TypeAliasId(it) => it.into(), | ||
320 | }) | ||
321 | })); | ||
322 | } | ||
323 | } | ||
324 | |||
293 | res | 325 | res |
294 | } | 326 | } |
295 | 327 | ||
296 | #[cfg(test)] | 328 | #[cfg(test)] |
297 | mod tests { | 329 | mod tests { |
298 | use super::*; | 330 | use super::*; |
299 | use crate::test_db::TestDB; | 331 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; |
300 | use insta::assert_snapshot; | 332 | use insta::assert_snapshot; |
301 | use itertools::Itertools; | 333 | use itertools::Itertools; |
302 | use ra_db::fixture::WithFixture; | 334 | use ra_db::fixture::WithFixture; |
@@ -339,6 +371,7 @@ mod tests { | |||
339 | ItemInNs::Values(_) => "v", | 371 | ItemInNs::Values(_) => "v", |
340 | ItemInNs::Macros(_) => "m", | 372 | ItemInNs::Macros(_) => "m", |
341 | }; | 373 | }; |
374 | let item = assoc_to_trait(&db, item); | ||
342 | item.krate(db.upcast()).map(|krate| { | 375 | item.krate(db.upcast()).map(|krate| { |
343 | let map = db.import_map(krate); | 376 | let map = db.import_map(krate); |
344 | let path = map.path_of(item).unwrap(); | 377 | let path = map.path_of(item).unwrap(); |
@@ -353,6 +386,29 @@ mod tests { | |||
353 | .join("\n") | 386 | .join("\n") |
354 | } | 387 | } |
355 | 388 | ||
389 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { | ||
390 | let assoc: AssocItemId = match item { | ||
391 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { | ||
392 | ModuleDefId::TypeAliasId(it) => it.into(), | ||
393 | ModuleDefId::FunctionId(it) => it.into(), | ||
394 | ModuleDefId::ConstId(it) => it.into(), | ||
395 | _ => return item, | ||
396 | }, | ||
397 | _ => return item, | ||
398 | }; | ||
399 | |||
400 | let container = match assoc { | ||
401 | AssocItemId::FunctionId(it) => it.lookup(db).container, | ||
402 | AssocItemId::ConstId(it) => it.lookup(db).container, | ||
403 | AssocItemId::TypeAliasId(it) => it.lookup(db).container, | ||
404 | }; | ||
405 | |||
406 | match container { | ||
407 | AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), | ||
408 | _ => item, | ||
409 | } | ||
410 | } | ||
411 | |||
356 | #[test] | 412 | #[test] |
357 | fn smoke() { | 413 | fn smoke() { |
358 | let map = import_map( | 414 | let map = import_map( |
@@ -610,6 +666,7 @@ mod tests { | |||
610 | dep::Fmt (m) | 666 | dep::Fmt (m) |
611 | dep::fmt::Display (t) | 667 | dep::fmt::Display (t) |
612 | dep::format (v) | 668 | dep::format (v) |
669 | dep::fmt::Display (t) | ||
613 | "###); | 670 | "###); |
614 | 671 | ||
615 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | 672 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); |
@@ -618,6 +675,7 @@ mod tests { | |||
618 | dep::Fmt (t) | 675 | dep::Fmt (t) |
619 | dep::Fmt (v) | 676 | dep::Fmt (v) |
620 | dep::Fmt (m) | 677 | dep::Fmt (m) |
678 | dep::fmt::Display (t) | ||
621 | "###); | 679 | "###); |
622 | } | 680 | } |
623 | 681 | ||
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 47a1d393d..361fc8eea 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -45,7 +45,7 @@ impl ops::Index<Target> for CargoWorkspace { | |||
45 | } | 45 | } |
46 | } | 46 | } |
47 | 47 | ||
48 | #[derive(Clone, Debug, PartialEq, Eq)] | 48 | #[derive(Default, Clone, Debug, PartialEq, Eq)] |
49 | pub struct CargoConfig { | 49 | pub struct CargoConfig { |
50 | /// Do not activate the `default` feature. | 50 | /// Do not activate the `default` feature. |
51 | pub no_default_features: bool, | 51 | pub no_default_features: bool, |
@@ -64,18 +64,6 @@ pub struct CargoConfig { | |||
64 | pub target: Option<String>, | 64 | pub target: Option<String>, |
65 | } | 65 | } |
66 | 66 | ||
67 | impl Default for CargoConfig { | ||
68 | fn default() -> Self { | ||
69 | CargoConfig { | ||
70 | no_default_features: false, | ||
71 | all_features: false, | ||
72 | features: Vec::new(), | ||
73 | load_out_dirs_from_check: false, | ||
74 | target: None, | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | pub type Package = Idx<PackageData>; | 67 | pub type Package = Idx<PackageData>; |
80 | 68 | ||
81 | pub type Target = Idx<TargetData>; | 69 | pub type Target = Idx<TargetData>; |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 8dbf4e6ea..464c3b2e3 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -150,7 +150,7 @@ impl ProjectManifest { | |||
150 | impl ProjectWorkspace { | 150 | impl ProjectWorkspace { |
151 | pub fn load( | 151 | pub fn load( |
152 | manifest: ProjectManifest, | 152 | manifest: ProjectManifest, |
153 | cargo_features: &CargoConfig, | 153 | cargo_config: &CargoConfig, |
154 | with_sysroot: bool, | 154 | with_sysroot: bool, |
155 | ) -> Result<ProjectWorkspace> { | 155 | ) -> Result<ProjectWorkspace> { |
156 | let res = match manifest { | 156 | let res = match manifest { |
@@ -166,7 +166,7 @@ impl ProjectWorkspace { | |||
166 | ProjectWorkspace::Json { project } | 166 | ProjectWorkspace::Json { project } |
167 | } | 167 | } |
168 | ProjectManifest::CargoToml(cargo_toml) => { | 168 | ProjectManifest::CargoToml(cargo_toml) => { |
169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) | 169 | let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_config) |
170 | .with_context(|| { | 170 | .with_context(|| { |
171 | format!( | 171 | format!( |
172 | "Failed to read Cargo metadata from Cargo.toml file {}", | 172 | "Failed to read Cargo metadata from Cargo.toml file {}", |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 6c311648a..21acfe644 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -130,6 +130,7 @@ pub struct ClientCapsConfig { | |||
130 | pub code_action_group: bool, | 130 | pub code_action_group: bool, |
131 | pub resolve_code_action: bool, | 131 | pub resolve_code_action: bool, |
132 | pub hover_actions: bool, | 132 | pub hover_actions: bool, |
133 | pub status_notification: bool, | ||
133 | } | 134 | } |
134 | 135 | ||
135 | impl Config { | 136 | impl Config { |
@@ -365,6 +366,7 @@ impl Config { | |||
365 | self.client_caps.code_action_group = get_bool("codeActionGroup"); | 366 | self.client_caps.code_action_group = get_bool("codeActionGroup"); |
366 | self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); | 367 | self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); |
367 | self.client_caps.hover_actions = get_bool("hoverActions"); | 368 | self.client_caps.hover_actions = get_bool("hoverActions"); |
369 | self.client_caps.status_notification = get_bool("statusNotification"); | ||
368 | } | 370 | } |
369 | } | 371 | } |
370 | } | 372 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b7b4edf66..640b3959d 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -31,6 +31,8 @@ use crate::{ | |||
31 | pub(crate) enum Status { | 31 | pub(crate) enum Status { |
32 | Loading, | 32 | Loading, |
33 | Ready, | 33 | Ready, |
34 | Invalid, | ||
35 | NeedsReload, | ||
34 | } | 36 | } |
35 | 37 | ||
36 | impl Default for Status { | 38 | impl Default for Status { |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 82207bbb8..d225ad5ff 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | use std::{collections::HashMap, path::PathBuf}; | 3 | use std::{collections::HashMap, path::PathBuf}; |
4 | 4 | ||
5 | use lsp_types::request::Request; | 5 | use lsp_types::request::Request; |
6 | use lsp_types::{Position, Range, TextDocumentIdentifier}; | 6 | use lsp_types::{notification::Notification, Position, Range, TextDocumentIdentifier}; |
7 | use serde::{Deserialize, Serialize}; | 7 | use serde::{Deserialize, Serialize}; |
8 | 8 | ||
9 | pub enum AnalyzerStatus {} | 9 | pub enum AnalyzerStatus {} |
@@ -208,6 +208,22 @@ pub struct SsrParams { | |||
208 | pub parse_only: bool, | 208 | pub parse_only: bool, |
209 | } | 209 | } |
210 | 210 | ||
211 | pub enum StatusNotification {} | ||
212 | |||
213 | #[serde(rename_all = "camelCase")] | ||
214 | #[derive(Serialize, Deserialize)] | ||
215 | pub enum Status { | ||
216 | Loading, | ||
217 | Ready, | ||
218 | NeedsReload, | ||
219 | Invalid, | ||
220 | } | ||
221 | |||
222 | impl Notification for StatusNotification { | ||
223 | type Params = Status; | ||
224 | const METHOD: &'static str = "rust-analyzer/status"; | ||
225 | } | ||
226 | |||
211 | pub enum CodeActionRequest {} | 227 | pub enum CodeActionRequest {} |
212 | 228 | ||
213 | impl Request for CodeActionRequest { | 229 | impl Request for CodeActionRequest { |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index e03038b25..cfde55431 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -21,6 +21,7 @@ use crate::{ | |||
21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, | 21 | lsp_utils::{apply_document_changes, is_canceled, notification_is, Progress}, |
22 | Result, | 22 | Result, |
23 | }; | 23 | }; |
24 | use ra_project_model::ProjectWorkspace; | ||
24 | 25 | ||
25 | pub fn main_loop(config: Config, connection: Connection) -> Result<()> { | 26 | pub fn main_loop(config: Config, connection: Connection) -> Result<()> { |
26 | log::info!("initial config: {:#?}", config); | 27 | log::info!("initial config: {:#?}", config); |
@@ -58,6 +59,7 @@ enum Event { | |||
58 | pub(crate) enum Task { | 59 | pub(crate) enum Task { |
59 | Response(Response), | 60 | Response(Response), |
60 | Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), | 61 | Diagnostics(Vec<(FileId, Vec<lsp_types::Diagnostic>)>), |
62 | Workspaces(Vec<anyhow::Result<ProjectWorkspace>>), | ||
61 | Unit, | 63 | Unit, |
62 | } | 64 | } |
63 | 65 | ||
@@ -111,7 +113,44 @@ impl GlobalState { | |||
111 | } | 113 | } |
112 | 114 | ||
113 | fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { | 115 | fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { |
114 | self.reload(); | 116 | if self.config.linked_projects.is_empty() && self.config.notifications.cargo_toml_not_found |
117 | { | ||
118 | self.show_message( | ||
119 | lsp_types::MessageType::Error, | ||
120 | "rust-analyzer failed to discover workspace".to_string(), | ||
121 | ); | ||
122 | }; | ||
123 | |||
124 | let registration_options = lsp_types::TextDocumentRegistrationOptions { | ||
125 | document_selector: Some(vec![ | ||
126 | lsp_types::DocumentFilter { | ||
127 | language: None, | ||
128 | scheme: None, | ||
129 | pattern: Some("**/*.rs".into()), | ||
130 | }, | ||
131 | lsp_types::DocumentFilter { | ||
132 | language: None, | ||
133 | scheme: None, | ||
134 | pattern: Some("**/Cargo.toml".into()), | ||
135 | }, | ||
136 | lsp_types::DocumentFilter { | ||
137 | language: None, | ||
138 | scheme: None, | ||
139 | pattern: Some("**/Cargo.lock".into()), | ||
140 | }, | ||
141 | ]), | ||
142 | }; | ||
143 | let registration = lsp_types::Registration { | ||
144 | id: "textDocument/didSave".to_string(), | ||
145 | method: "textDocument/didSave".to_string(), | ||
146 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | ||
147 | }; | ||
148 | self.send_request::<lsp_types::request::RegisterCapability>( | ||
149 | lsp_types::RegistrationParams { registrations: vec![registration] }, | ||
150 | |_, _| (), | ||
151 | ); | ||
152 | |||
153 | self.fetch_workspaces(); | ||
115 | 154 | ||
116 | while let Some(event) = self.next_event(&inbox) { | 155 | while let Some(event) = self.next_event(&inbox) { |
117 | if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { | 156 | if let Event::Lsp(lsp_server::Message::Notification(not)) = &event { |
@@ -153,6 +192,7 @@ impl GlobalState { | |||
153 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) | 192 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) |
154 | } | 193 | } |
155 | } | 194 | } |
195 | Task::Workspaces(workspaces) => self.switch_workspaces(workspaces), | ||
156 | Task::Unit => (), | 196 | Task::Unit => (), |
157 | } | 197 | } |
158 | self.analysis_host.maybe_collect_garbage(); | 198 | self.analysis_host.maybe_collect_garbage(); |
@@ -169,16 +209,16 @@ impl GlobalState { | |||
169 | } | 209 | } |
170 | vfs::loader::Message::Progress { n_total, n_done } => { | 210 | vfs::loader::Message::Progress { n_total, n_done } => { |
171 | if n_total == 0 { | 211 | if n_total == 0 { |
172 | self.status = Status::Ready; | 212 | self.transition(Status::Invalid); |
173 | } else { | 213 | } else { |
174 | let state = if n_done == 0 { | 214 | let state = if n_done == 0 { |
175 | self.status = Status::Loading; | 215 | self.transition(Status::Loading); |
176 | Progress::Begin | 216 | Progress::Begin |
177 | } else if n_done < n_total { | 217 | } else if n_done < n_total { |
178 | Progress::Report | 218 | Progress::Report |
179 | } else { | 219 | } else { |
180 | assert_eq!(n_done, n_total); | 220 | assert_eq!(n_done, n_total); |
181 | self.status = Status::Ready; | 221 | self.transition(Status::Ready); |
182 | Progress::End | 222 | Progress::End |
183 | }; | 223 | }; |
184 | self.report_progress( | 224 | self.report_progress( |
@@ -274,11 +314,24 @@ impl GlobalState { | |||
274 | Ok(()) | 314 | Ok(()) |
275 | } | 315 | } |
276 | 316 | ||
317 | fn transition(&mut self, new_status: Status) { | ||
318 | self.status = Status::Ready; | ||
319 | if self.config.client_caps.status_notification { | ||
320 | let lsp_status = match new_status { | ||
321 | Status::Loading => lsp_ext::Status::Loading, | ||
322 | Status::Ready => lsp_ext::Status::Ready, | ||
323 | Status::Invalid => lsp_ext::Status::Invalid, | ||
324 | Status::NeedsReload => lsp_ext::Status::NeedsReload, | ||
325 | }; | ||
326 | self.send_notification::<lsp_ext::StatusNotification>(lsp_status); | ||
327 | } | ||
328 | } | ||
329 | |||
277 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { | 330 | fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { |
278 | self.register_request(&req, request_received); | 331 | self.register_request(&req, request_received); |
279 | 332 | ||
280 | RequestDispatcher { req: Some(req), global_state: self } | 333 | RequestDispatcher { req: Some(req), global_state: self } |
281 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.reload()))? | 334 | .on_sync::<lsp_ext::ReloadWorkspace>(|s, ()| Ok(s.fetch_workspaces()))? |
282 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? | 335 | .on_sync::<lsp_ext::JoinLines>(|s, p| handlers::handle_join_lines(s.snapshot(), p))? |
283 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? | 336 | .on_sync::<lsp_ext::OnEnter>(|s, p| handlers::handle_on_enter(s.snapshot(), p))? |
284 | .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))? | 337 | .on_sync::<lsp_types::request::Shutdown>(|_, ()| Ok(()))? |
@@ -383,10 +436,16 @@ impl GlobalState { | |||
383 | ); | 436 | ); |
384 | Ok(()) | 437 | Ok(()) |
385 | })? | 438 | })? |
386 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| { | 439 | .on::<lsp_types::notification::DidSaveTextDocument>(|this, params| { |
387 | if let Some(flycheck) = &this.flycheck { | 440 | if let Some(flycheck) = &this.flycheck { |
388 | flycheck.handle.update(); | 441 | flycheck.handle.update(); |
389 | } | 442 | } |
443 | let uri = params.text_document.uri.as_str(); | ||
444 | if uri.ends_with("Cargo.toml") || uri.ends_with("Cargo.lock") { | ||
445 | if matches!(this.status, Status::Ready | Status::Invalid) { | ||
446 | this.transition(Status::NeedsReload); | ||
447 | } | ||
448 | } | ||
390 | Ok(()) | 449 | Ok(()) |
391 | })? | 450 | })? |
392 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { | 451 | .on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| { |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 0c1fd1b8b..74c3344df 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -11,6 +11,7 @@ use vfs::{file_set::FileSetConfig, AbsPath}; | |||
11 | use crate::{ | 11 | use crate::{ |
12 | config::{Config, FilesWatcher, LinkedProject}, | 12 | config::{Config, FilesWatcher, LinkedProject}, |
13 | global_state::{GlobalState, Handle}, | 13 | global_state::{GlobalState, Handle}, |
14 | main_loop::Task, | ||
14 | }; | 15 | }; |
15 | 16 | ||
16 | impl GlobalState { | 17 | impl GlobalState { |
@@ -20,52 +21,51 @@ impl GlobalState { | |||
20 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); | 21 | self.analysis_host.update_lru_capacity(old_config.lru_capacity); |
21 | } | 22 | } |
22 | if self.config.linked_projects != old_config.linked_projects { | 23 | if self.config.linked_projects != old_config.linked_projects { |
23 | self.reload() | 24 | self.fetch_workspaces() |
24 | } else if self.config.flycheck != old_config.flycheck { | 25 | } else if self.config.flycheck != old_config.flycheck { |
25 | self.reload_flycheck(); | 26 | self.reload_flycheck(); |
26 | } | 27 | } |
27 | } | 28 | } |
28 | pub(crate) fn reload(&mut self) { | 29 | pub(crate) fn fetch_workspaces(&mut self) { |
29 | log::info!("reloading projects: {:?}", self.config.linked_projects); | 30 | self.task_pool.handle.spawn({ |
30 | let workspaces = { | 31 | let linked_projects = self.config.linked_projects.clone(); |
31 | if self.config.linked_projects.is_empty() | 32 | let cargo_config = self.config.cargo.clone(); |
32 | && self.config.notifications.cargo_toml_not_found | 33 | let with_sysroot = self.config.with_sysroot.clone(); |
33 | { | 34 | move || { |
34 | self.show_message( | 35 | let workspaces = linked_projects |
35 | lsp_types::MessageType::Error, | 36 | .iter() |
36 | "rust-analyzer failed to discover workspace".to_string(), | 37 | .map(|project| match project { |
37 | ); | 38 | LinkedProject::ProjectManifest(manifest) => { |
38 | }; | 39 | ra_project_model::ProjectWorkspace::load( |
39 | 40 | manifest.clone(), | |
40 | self.config | 41 | &cargo_config, |
41 | .linked_projects | 42 | with_sysroot, |
42 | .iter() | 43 | ) |
43 | .map(|project| match project { | 44 | } |
44 | LinkedProject::ProjectManifest(manifest) => { | 45 | LinkedProject::InlineJsonProject(it) => { |
45 | ra_project_model::ProjectWorkspace::load( | 46 | Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) |
46 | manifest.clone(), | 47 | } |
47 | &self.config.cargo, | ||
48 | self.config.with_sysroot, | ||
49 | ) | ||
50 | } | ||
51 | LinkedProject::InlineJsonProject(it) => { | ||
52 | Ok(ra_project_model::ProjectWorkspace::Json { project: it.clone() }) | ||
53 | } | ||
54 | }) | ||
55 | .collect::<Vec<_>>() | ||
56 | .into_iter() | ||
57 | .filter_map(|res| { | ||
58 | res.map_err(|err| { | ||
59 | log::error!("failed to load workspace: {:#}", err); | ||
60 | self.show_message( | ||
61 | lsp_types::MessageType::Error, | ||
62 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
63 | ); | ||
64 | }) | 48 | }) |
65 | .ok() | 49 | .collect::<Vec<_>>(); |
50 | Task::Workspaces(workspaces) | ||
51 | } | ||
52 | }); | ||
53 | } | ||
54 | pub(crate) fn switch_workspaces(&mut self, workspaces: Vec<anyhow::Result<ProjectWorkspace>>) { | ||
55 | log::info!("reloading projects: {:?}", self.config.linked_projects); | ||
56 | let workspaces = workspaces | ||
57 | .into_iter() | ||
58 | .filter_map(|res| { | ||
59 | res.map_err(|err| { | ||
60 | log::error!("failed to load workspace: {:#}", err); | ||
61 | self.show_message( | ||
62 | lsp_types::MessageType::Error, | ||
63 | format!("rust-analyzer failed to load workspace: {:#}", err), | ||
64 | ); | ||
66 | }) | 65 | }) |
67 | .collect::<Vec<_>>() | 66 | .ok() |
68 | }; | 67 | }) |
68 | .collect::<Vec<_>>(); | ||
69 | 69 | ||
70 | if let FilesWatcher::Client = self.config.files.watcher { | 70 | if let FilesWatcher::Client = self.config.files.watcher { |
71 | let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { | 71 | let registration_options = lsp_types::DidChangeWatchedFilesRegistrationOptions { |
@@ -78,7 +78,7 @@ impl GlobalState { | |||
78 | .collect(), | 78 | .collect(), |
79 | }; | 79 | }; |
80 | let registration = lsp_types::Registration { | 80 | let registration = lsp_types::Registration { |
81 | id: "file-watcher".to_string(), | 81 | id: "workspace/didChangeWatchedFiles".to_string(), |
82 | method: "workspace/didChangeWatchedFiles".to_string(), | 82 | method: "workspace/didChangeWatchedFiles".to_string(), |
83 | register_options: Some(serde_json::to_value(registration_options).unwrap()), | 83 | register_options: Some(serde_json::to_value(registration_options).unwrap()), |
84 | }; | 84 | }; |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index cc079790e..7b908d30c 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -447,6 +447,7 @@ version = \"0.0.0\" | |||
447 | ", | 447 | ", |
448 | ) | 448 | ) |
449 | .server(); | 449 | .server(); |
450 | server.wait_until_workspace_is_loaded(); | ||
450 | 451 | ||
451 | server.request::<OnEnter>( | 452 | server.request::<OnEnter>( |
452 | TextDocumentPositionParams { | 453 | TextDocumentPositionParams { |
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index 49f194f7e..7bf687794 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs | |||
@@ -176,12 +176,19 @@ impl Server { | |||
176 | while let Some(msg) = self.recv() { | 176 | while let Some(msg) = self.recv() { |
177 | match msg { | 177 | match msg { |
178 | Message::Request(req) => { | 178 | Message::Request(req) => { |
179 | if req.method != "window/workDoneProgress/create" | 179 | if req.method == "window/workDoneProgress/create" { |
180 | && !(req.method == "client/registerCapability" | 180 | continue; |
181 | && req.params.to_string().contains("workspace/didChangeWatchedFiles")) | ||
182 | { | ||
183 | panic!("unexpected request: {:?}", req) | ||
184 | } | 181 | } |
182 | if req.method == "client/registerCapability" { | ||
183 | let params = req.params.to_string(); | ||
184 | if ["workspace/didChangeWatchedFiles", "textDocument/didSave"] | ||
185 | .iter() | ||
186 | .any(|&it| params.contains(it)) | ||
187 | { | ||
188 | continue; | ||
189 | } | ||
190 | } | ||
191 | panic!("unexpected request: {:?}", req) | ||
185 | } | 192 | } |
186 | Message::Notification(_) => (), | 193 | Message::Notification(_) => (), |
187 | Message::Response(res) => { | 194 | Message::Response(res) => { |