diff options
44 files changed, 1741 insertions, 1262 deletions
diff --git a/Cargo.lock b/Cargo.lock index c6ed5107c..42a962cf6 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -351,6 +351,11 @@ version = "1.1.0" | |||
351 | source = "registry+https://github.com/rust-lang/crates.io-index" | 351 | source = "registry+https://github.com/rust-lang/crates.io-index" |
352 | 352 | ||
353 | [[package]] | 353 | [[package]] |
354 | name = "id-arena" | ||
355 | version = "2.0.0" | ||
356 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
357 | |||
358 | [[package]] | ||
354 | name = "idna" | 359 | name = "idna" |
355 | version = "0.1.5" | 360 | version = "0.1.5" |
356 | source = "registry+https://github.com/rust-lang/crates.io-index" | 361 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -601,7 +606,9 @@ dependencies = [ | |||
601 | "fst 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", | 606 | "fst 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", |
602 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | 607 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", |
603 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", | 608 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", |
609 | "ra_db 0.1.0", | ||
604 | "ra_editor 0.1.0", | 610 | "ra_editor 0.1.0", |
611 | "ra_hir 0.1.0", | ||
605 | "ra_syntax 0.1.0", | 612 | "ra_syntax 0.1.0", |
606 | "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", | 613 | "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", |
607 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | 614 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
@@ -623,6 +630,19 @@ dependencies = [ | |||
623 | ] | 630 | ] |
624 | 631 | ||
625 | [[package]] | 632 | [[package]] |
633 | name = "ra_db" | ||
634 | version = "0.1.0" | ||
635 | dependencies = [ | ||
636 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||
637 | "ra_editor 0.1.0", | ||
638 | "ra_syntax 0.1.0", | ||
639 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
640 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
641 | "salsa 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
642 | "test_utils 0.1.0", | ||
643 | ] | ||
644 | |||
645 | [[package]] | ||
626 | name = "ra_editor" | 646 | name = "ra_editor" |
627 | version = "0.1.0" | 647 | version = "0.1.0" |
628 | dependencies = [ | 648 | dependencies = [ |
@@ -635,6 +655,22 @@ dependencies = [ | |||
635 | ] | 655 | ] |
636 | 656 | ||
637 | [[package]] | 657 | [[package]] |
658 | name = "ra_hir" | ||
659 | version = "0.1.0" | ||
660 | dependencies = [ | ||
661 | "id-arena 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
662 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||
663 | "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||
664 | "ra_db 0.1.0", | ||
665 | "ra_editor 0.1.0", | ||
666 | "ra_syntax 0.1.0", | ||
667 | "relative-path 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
668 | "rustc-hash 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
669 | "salsa 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
670 | "test_utils 0.1.0", | ||
671 | ] | ||
672 | |||
673 | [[package]] | ||
638 | name = "ra_lsp_server" | 674 | name = "ra_lsp_server" |
639 | version = "0.1.0" | 675 | version = "0.1.0" |
640 | dependencies = [ | 676 | dependencies = [ |
@@ -1301,6 +1337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1301 | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" | 1337 | "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" |
1302 | "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" | 1338 | "checksum heck 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ea04fa3ead4e05e51a7c806fc07271fdbde4e246a6c6d1efd52e72230b771b82" |
1303 | "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" | 1339 | "checksum humansize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b6cab2627acfc432780848602f3f558f7e9dd427352224b0d9324025796d2a5e" |
1340 | "checksum id-arena 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a7250033feafee46a1cecd2c2616a64aec1d064f38c9ae2bdd297728542843e" | ||
1304 | "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" | 1341 | "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" |
1305 | "checksum im 12.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9c7f9bb8aee47fc16d535a705f7867a9fc83bb822e5e1043bb98e77ffeed3c" | 1342 | "checksum im 12.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9c7f9bb8aee47fc16d535a705f7867a9fc83bb822e5e1043bb98e77ffeed3c" |
1306 | "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" | 1343 | "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" |
diff --git a/crates/gen_lsp_server/src/msg.rs b/crates/gen_lsp_server/src/msg.rs index e0d0aeab5..e1b27c808 100644 --- a/crates/gen_lsp_server/src/msg.rs +++ b/crates/gen_lsp_server/src/msg.rs | |||
@@ -94,7 +94,7 @@ impl RawRequest { | |||
94 | R::Params: Serialize, | 94 | R::Params: Serialize, |
95 | { | 95 | { |
96 | RawRequest { | 96 | RawRequest { |
97 | id: id, | 97 | id, |
98 | method: R::METHOD.to_string(), | 98 | method: R::METHOD.to_string(), |
99 | params: to_value(params).unwrap(), | 99 | params: to_value(params).unwrap(), |
100 | } | 100 | } |
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml index b4a1a09b5..fe9765a66 100644 --- a/crates/ra_analysis/Cargo.toml +++ b/crates/ra_analysis/Cargo.toml | |||
@@ -14,4 +14,6 @@ rustc-hash = "1.0" | |||
14 | parking_lot = "0.6.4" | 14 | parking_lot = "0.6.4" |
15 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_editor = { path = "../ra_editor" } | 16 | ra_editor = { path = "../ra_editor" } |
17 | ra_db = { path = "../ra_db" } | ||
18 | hir = { path = "../ra_hir", package = "ra_hir" } | ||
17 | test_utils = { path = "../test_utils" } | 19 | test_utils = { path = "../test_utils" } |
diff --git a/crates/ra_analysis/src/arena.rs b/crates/ra_analysis/src/arena.rs deleted file mode 100644 index 98ed89274..000000000 --- a/crates/ra_analysis/src/arena.rs +++ /dev/null | |||
@@ -1,96 +0,0 @@ | |||
1 | //! A simple id-based arena, similar to https://github.com/fitzgen/id-arena. | ||
2 | //! We use our own version for more compact id's and to allow inherent impls | ||
3 | //! on Ids. | ||
4 | |||
5 | use std::{ | ||
6 | fmt, | ||
7 | ops::{Index, IndexMut}, | ||
8 | hash::{Hash, Hasher}, | ||
9 | marker::PhantomData, | ||
10 | }; | ||
11 | |||
12 | pub(crate) struct Id<T> { | ||
13 | idx: u32, | ||
14 | _ty: PhantomData<fn() -> T>, | ||
15 | } | ||
16 | |||
17 | impl<T> fmt::Debug for Id<T> { | ||
18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
19 | f.debug_tuple("Id").field(&self.idx).finish() | ||
20 | } | ||
21 | } | ||
22 | impl<T> Copy for Id<T> {} | ||
23 | impl<T> Clone for Id<T> { | ||
24 | fn clone(&self) -> Id<T> { | ||
25 | *self | ||
26 | } | ||
27 | } | ||
28 | |||
29 | impl<T> PartialEq for Id<T> { | ||
30 | fn eq(&self, other: &Id<T>) -> bool { | ||
31 | self.idx == other.idx | ||
32 | } | ||
33 | } | ||
34 | |||
35 | impl<T> Eq for Id<T> {} | ||
36 | |||
37 | impl<T> Hash for Id<T> { | ||
38 | fn hash<H: Hasher>(&self, h: &mut H) { | ||
39 | self.idx.hash(h); | ||
40 | } | ||
41 | } | ||
42 | |||
43 | #[derive(Debug, PartialEq, Eq)] | ||
44 | pub(crate) struct Arena<T> { | ||
45 | data: Vec<T>, | ||
46 | } | ||
47 | |||
48 | impl<T> Default for Arena<T> { | ||
49 | fn default() -> Arena<T> { | ||
50 | Arena { data: Vec::new() } | ||
51 | } | ||
52 | } | ||
53 | |||
54 | impl<T> Arena<T> { | ||
55 | pub(crate) fn push(&mut self, value: T) -> Id<T> { | ||
56 | let id = self.data.len() as u32; | ||
57 | self.data.push(value); | ||
58 | Id { | ||
59 | idx: id as u32, | ||
60 | _ty: PhantomData, | ||
61 | } | ||
62 | } | ||
63 | |||
64 | pub(crate) fn keys<'a>(&'a self) -> impl Iterator<Item = Id<T>> + 'a { | ||
65 | (0..(self.data.len() as u32)).into_iter().map(|idx| Id { | ||
66 | idx, | ||
67 | _ty: PhantomData, | ||
68 | }) | ||
69 | } | ||
70 | |||
71 | pub(crate) fn items<'a>(&'a self) -> impl Iterator<Item = (Id<T>, &T)> + 'a { | ||
72 | self.data.iter().enumerate().map(|(idx, item)| { | ||
73 | let idx = idx as u32; | ||
74 | ( | ||
75 | Id { | ||
76 | idx, | ||
77 | _ty: PhantomData, | ||
78 | }, | ||
79 | item, | ||
80 | ) | ||
81 | }) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl<T> Index<Id<T>> for Arena<T> { | ||
86 | type Output = T; | ||
87 | fn index(&self, id: Id<T>) -> &T { | ||
88 | &self.data[id.idx as usize] | ||
89 | } | ||
90 | } | ||
91 | |||
92 | impl<T> IndexMut<Id<T>> for Arena<T> { | ||
93 | fn index_mut(&mut self, id: Id<T>) -> &mut T { | ||
94 | &mut self.data[id.idx as usize] | ||
95 | } | ||
96 | } | ||
diff --git a/crates/ra_analysis/src/completion/mod.rs b/crates/ra_analysis/src/completion/mod.rs index d7acca044..124da486a 100644 --- a/crates/ra_analysis/src/completion/mod.rs +++ b/crates/ra_analysis/src/completion/mod.rs | |||
@@ -7,13 +7,11 @@ use ra_syntax::{ | |||
7 | AstNode, AtomEdit, | 7 | AstNode, AtomEdit, |
8 | SyntaxNodeRef, | 8 | SyntaxNodeRef, |
9 | }; | 9 | }; |
10 | use ra_db::SyntaxDatabase; | ||
10 | use rustc_hash::{FxHashMap}; | 11 | use rustc_hash::{FxHashMap}; |
11 | 12 | ||
12 | use crate::{ | 13 | use crate::{ |
13 | db::{self, SyntaxDatabase}, | 14 | db, |
14 | descriptors::{ | ||
15 | module::{ModuleDescriptor} | ||
16 | }, | ||
17 | Cancelable, FilePosition | 15 | Cancelable, FilePosition |
18 | }; | 16 | }; |
19 | 17 | ||
@@ -31,17 +29,14 @@ pub(crate) fn completions( | |||
31 | db: &db::RootDatabase, | 29 | db: &db::RootDatabase, |
32 | position: FilePosition, | 30 | position: FilePosition, |
33 | ) -> Cancelable<Option<Vec<CompletionItem>>> { | 31 | ) -> Cancelable<Option<Vec<CompletionItem>>> { |
34 | let original_file = db.file_syntax(position.file_id); | 32 | let original_file = db.source_file(position.file_id); |
35 | // Insert a fake ident to get a valid parse tree | 33 | // Insert a fake ident to get a valid parse tree |
36 | let file = { | 34 | let file = { |
37 | let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); | 35 | let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string()); |
38 | original_file.reparse(&edit) | 36 | original_file.reparse(&edit) |
39 | }; | 37 | }; |
40 | 38 | ||
41 | let module = match ModuleDescriptor::guess_from_position(db, position)? { | 39 | let module = ctry!(hir::Module::guess_from_position(db, position)?); |
42 | None => return Ok(None), | ||
43 | Some(it) => it, | ||
44 | }; | ||
45 | 40 | ||
46 | let mut res = Vec::new(); | 41 | let mut res = Vec::new(); |
47 | let mut has_completions = false; | 42 | let mut has_completions = false; |
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index e52062107..8ea7478a8 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs | |||
@@ -6,22 +6,23 @@ use ra_syntax::{ | |||
6 | ast::{self, LoopBodyOwner}, | 6 | ast::{self, LoopBodyOwner}, |
7 | SyntaxKind::*, | 7 | SyntaxKind::*, |
8 | }; | 8 | }; |
9 | use hir::{ | ||
10 | self, | ||
11 | FnScopes, | ||
12 | Def, | ||
13 | Path, | ||
14 | }; | ||
9 | 15 | ||
10 | use crate::{ | 16 | use crate::{ |
11 | db::RootDatabase, | 17 | db::RootDatabase, |
12 | completion::CompletionItem, | 18 | completion::CompletionItem, |
13 | descriptors::{ | ||
14 | module::{ModuleDescriptor}, | ||
15 | function::FnScopes, | ||
16 | Path, | ||
17 | }, | ||
18 | Cancelable | 19 | Cancelable |
19 | }; | 20 | }; |
20 | 21 | ||
21 | pub(super) fn completions( | 22 | pub(super) fn completions( |
22 | acc: &mut Vec<CompletionItem>, | 23 | acc: &mut Vec<CompletionItem>, |
23 | db: &RootDatabase, | 24 | db: &RootDatabase, |
24 | module: &ModuleDescriptor, | 25 | module: &hir::Module, |
25 | file: &SourceFileNode, | 26 | file: &SourceFileNode, |
26 | name_ref: ast::NameRef, | 27 | name_ref: ast::NameRef, |
27 | ) -> Cancelable<()> { | 28 | ) -> Cancelable<()> { |
@@ -42,13 +43,15 @@ pub(super) fn completions( | |||
42 | let module_scope = module.scope(db)?; | 43 | let module_scope = module.scope(db)?; |
43 | acc.extend( | 44 | acc.extend( |
44 | module_scope | 45 | module_scope |
45 | .items | 46 | .entries() |
46 | .iter() | ||
47 | .filter(|(_name, res)| { | 47 | .filter(|(_name, res)| { |
48 | // Don't expose this item | 48 | // Don't expose this item |
49 | match res.import_name { | 49 | match res.import { |
50 | None => true, | 50 | None => true, |
51 | Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()), | 51 | Some(import) => { |
52 | let range = import.range(db, module.source().file_id()); | ||
53 | !range.is_subrange(&name_ref.syntax().range()) | ||
54 | } | ||
52 | } | 55 | } |
53 | }) | 56 | }) |
54 | .map(|(name, _res)| CompletionItem { | 57 | .map(|(name, _res)| CompletionItem { |
@@ -147,26 +150,27 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi | |||
147 | fn complete_path( | 150 | fn complete_path( |
148 | acc: &mut Vec<CompletionItem>, | 151 | acc: &mut Vec<CompletionItem>, |
149 | db: &RootDatabase, | 152 | db: &RootDatabase, |
150 | module: &ModuleDescriptor, | 153 | module: &hir::Module, |
151 | mut path: Path, | 154 | mut path: Path, |
152 | ) -> Cancelable<()> { | 155 | ) -> Cancelable<()> { |
153 | if path.segments.is_empty() { | 156 | if path.segments.is_empty() { |
154 | return Ok(()); | 157 | return Ok(()); |
155 | } | 158 | } |
156 | path.segments.pop(); | 159 | path.segments.pop(); |
157 | let target_module = match module.resolve_path(path) { | 160 | let def_id = match module.resolve_path(db, path)? { |
158 | None => return Ok(()), | 161 | None => return Ok(()), |
159 | Some(it) => it, | 162 | Some(it) => it, |
160 | }; | 163 | }; |
164 | let target_module = match def_id.resolve(db)? { | ||
165 | Def::Module(it) => it, | ||
166 | Def::Item => return Ok(()), | ||
167 | }; | ||
161 | let module_scope = target_module.scope(db)?; | 168 | let module_scope = target_module.scope(db)?; |
162 | let completions = module_scope | 169 | let completions = module_scope.entries().map(|(name, _res)| CompletionItem { |
163 | .items | 170 | label: name.to_string(), |
164 | .iter() | 171 | lookup: None, |
165 | .map(|(name, _res)| CompletionItem { | 172 | snippet: None, |
166 | label: name.to_string(), | 173 | }); |
167 | lookup: None, | ||
168 | snippet: None, | ||
169 | }); | ||
170 | acc.extend(completions); | 174 | acc.extend(completions); |
171 | Ok(()) | 175 | Ok(()) |
172 | } | 176 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index 6b56f99ac..df2ef293d 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -1,171 +1,95 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | #[cfg(test)] | ||
3 | use parking_lot::Mutex; | ||
4 | use ra_editor::LineIndex; | ||
5 | use ra_syntax::{SourceFileNode, SyntaxNode}; | ||
6 | use salsa::{self, Database}; | 2 | use salsa::{self, Database}; |
3 | use ra_db::{LocationIntener, BaseDatabase}; | ||
4 | use hir::{self, DefId, DefLoc, FnId, SourceItemId}; | ||
7 | 5 | ||
8 | use crate::{ | 6 | use crate::{ |
9 | db, | 7 | symbol_index, |
10 | descriptors::{ | ||
11 | DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleTreeQuery, | ||
12 | SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery, | ||
13 | }, | ||
14 | symbol_index::SymbolIndex, | ||
15 | syntax_ptr::SyntaxPtr, | ||
16 | loc2id::{IdMaps, IdDatabase}, | ||
17 | Cancelable, Canceled, FileId, | ||
18 | }; | 8 | }; |
19 | 9 | ||
20 | #[derive(Debug)] | 10 | #[derive(Debug)] |
21 | pub(crate) struct RootDatabase { | 11 | pub(crate) struct RootDatabase { |
22 | #[cfg(test)] | ||
23 | events: Mutex<Option<Vec<salsa::Event<RootDatabase>>>>, | ||
24 | #[cfg(not(test))] | ||
25 | events: (), | ||
26 | |||
27 | runtime: salsa::Runtime<RootDatabase>, | 12 | runtime: salsa::Runtime<RootDatabase>, |
28 | id_maps: IdMaps, | 13 | id_maps: Arc<IdMaps>, |
14 | } | ||
15 | |||
16 | #[derive(Debug, Default)] | ||
17 | struct IdMaps { | ||
18 | fns: LocationIntener<SourceItemId, FnId>, | ||
19 | defs: LocationIntener<DefLoc, DefId>, | ||
29 | } | 20 | } |
30 | 21 | ||
31 | impl salsa::Database for RootDatabase { | 22 | impl salsa::Database for RootDatabase { |
32 | fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { | 23 | fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { |
33 | &self.runtime | 24 | &self.runtime |
34 | } | 25 | } |
35 | |||
36 | #[allow(unused)] | ||
37 | fn salsa_event(&self, event: impl Fn() -> salsa::Event<RootDatabase>) { | ||
38 | #[cfg(test)] | ||
39 | { | ||
40 | let mut events = self.events.lock(); | ||
41 | if let Some(events) = &mut *events { | ||
42 | events.push(event()); | ||
43 | } | ||
44 | } | ||
45 | } | ||
46 | } | 26 | } |
47 | 27 | ||
48 | impl Default for RootDatabase { | 28 | impl Default for RootDatabase { |
49 | fn default() -> RootDatabase { | 29 | fn default() -> RootDatabase { |
50 | let mut db = RootDatabase { | 30 | let mut db = RootDatabase { |
51 | events: Default::default(), | ||
52 | runtime: salsa::Runtime::default(), | 31 | runtime: salsa::Runtime::default(), |
53 | id_maps: IdMaps::default(), | 32 | id_maps: Default::default(), |
54 | }; | 33 | }; |
55 | db.query_mut(crate::input::SourceRootQuery) | 34 | db.query_mut(ra_db::SourceRootQuery) |
56 | .set(crate::input::WORKSPACE, Default::default()); | 35 | .set(ra_db::WORKSPACE, Default::default()); |
57 | db.query_mut(crate::input::CrateGraphQuery) | 36 | db.query_mut(ra_db::CrateGraphQuery) |
58 | .set((), Default::default()); | 37 | .set((), Default::default()); |
59 | db.query_mut(crate::input::LibrariesQuery) | 38 | db.query_mut(ra_db::LibrariesQuery) |
60 | .set((), Default::default()); | 39 | .set((), Default::default()); |
61 | db | 40 | db |
62 | } | 41 | } |
63 | } | 42 | } |
64 | 43 | ||
65 | pub(crate) fn check_canceled(db: &impl salsa::Database) -> Cancelable<()> { | ||
66 | if db.salsa_runtime().is_current_revision_canceled() { | ||
67 | Err(Canceled) | ||
68 | } else { | ||
69 | Ok(()) | ||
70 | } | ||
71 | } | ||
72 | |||
73 | impl salsa::ParallelDatabase for RootDatabase { | 44 | impl salsa::ParallelDatabase for RootDatabase { |
74 | fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { | 45 | fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { |
75 | salsa::Snapshot::new(RootDatabase { | 46 | salsa::Snapshot::new(RootDatabase { |
76 | events: Default::default(), | ||
77 | runtime: self.runtime.snapshot(self), | 47 | runtime: self.runtime.snapshot(self), |
78 | id_maps: self.id_maps.clone(), | 48 | id_maps: self.id_maps.clone(), |
79 | }) | 49 | }) |
80 | } | 50 | } |
81 | } | 51 | } |
82 | 52 | ||
83 | impl IdDatabase for RootDatabase { | 53 | impl BaseDatabase for RootDatabase {} |
84 | fn id_maps(&self) -> &IdMaps { | ||
85 | &self.id_maps | ||
86 | } | ||
87 | } | ||
88 | 54 | ||
89 | #[cfg(test)] | 55 | impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { |
90 | impl RootDatabase { | 56 | fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { |
91 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<RootDatabase>> { | 57 | &self.id_maps.defs |
92 | *self.events.lock() = Some(Vec::new()); | ||
93 | f(); | ||
94 | let events = self.events.lock().take().unwrap(); | ||
95 | events | ||
96 | } | 58 | } |
59 | } | ||
97 | 60 | ||
98 | pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> { | 61 | impl AsRef<LocationIntener<hir::SourceItemId, FnId>> for RootDatabase { |
99 | let events = self.log(f); | 62 | fn as_ref(&self) -> &LocationIntener<hir::SourceItemId, FnId> { |
100 | events | 63 | &self.id_maps.fns |
101 | .into_iter() | ||
102 | .filter_map(|e| match e.kind { | ||
103 | // This pretty horrible, but `Debug` is the only way to inspect | ||
104 | // QueryDescriptor at the moment. | ||
105 | salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)), | ||
106 | _ => None, | ||
107 | }) | ||
108 | .collect() | ||
109 | } | 64 | } |
110 | } | 65 | } |
111 | 66 | ||
112 | salsa::database_storage! { | 67 | salsa::database_storage! { |
113 | pub(crate) struct RootDatabaseStorage for RootDatabase { | 68 | pub(crate) struct RootDatabaseStorage for RootDatabase { |
114 | impl crate::input::FilesDatabase { | 69 | impl ra_db::FilesDatabase { |
115 | fn file_text() for crate::input::FileTextQuery; | 70 | fn file_text() for ra_db::FileTextQuery; |
116 | fn file_source_root() for crate::input::FileSourceRootQuery; | 71 | fn file_source_root() for ra_db::FileSourceRootQuery; |
117 | fn source_root() for crate::input::SourceRootQuery; | 72 | fn source_root() for ra_db::SourceRootQuery; |
118 | fn libraries() for crate::input::LibrariesQuery; | 73 | fn libraries() for ra_db::LibrariesQuery; |
119 | fn library_symbols() for crate::input::LibrarySymbolsQuery; | 74 | fn crate_graph() for ra_db::CrateGraphQuery; |
120 | fn crate_graph() for crate::input::CrateGraphQuery; | ||
121 | } | ||
122 | impl SyntaxDatabase { | ||
123 | fn file_syntax() for FileSyntaxQuery; | ||
124 | fn file_lines() for FileLinesQuery; | ||
125 | fn file_symbols() for FileSymbolsQuery; | ||
126 | fn resolve_syntax_ptr() for ResolveSyntaxPtrQuery; | ||
127 | } | ||
128 | impl DescriptorDatabase { | ||
129 | fn module_tree() for ModuleTreeQuery; | ||
130 | fn fn_scopes() for FnScopesQuery; | ||
131 | fn _input_module_items() for InputModuleItemsQuery; | ||
132 | fn _item_map() for ItemMapQuery; | ||
133 | fn _fn_syntax() for FnSyntaxQuery; | ||
134 | fn _submodules() for SubmodulesQuery; | ||
135 | } | 75 | } |
136 | } | 76 | impl ra_db::SyntaxDatabase { |
137 | } | 77 | fn source_file() for ra_db::SourceFileQuery; |
138 | 78 | fn file_lines() for ra_db::FileLinesQuery; | |
139 | salsa::query_group! { | ||
140 | pub(crate) trait SyntaxDatabase: crate::input::FilesDatabase { | ||
141 | fn file_syntax(file_id: FileId) -> SourceFileNode { | ||
142 | type FileSyntaxQuery; | ||
143 | } | 79 | } |
144 | fn file_lines(file_id: FileId) -> Arc<LineIndex> { | 80 | impl symbol_index::SymbolsDatabase { |
145 | type FileLinesQuery; | 81 | fn file_symbols() for symbol_index::FileSymbolsQuery; |
82 | fn library_symbols() for symbol_index::LibrarySymbolsQuery; | ||
146 | } | 83 | } |
147 | fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { | 84 | impl hir::db::HirDatabase { |
148 | type FileSymbolsQuery; | 85 | fn module_tree() for hir::db::ModuleTreeQuery; |
149 | } | 86 | fn fn_scopes() for hir::db::FnScopesQuery; |
150 | fn resolve_syntax_ptr(ptr: SyntaxPtr) -> SyntaxNode { | 87 | fn file_items() for hir::db::SourceFileItemsQuery; |
151 | type ResolveSyntaxPtrQuery; | 88 | fn file_item() for hir::db::FileItemQuery; |
152 | // Don't retain syntax trees in memory | 89 | fn input_module_items() for hir::db::InputModuleItemsQuery; |
153 | storage volatile; | 90 | fn item_map() for hir::db::ItemMapQuery; |
154 | use fn crate::syntax_ptr::resolve_syntax_ptr; | 91 | fn fn_syntax() for hir::db::FnSyntaxQuery; |
92 | fn submodules() for hir::db::SubmodulesQuery; | ||
155 | } | 93 | } |
156 | } | 94 | } |
157 | } | 95 | } |
158 | |||
159 | fn file_syntax(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode { | ||
160 | let text = db.file_text(file_id); | ||
161 | SourceFileNode::parse(&*text) | ||
162 | } | ||
163 | fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> { | ||
164 | let text = db.file_text(file_id); | ||
165 | Arc::new(LineIndex::new(&*text)) | ||
166 | } | ||
167 | fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { | ||
168 | db::check_canceled(db)?; | ||
169 | let syntax = db.file_syntax(file_id); | ||
170 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) | ||
171 | } | ||
diff --git a/crates/ra_analysis/src/descriptors/function/imp.rs b/crates/ra_analysis/src/descriptors/function/imp.rs deleted file mode 100644 index e09deba0f..000000000 --- a/crates/ra_analysis/src/descriptors/function/imp.rs +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::ast::{AstNode, FnDef, FnDefNode}; | ||
4 | |||
5 | use crate::descriptors::{ | ||
6 | function::{FnId, FnScopes}, | ||
7 | DescriptorDatabase, | ||
8 | }; | ||
9 | |||
10 | /// Resolve `FnId` to the corresponding `SyntaxNode` | ||
11 | pub(crate) fn fn_syntax(db: &impl DescriptorDatabase, fn_id: FnId) -> FnDefNode { | ||
12 | let ptr = db.id_maps().fn_ptr(fn_id); | ||
13 | let syntax = db.resolve_syntax_ptr(ptr); | ||
14 | FnDef::cast(syntax.borrowed()).unwrap().owned() | ||
15 | } | ||
16 | |||
17 | pub(crate) fn fn_scopes(db: &impl DescriptorDatabase, fn_id: FnId) -> Arc<FnScopes> { | ||
18 | let syntax = db._fn_syntax(fn_id); | ||
19 | let res = FnScopes::new(syntax.borrowed()); | ||
20 | Arc::new(res) | ||
21 | } | ||
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs deleted file mode 100644 index 97750ea64..000000000 --- a/crates/ra_analysis/src/descriptors/mod.rs +++ /dev/null | |||
@@ -1,101 +0,0 @@ | |||
1 | pub(crate) mod function; | ||
2 | pub(crate) mod module; | ||
3 | mod path; | ||
4 | |||
5 | use std::sync::Arc; | ||
6 | |||
7 | use ra_syntax::{ | ||
8 | ast::{self, FnDefNode, AstNode}, | ||
9 | TextRange, | ||
10 | }; | ||
11 | |||
12 | use crate::{ | ||
13 | db::SyntaxDatabase, | ||
14 | descriptors::function::{resolve_local_name, FnId, FnScopes}, | ||
15 | descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}}, | ||
16 | input::SourceRootId, | ||
17 | loc2id::IdDatabase, | ||
18 | syntax_ptr::LocalSyntaxPtr, | ||
19 | Cancelable, | ||
20 | }; | ||
21 | |||
22 | pub(crate) use self::path::{Path, PathKind}; | ||
23 | |||
24 | salsa::query_group! { | ||
25 | pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase { | ||
26 | fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { | ||
27 | type FnScopesQuery; | ||
28 | use fn function::imp::fn_scopes; | ||
29 | } | ||
30 | |||
31 | fn _input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> { | ||
32 | type InputModuleItemsQuery; | ||
33 | use fn module::nameres::input_module_items; | ||
34 | } | ||
35 | fn _item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> { | ||
36 | type ItemMapQuery; | ||
37 | use fn module::nameres::item_map; | ||
38 | } | ||
39 | fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { | ||
40 | type ModuleTreeQuery; | ||
41 | use fn module::imp::module_tree; | ||
42 | } | ||
43 | fn _fn_syntax(fn_id: FnId) -> FnDefNode { | ||
44 | type FnSyntaxQuery; | ||
45 | // Don't retain syntax trees in memory | ||
46 | storage volatile; | ||
47 | use fn function::imp::fn_syntax; | ||
48 | } | ||
49 | fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> { | ||
50 | type SubmodulesQuery; | ||
51 | use fn module::imp::submodules; | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #[derive(Debug)] | ||
57 | pub struct ReferenceDescriptor { | ||
58 | pub range: TextRange, | ||
59 | pub name: String, | ||
60 | } | ||
61 | |||
62 | #[derive(Debug)] | ||
63 | pub struct DeclarationDescriptor<'a> { | ||
64 | pat: ast::BindPat<'a>, | ||
65 | pub range: TextRange, | ||
66 | } | ||
67 | |||
68 | impl<'a> DeclarationDescriptor<'a> { | ||
69 | pub fn new(pat: ast::BindPat) -> DeclarationDescriptor { | ||
70 | let range = pat.syntax().range(); | ||
71 | |||
72 | DeclarationDescriptor { pat, range } | ||
73 | } | ||
74 | |||
75 | pub fn find_all_refs(&self) -> Vec<ReferenceDescriptor> { | ||
76 | let name_ptr = LocalSyntaxPtr::new(self.pat.syntax()); | ||
77 | |||
78 | let fn_def = match self.pat.syntax().ancestors().find_map(ast::FnDef::cast) { | ||
79 | Some(def) => def, | ||
80 | None => return Default::default(), | ||
81 | }; | ||
82 | |||
83 | let fn_scopes = FnScopes::new(fn_def); | ||
84 | |||
85 | let refs: Vec<_> = fn_def | ||
86 | .syntax() | ||
87 | .descendants() | ||
88 | .filter_map(ast::NameRef::cast) | ||
89 | .filter(|name_ref| match resolve_local_name(*name_ref, &fn_scopes) { | ||
90 | None => false, | ||
91 | Some(entry) => entry.ptr() == name_ptr, | ||
92 | }) | ||
93 | .map(|name_ref| ReferenceDescriptor { | ||
94 | name: name_ref.syntax().text().to_string(), | ||
95 | range: name_ref.syntax().range(), | ||
96 | }) | ||
97 | .collect(); | ||
98 | |||
99 | refs | ||
100 | } | ||
101 | } | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index ad6b52371..f5cb3550e 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -1,94 +1,33 @@ | |||
1 | use std::{ | 1 | use std::{ |
2 | fmt, | 2 | fmt, |
3 | hash::{Hash, Hasher}, | ||
4 | sync::Arc, | 3 | sync::Arc, |
5 | }; | 4 | }; |
6 | 5 | ||
7 | use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit}; | 6 | use ra_editor::{self, find_node_at_offset, FileSymbol, LineIndex, LocalEdit}; |
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | ast::{self, ArgListOwner, Expr, NameOwner}, | 8 | ast::{self, ArgListOwner, Expr, NameOwner}, |
10 | AstNode, SourceFileNode, SmolStr, | 9 | AstNode, SourceFileNode, |
11 | SyntaxKind::*, | 10 | SyntaxKind::*, |
12 | SyntaxNodeRef, TextRange, TextUnit, | 11 | SyntaxNodeRef, TextRange, TextUnit, |
13 | }; | 12 | }; |
13 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE, SyntaxDatabase, SourceFileQuery}; | ||
14 | use rayon::prelude::*; | 14 | use rayon::prelude::*; |
15 | use relative_path::RelativePath; | ||
16 | use rustc_hash::FxHashSet; | 15 | use rustc_hash::FxHashSet; |
17 | use salsa::{Database, ParallelDatabase}; | 16 | use salsa::{Database, ParallelDatabase}; |
17 | use hir::{ | ||
18 | self, | ||
19 | FnSignatureInfo, | ||
20 | Problem, | ||
21 | }; | ||
18 | 22 | ||
19 | use crate::{ | 23 | use crate::{ |
20 | completion::{completions, CompletionItem}, | 24 | completion::{completions, CompletionItem}, |
21 | db::{self, FileSyntaxQuery, SyntaxDatabase}, | 25 | db, |
22 | descriptors::{ | 26 | symbol_index::{SymbolIndex, SymbolsDatabase}, |
23 | function::{FnDescriptor, FnId}, | 27 | AnalysisChange, Cancelable, CrateId, Diagnostic, FileId, |
24 | module::{ModuleDescriptor, Problem}, | ||
25 | DeclarationDescriptor, DescriptorDatabase, | ||
26 | }, | ||
27 | input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE}, | ||
28 | symbol_index::SymbolIndex, | ||
29 | AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver, | ||
30 | FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit, | 28 | FileSystemEdit, FilePosition, Query, SourceChange, SourceFileNodeEdit, |
31 | }; | 29 | }; |
32 | 30 | ||
33 | #[derive(Clone, Debug)] | ||
34 | pub(crate) struct FileResolverImp { | ||
35 | inner: Arc<FileResolver>, | ||
36 | } | ||
37 | |||
38 | impl PartialEq for FileResolverImp { | ||
39 | fn eq(&self, other: &FileResolverImp) -> bool { | ||
40 | self.inner() == other.inner() | ||
41 | } | ||
42 | } | ||
43 | |||
44 | impl Eq for FileResolverImp {} | ||
45 | |||
46 | impl Hash for FileResolverImp { | ||
47 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
48 | self.inner().hash(hasher); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | impl FileResolverImp { | ||
53 | pub(crate) fn new(inner: Arc<FileResolver>) -> FileResolverImp { | ||
54 | FileResolverImp { inner } | ||
55 | } | ||
56 | pub(crate) fn file_stem(&self, file_id: FileId) -> String { | ||
57 | self.inner.file_stem(file_id) | ||
58 | } | ||
59 | pub(crate) fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | ||
60 | self.inner.resolve(file_id, path) | ||
61 | } | ||
62 | pub(crate) fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> { | ||
63 | self.inner.debug_path(file_id) | ||
64 | } | ||
65 | fn inner(&self) -> *const FileResolver { | ||
66 | &*self.inner | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl Default for FileResolverImp { | ||
71 | fn default() -> FileResolverImp { | ||
72 | #[derive(Debug)] | ||
73 | struct DummyResolver; | ||
74 | impl FileResolver for DummyResolver { | ||
75 | fn file_stem(&self, _file_: FileId) -> String { | ||
76 | panic!("file resolver not set") | ||
77 | } | ||
78 | fn resolve( | ||
79 | &self, | ||
80 | _file_id: FileId, | ||
81 | _path: &::relative_path::RelativePath, | ||
82 | ) -> Option<FileId> { | ||
83 | panic!("file resolver not set") | ||
84 | } | ||
85 | } | ||
86 | FileResolverImp { | ||
87 | inner: Arc::new(DummyResolver), | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | |||
92 | #[derive(Debug, Default)] | 31 | #[derive(Debug, Default)] |
93 | pub(crate) struct AnalysisHostImpl { | 32 | pub(crate) struct AnalysisHostImpl { |
94 | db: db::RootDatabase, | 33 | db: db::RootDatabase, |
@@ -105,7 +44,7 @@ impl AnalysisHostImpl { | |||
105 | 44 | ||
106 | for (file_id, text) in change.files_changed { | 45 | for (file_id, text) in change.files_changed { |
107 | self.db | 46 | self.db |
108 | .query_mut(crate::input::FileTextQuery) | 47 | .query_mut(ra_db::FileTextQuery) |
109 | .set(file_id, Arc::new(text)) | 48 | .set(file_id, Arc::new(text)) |
110 | } | 49 | } |
111 | if !(change.files_added.is_empty() && change.files_removed.is_empty()) { | 50 | if !(change.files_added.is_empty() && change.files_removed.is_empty()) { |
@@ -115,22 +54,22 @@ impl AnalysisHostImpl { | |||
115 | let mut source_root = SourceRoot::clone(&self.db.source_root(WORKSPACE)); | 54 | let mut source_root = SourceRoot::clone(&self.db.source_root(WORKSPACE)); |
116 | for (file_id, text) in change.files_added { | 55 | for (file_id, text) in change.files_added { |
117 | self.db | 56 | self.db |
118 | .query_mut(crate::input::FileTextQuery) | 57 | .query_mut(ra_db::FileTextQuery) |
119 | .set(file_id, Arc::new(text)); | 58 | .set(file_id, Arc::new(text)); |
120 | self.db | 59 | self.db |
121 | .query_mut(crate::input::FileSourceRootQuery) | 60 | .query_mut(ra_db::FileSourceRootQuery) |
122 | .set(file_id, crate::input::WORKSPACE); | 61 | .set(file_id, ra_db::WORKSPACE); |
123 | source_root.files.insert(file_id); | 62 | source_root.files.insert(file_id); |
124 | } | 63 | } |
125 | for file_id in change.files_removed { | 64 | for file_id in change.files_removed { |
126 | self.db | 65 | self.db |
127 | .query_mut(crate::input::FileTextQuery) | 66 | .query_mut(ra_db::FileTextQuery) |
128 | .set(file_id, Arc::new(String::new())); | 67 | .set(file_id, Arc::new(String::new())); |
129 | source_root.files.remove(&file_id); | 68 | source_root.files.remove(&file_id); |
130 | } | 69 | } |
131 | source_root.file_resolver = file_resolver; | 70 | source_root.file_resolver = file_resolver; |
132 | self.db | 71 | self.db |
133 | .query_mut(crate::input::SourceRootQuery) | 72 | .query_mut(ra_db::SourceRootQuery) |
134 | .set(WORKSPACE, Arc::new(source_root)) | 73 | .set(WORKSPACE, Arc::new(source_root)) |
135 | } | 74 | } |
136 | if !change.libraries_added.is_empty() { | 75 | if !change.libraries_added.is_empty() { |
@@ -147,10 +86,10 @@ impl AnalysisHostImpl { | |||
147 | library.file_resolver.debug_path(file_id) | 86 | library.file_resolver.debug_path(file_id) |
148 | ); | 87 | ); |
149 | self.db | 88 | self.db |
150 | .query_mut(crate::input::FileSourceRootQuery) | 89 | .query_mut(ra_db::FileSourceRootQuery) |
151 | .set_constant(file_id, source_root_id); | 90 | .set_constant(file_id, source_root_id); |
152 | self.db | 91 | self.db |
153 | .query_mut(crate::input::FileTextQuery) | 92 | .query_mut(ra_db::FileTextQuery) |
154 | .set_constant(file_id, Arc::new(text)); | 93 | .set_constant(file_id, Arc::new(text)); |
155 | } | 94 | } |
156 | let source_root = SourceRoot { | 95 | let source_root = SourceRoot { |
@@ -158,19 +97,19 @@ impl AnalysisHostImpl { | |||
158 | file_resolver: library.file_resolver, | 97 | file_resolver: library.file_resolver, |
159 | }; | 98 | }; |
160 | self.db | 99 | self.db |
161 | .query_mut(crate::input::SourceRootQuery) | 100 | .query_mut(ra_db::SourceRootQuery) |
162 | .set(source_root_id, Arc::new(source_root)); | 101 | .set(source_root_id, Arc::new(source_root)); |
163 | self.db | 102 | self.db |
164 | .query_mut(crate::input::LibrarySymbolsQuery) | 103 | .query_mut(crate::symbol_index::LibrarySymbolsQuery) |
165 | .set(source_root_id, Arc::new(library.symbol_index)); | 104 | .set(source_root_id, Arc::new(library.symbol_index)); |
166 | } | 105 | } |
167 | self.db | 106 | self.db |
168 | .query_mut(crate::input::LibrariesQuery) | 107 | .query_mut(ra_db::LibrariesQuery) |
169 | .set((), Arc::new(libraries)); | 108 | .set((), Arc::new(libraries)); |
170 | } | 109 | } |
171 | if let Some(crate_graph) = change.crate_graph { | 110 | if let Some(crate_graph) = change.crate_graph { |
172 | self.db | 111 | self.db |
173 | .query_mut(crate::input::CrateGraphQuery) | 112 | .query_mut(ra_db::CrateGraphQuery) |
174 | .set((), Arc::new(crate_graph)) | 113 | .set((), Arc::new(crate_graph)) |
175 | } | 114 | } |
176 | } | 115 | } |
@@ -189,7 +128,7 @@ impl fmt::Debug for AnalysisImpl { | |||
189 | 128 | ||
190 | impl AnalysisImpl { | 129 | impl AnalysisImpl { |
191 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { | 130 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { |
192 | self.db.file_syntax(file_id) | 131 | self.db.source_file(file_id) |
193 | } | 132 | } |
194 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { | 133 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { |
195 | self.db.file_lines(file_id) | 134 | self.db.file_lines(file_id) |
@@ -220,14 +159,14 @@ impl AnalysisImpl { | |||
220 | .collect() | 159 | .collect() |
221 | }; | 160 | }; |
222 | self.db | 161 | self.db |
223 | .query(FileSyntaxQuery) | 162 | .query(SourceFileQuery) |
224 | .sweep(salsa::SweepStrategy::default().discard_values()); | 163 | .sweep(salsa::SweepStrategy::default().discard_values()); |
225 | Ok(query.search(&buf)) | 164 | Ok(query.search(&buf)) |
226 | } | 165 | } |
227 | /// This return `Vec`: a module may be included from several places. We | 166 | /// This return `Vec`: a module may be included from several places. We |
228 | /// don't handle this case yet though, so the Vec has length at most one. | 167 | /// don't handle this case yet though, so the Vec has length at most one. |
229 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 168 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
230 | let descr = match ModuleDescriptor::guess_from_position(&*self.db, position)? { | 169 | let descr = match hir::Module::guess_from_position(&*self.db, position)? { |
231 | None => return Ok(Vec::new()), | 170 | None => return Ok(Vec::new()), |
232 | Some(it) => it, | 171 | Some(it) => it, |
233 | }; | 172 | }; |
@@ -246,7 +185,7 @@ impl AnalysisImpl { | |||
246 | } | 185 | } |
247 | /// Returns `Vec` for the same reason as `parent_module` | 186 | /// Returns `Vec` for the same reason as `parent_module` |
248 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 187 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
249 | let descr = match ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? { | 188 | let descr = match hir::Module::guess_from_file_id(&*self.db, file_id)? { |
250 | None => return Ok(Vec::new()), | 189 | None => return Ok(Vec::new()), |
251 | Some(it) => it, | 190 | Some(it) => it, |
252 | }; | 191 | }; |
@@ -261,7 +200,7 @@ impl AnalysisImpl { | |||
261 | Ok(crate_id.into_iter().collect()) | 200 | Ok(crate_id.into_iter().collect()) |
262 | } | 201 | } |
263 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 202 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { |
264 | self.db.crate_graph().crate_roots[&crate_id] | 203 | self.db.crate_graph().crate_root(crate_id) |
265 | } | 204 | } |
266 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { | 205 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { |
267 | completions(&self.db, position) | 206 | completions(&self.db, position) |
@@ -270,33 +209,36 @@ impl AnalysisImpl { | |||
270 | &self, | 209 | &self, |
271 | position: FilePosition, | 210 | position: FilePosition, |
272 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 211 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { |
273 | let file = self.db.file_syntax(position.file_id); | 212 | let file = self.db.source_file(position.file_id); |
274 | let syntax = file.syntax(); | 213 | let syntax = file.syntax(); |
275 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { | 214 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
276 | // First try to resolve the symbol locally | 215 | if let Some(fn_descr) = |
277 | return if let Some((name, range)) = | 216 | hir::Function::guess_for_name_ref(&*self.db, position.file_id, name_ref) |
278 | resolve_local_name(&self.db, position.file_id, name_ref) | ||
279 | { | 217 | { |
280 | let mut vec = vec![]; | 218 | let scope = fn_descr.scope(&*self.db); |
281 | vec.push(( | 219 | // First try to resolve the symbol locally |
282 | position.file_id, | 220 | return if let Some(entry) = scope.resolve_local_name(name_ref) { |
283 | FileSymbol { | 221 | let mut vec = vec![]; |
284 | name, | 222 | vec.push(( |
285 | node_range: range, | 223 | position.file_id, |
286 | kind: NAME, | 224 | FileSymbol { |
287 | }, | 225 | name: entry.name().clone(), |
288 | )); | 226 | node_range: entry.ptr().range(), |
289 | Ok(vec) | 227 | kind: NAME, |
290 | } else { | 228 | }, |
291 | // If that fails try the index based approach. | 229 | )); |
292 | self.index_resolve(name_ref) | 230 | Ok(vec) |
293 | }; | 231 | } else { |
232 | // If that fails try the index based approach. | ||
233 | self.index_resolve(name_ref) | ||
234 | }; | ||
235 | } | ||
294 | } | 236 | } |
295 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { | 237 | if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) { |
296 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 238 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
297 | if module.has_semi() { | 239 | if module.has_semi() { |
298 | let parent_module = | 240 | let parent_module = |
299 | ModuleDescriptor::guess_from_file_id(&*self.db, position.file_id)?; | 241 | hir::Module::guess_from_file_id(&*self.db, position.file_id)?; |
300 | let child_name = module.name(); | 242 | let child_name = module.name(); |
301 | match (parent_module, child_name) { | 243 | match (parent_module, child_name) { |
302 | (Some(parent_module), Some(child_name)) => { | 244 | (Some(parent_module), Some(child_name)) => { |
@@ -319,32 +261,42 @@ impl AnalysisImpl { | |||
319 | } | 261 | } |
320 | 262 | ||
321 | pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> { | 263 | pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> { |
322 | let file = self.db.file_syntax(position.file_id); | 264 | let file = self.db.source_file(position.file_id); |
323 | let syntax = file.syntax(); | ||
324 | |||
325 | // Find the binding associated with the offset | 265 | // Find the binding associated with the offset |
326 | let maybe_binding = | 266 | let (binding, descr) = match find_binding(&self.db, &file, position) { |
327 | find_node_at_offset::<ast::BindPat>(syntax, position.offset).or_else(|| { | ||
328 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; | ||
329 | let resolved = resolve_local_name(&self.db, position.file_id, name_ref)?; | ||
330 | find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end()) | ||
331 | }); | ||
332 | |||
333 | let binding = match maybe_binding { | ||
334 | None => return Vec::new(), | 267 | None => return Vec::new(), |
335 | Some(it) => it, | 268 | Some(it) => it, |
336 | }; | 269 | }; |
337 | 270 | ||
338 | let decl = DeclarationDescriptor::new(binding); | 271 | let mut ret = vec![(position.file_id, binding.syntax().range())]; |
339 | |||
340 | let mut ret = vec![(position.file_id, decl.range)]; | ||
341 | ret.extend( | 272 | ret.extend( |
342 | decl.find_all_refs() | 273 | descr |
274 | .scope(&*self.db) | ||
275 | .find_all_refs(binding) | ||
343 | .into_iter() | 276 | .into_iter() |
344 | .map(|ref_desc| (position.file_id, ref_desc.range)), | 277 | .map(|ref_desc| (position.file_id, ref_desc.range)), |
345 | ); | 278 | ); |
346 | 279 | ||
347 | ret | 280 | return ret; |
281 | |||
282 | fn find_binding<'a>( | ||
283 | db: &db::RootDatabase, | ||
284 | source_file: &'a SourceFileNode, | ||
285 | position: FilePosition, | ||
286 | ) -> Option<(ast::BindPat<'a>, hir::Function)> { | ||
287 | let syntax = source_file.syntax(); | ||
288 | if let Some(binding) = find_node_at_offset::<ast::BindPat>(syntax, position.offset) { | ||
289 | let descr = hir::Function::guess_for_bind_pat(db, position.file_id, binding)?; | ||
290 | return Some((binding, descr)); | ||
291 | }; | ||
292 | let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?; | ||
293 | let descr = hir::Function::guess_for_name_ref(db, position.file_id, name_ref)?; | ||
294 | let scope = descr.scope(db); | ||
295 | let resolved = scope.resolve_local_name(name_ref)?; | ||
296 | let resolved = resolved.ptr().resolve(source_file); | ||
297 | let binding = find_node_at_offset::<ast::BindPat>(syntax, resolved.range().end())?; | ||
298 | Some((binding, descr)) | ||
299 | } | ||
348 | } | 300 | } |
349 | 301 | ||
350 | pub fn doc_comment_for( | 302 | pub fn doc_comment_for( |
@@ -352,13 +304,13 @@ impl AnalysisImpl { | |||
352 | file_id: FileId, | 304 | file_id: FileId, |
353 | symbol: FileSymbol, | 305 | symbol: FileSymbol, |
354 | ) -> Cancelable<Option<String>> { | 306 | ) -> Cancelable<Option<String>> { |
355 | let file = self.db.file_syntax(file_id); | 307 | let file = self.db.source_file(file_id); |
356 | 308 | ||
357 | Ok(symbol.docs(&file)) | 309 | Ok(symbol.docs(&file)) |
358 | } | 310 | } |
359 | 311 | ||
360 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 312 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
361 | let syntax = self.db.file_syntax(file_id); | 313 | let syntax = self.db.source_file(file_id); |
362 | 314 | ||
363 | let mut res = ra_editor::diagnostics(&syntax) | 315 | let mut res = ra_editor::diagnostics(&syntax) |
364 | .into_iter() | 316 | .into_iter() |
@@ -368,7 +320,7 @@ impl AnalysisImpl { | |||
368 | fix: None, | 320 | fix: None, |
369 | }) | 321 | }) |
370 | .collect::<Vec<_>>(); | 322 | .collect::<Vec<_>>(); |
371 | if let Some(m) = ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? { | 323 | if let Some(m) = hir::Module::guess_from_file_id(&*self.db, file_id)? { |
372 | for (name_node, problem) in m.problems(&*self.db) { | 324 | for (name_node, problem) in m.problems(&*self.db) { |
373 | let diag = match problem { | 325 | let diag = match problem { |
374 | Problem::UnresolvedModule { candidate } => { | 326 | Problem::UnresolvedModule { candidate } => { |
@@ -445,27 +397,22 @@ impl AnalysisImpl { | |||
445 | pub fn resolve_callable( | 397 | pub fn resolve_callable( |
446 | &self, | 398 | &self, |
447 | position: FilePosition, | 399 | position: FilePosition, |
448 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { | 400 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { |
449 | let file = self.db.file_syntax(position.file_id); | 401 | let file = self.db.source_file(position.file_id); |
450 | let syntax = file.syntax(); | 402 | let syntax = file.syntax(); |
451 | 403 | ||
452 | // Find the calling expression and it's NameRef | 404 | // Find the calling expression and it's NameRef |
453 | let calling_node = match FnCallNode::with_node(syntax, position.offset) { | 405 | let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset)); |
454 | Some(node) => node, | 406 | let name_ref = ctry!(calling_node.name_ref()); |
455 | None => return Ok(None), | ||
456 | }; | ||
457 | let name_ref = match calling_node.name_ref() { | ||
458 | Some(name) => name, | ||
459 | None => return Ok(None), | ||
460 | }; | ||
461 | 407 | ||
462 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). | 408 | // Resolve the function's NameRef (NOTE: this isn't entirely accurate). |
463 | let file_symbols = self.index_resolve(name_ref)?; | 409 | let file_symbols = self.index_resolve(name_ref)?; |
464 | for (fn_fiel_id, fs) in file_symbols { | 410 | for (fn_file_id, fs) in file_symbols { |
465 | if fs.kind == FN_DEF { | 411 | if fs.kind == FN_DEF { |
466 | let fn_file = self.db.file_syntax(fn_fiel_id); | 412 | let fn_file = self.db.source_file(fn_file_id); |
467 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { | 413 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { |
468 | if let Some(descriptor) = FnDescriptor::new(fn_def) { | 414 | let descr = hir::Function::guess_from_source(&*self.db, fn_file_id, fn_def); |
415 | if let Some(descriptor) = descr.signature_info(&*self.db) { | ||
469 | // If we have a calling expression let's find which argument we are on | 416 | // If we have a calling expression let's find which argument we are on |
470 | let mut current_parameter = None; | 417 | let mut current_parameter = None; |
471 | 418 | ||
@@ -538,16 +485,6 @@ impl SourceChange { | |||
538 | } | 485 | } |
539 | } | 486 | } |
540 | 487 | ||
541 | impl CrateGraph { | ||
542 | fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | ||
543 | let (&crate_id, _) = self | ||
544 | .crate_roots | ||
545 | .iter() | ||
546 | .find(|(_crate_id, &root_id)| root_id == file_id)?; | ||
547 | Some(crate_id) | ||
548 | } | ||
549 | } | ||
550 | |||
551 | enum FnCallNode<'a> { | 488 | enum FnCallNode<'a> { |
552 | CallExpr(ast::CallExpr<'a>), | 489 | CallExpr(ast::CallExpr<'a>), |
553 | MethodCallExpr(ast::MethodCallExpr<'a>), | 490 | MethodCallExpr(ast::MethodCallExpr<'a>), |
@@ -586,16 +523,3 @@ impl<'a> FnCallNode<'a> { | |||
586 | } | 523 | } |
587 | } | 524 | } |
588 | } | 525 | } |
589 | |||
590 | fn resolve_local_name( | ||
591 | db: &db::RootDatabase, | ||
592 | file_id: FileId, | ||
593 | name_ref: ast::NameRef, | ||
594 | ) -> Option<(SmolStr, TextRange)> { | ||
595 | let fn_def = name_ref.syntax().ancestors().find_map(ast::FnDef::cast)?; | ||
596 | let fn_id = FnId::get(db, file_id, fn_def); | ||
597 | let scopes = db.fn_scopes(fn_id); | ||
598 | let scope_entry = crate::descriptors::function::resolve_local_name(name_ref, &scopes)?; | ||
599 | let syntax = db.resolve_syntax_ptr(scope_entry.ptr().into_global(file_id)); | ||
600 | Some((scope_entry.name().clone(), syntax.range())) | ||
601 | } | ||
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index cedbd1fc8..12df580ba 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -9,49 +9,45 @@ extern crate relative_path; | |||
9 | extern crate rustc_hash; | 9 | extern crate rustc_hash; |
10 | extern crate salsa; | 10 | extern crate salsa; |
11 | 11 | ||
12 | mod arena; | 12 | macro_rules! ctry { |
13 | ($expr:expr) => { | ||
14 | match $expr { | ||
15 | None => return Ok(None), | ||
16 | Some(it) => it, | ||
17 | } | ||
18 | }; | ||
19 | } | ||
20 | |||
13 | mod db; | 21 | mod db; |
14 | mod loc2id; | ||
15 | mod input; | ||
16 | mod imp; | 22 | mod imp; |
17 | mod completion; | 23 | mod completion; |
18 | mod descriptors; | ||
19 | mod symbol_index; | 24 | mod symbol_index; |
20 | mod syntax_ptr; | ||
21 | pub mod mock_analysis; | 25 | pub mod mock_analysis; |
22 | 26 | ||
23 | use std::{fmt, sync::Arc}; | 27 | use std::{fmt, sync::Arc}; |
24 | 28 | ||
25 | use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit}; | 29 | use ra_syntax::{AtomEdit, SourceFileNode, TextRange, TextUnit}; |
30 | use ra_db::FileResolverImp; | ||
26 | use rayon::prelude::*; | 31 | use rayon::prelude::*; |
27 | use relative_path::RelativePathBuf; | 32 | use relative_path::RelativePathBuf; |
28 | 33 | ||
29 | use crate::{ | 34 | use crate::{ |
30 | imp::{AnalysisHostImpl, AnalysisImpl, FileResolverImp}, | 35 | imp::{AnalysisHostImpl, AnalysisImpl}, |
31 | symbol_index::SymbolIndex, | 36 | symbol_index::SymbolIndex, |
32 | }; | 37 | }; |
33 | 38 | ||
34 | pub use crate::{ | 39 | pub use crate::{ |
35 | completion::CompletionItem, | 40 | completion::CompletionItem, |
36 | descriptors::function::FnDescriptor, | ||
37 | input::{CrateGraph, CrateId, FileId, FileResolver}, | ||
38 | }; | 41 | }; |
39 | pub use ra_editor::{ | 42 | pub use ra_editor::{ |
40 | FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, | 43 | FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, |
41 | }; | 44 | }; |
45 | pub use hir::FnSignatureInfo; | ||
42 | 46 | ||
43 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 47 | pub use ra_db::{ |
44 | pub struct Canceled; | 48 | Canceled, Cancelable, FilePosition, |
45 | 49 | CrateGraph, CrateId, FileId, FileResolver | |
46 | pub type Cancelable<T> = Result<T, Canceled>; | 50 | }; |
47 | |||
48 | impl std::fmt::Display for Canceled { | ||
49 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
50 | fmt.write_str("Canceled") | ||
51 | } | ||
52 | } | ||
53 | |||
54 | impl std::error::Error for Canceled {} | ||
55 | 51 | ||
56 | #[derive(Default)] | 52 | #[derive(Default)] |
57 | pub struct AnalysisChange { | 53 | pub struct AnalysisChange { |
@@ -121,12 +117,6 @@ impl AnalysisHost { | |||
121 | } | 117 | } |
122 | } | 118 | } |
123 | 119 | ||
124 | #[derive(Clone, Copy, Debug)] | ||
125 | pub struct FilePosition { | ||
126 | pub file_id: FileId, | ||
127 | pub offset: TextUnit, | ||
128 | } | ||
129 | |||
130 | #[derive(Debug)] | 120 | #[derive(Debug)] |
131 | pub struct SourceChange { | 121 | pub struct SourceChange { |
132 | pub label: String, | 122 | pub label: String, |
@@ -296,7 +286,7 @@ impl Analysis { | |||
296 | pub fn resolve_callable( | 286 | pub fn resolve_callable( |
297 | &self, | 287 | &self, |
298 | position: FilePosition, | 288 | position: FilePosition, |
299 | ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { | 289 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { |
300 | self.imp.resolve_callable(position) | 290 | self.imp.resolve_callable(position) |
301 | } | 291 | } |
302 | } | 292 | } |
diff --git a/crates/ra_analysis/src/loc2id.rs b/crates/ra_analysis/src/loc2id.rs deleted file mode 100644 index e4b55f9b0..000000000 --- a/crates/ra_analysis/src/loc2id.rs +++ /dev/null | |||
@@ -1,138 +0,0 @@ | |||
1 | use parking_lot::Mutex; | ||
2 | |||
3 | use std::{ | ||
4 | hash::Hash, | ||
5 | sync::Arc, | ||
6 | }; | ||
7 | |||
8 | use rustc_hash::FxHashMap; | ||
9 | |||
10 | use crate::{ | ||
11 | descriptors::module::ModuleId, | ||
12 | syntax_ptr::SyntaxPtr, | ||
13 | input::SourceRootId, | ||
14 | }; | ||
15 | |||
16 | /// There are two principle ways to refer to things: | ||
17 | /// - by their locatinon (module in foo/bar/baz.rs at line 42) | ||
18 | /// - by their numeric id (module `ModuleId(42)`) | ||
19 | /// | ||
20 | /// The first one is more powerful (you can actually find the thing in question | ||
21 | /// by id), but the second one is so much more compact. | ||
22 | /// | ||
23 | /// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a | ||
24 | /// bidirectional mapping between positional and numeric ids, we can use compact | ||
25 | /// representation wich still allows us to get the actual item | ||
26 | #[derive(Debug)] | ||
27 | pub(crate) struct Loc2IdMap<L, ID> | ||
28 | where | ||
29 | ID: NumericId, | ||
30 | L: Clone + Eq + Hash, | ||
31 | { | ||
32 | loc2id: FxHashMap<L, ID>, | ||
33 | id2loc: FxHashMap<ID, L>, | ||
34 | } | ||
35 | |||
36 | impl<L, ID> Default for Loc2IdMap<L, ID> | ||
37 | where | ||
38 | ID: NumericId, | ||
39 | L: Clone + Eq + Hash, | ||
40 | { | ||
41 | fn default() -> Self { | ||
42 | Loc2IdMap { | ||
43 | loc2id: FxHashMap::default(), | ||
44 | id2loc: FxHashMap::default(), | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | |||
49 | impl<L, ID> Loc2IdMap<L, ID> | ||
50 | where | ||
51 | ID: NumericId, | ||
52 | L: Clone + Eq + Hash, | ||
53 | { | ||
54 | pub fn loc2id(&mut self, loc: &L) -> ID { | ||
55 | match self.loc2id.get(loc) { | ||
56 | Some(id) => return id.clone(), | ||
57 | None => (), | ||
58 | } | ||
59 | let id = self.loc2id.len(); | ||
60 | assert!(id < u32::max_value() as usize); | ||
61 | let id = ID::from_u32(id as u32); | ||
62 | self.loc2id.insert(loc.clone(), id.clone()); | ||
63 | self.id2loc.insert(id.clone(), loc.clone()); | ||
64 | id | ||
65 | } | ||
66 | |||
67 | pub fn id2loc(&self, id: ID) -> L { | ||
68 | self.id2loc[&id].clone() | ||
69 | } | ||
70 | } | ||
71 | |||
72 | pub(crate) trait NumericId: Clone + Eq + Hash { | ||
73 | fn from_u32(id: u32) -> Self; | ||
74 | fn to_u32(self) -> u32; | ||
75 | } | ||
76 | |||
77 | macro_rules! impl_numeric_id { | ||
78 | ($id:ident) => { | ||
79 | impl NumericId for $id { | ||
80 | fn from_u32(id: u32) -> Self { | ||
81 | $id(id) | ||
82 | } | ||
83 | fn to_u32(self) -> u32 { | ||
84 | self.0 | ||
85 | } | ||
86 | } | ||
87 | }; | ||
88 | } | ||
89 | |||
90 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
91 | pub(crate) struct FnId(u32); | ||
92 | impl_numeric_id!(FnId); | ||
93 | |||
94 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
95 | pub(crate) struct DefId(u32); | ||
96 | impl_numeric_id!(DefId); | ||
97 | |||
98 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
99 | pub(crate) enum DefLoc { | ||
100 | Module { | ||
101 | id: ModuleId, | ||
102 | source_root: SourceRootId, | ||
103 | }, | ||
104 | Item { | ||
105 | ptr: SyntaxPtr, | ||
106 | }, | ||
107 | } | ||
108 | |||
109 | pub(crate) trait IdDatabase: salsa::Database { | ||
110 | fn id_maps(&self) -> &IdMaps; | ||
111 | } | ||
112 | |||
113 | #[derive(Debug, Default, Clone)] | ||
114 | pub(crate) struct IdMaps { | ||
115 | inner: Arc<IdMapsInner>, | ||
116 | } | ||
117 | |||
118 | impl IdMaps { | ||
119 | pub(crate) fn fn_id(&self, ptr: SyntaxPtr) -> FnId { | ||
120 | self.inner.fns.lock().loc2id(&ptr) | ||
121 | } | ||
122 | pub(crate) fn fn_ptr(&self, fn_id: FnId) -> SyntaxPtr { | ||
123 | self.inner.fns.lock().id2loc(fn_id) | ||
124 | } | ||
125 | |||
126 | pub(crate) fn def_id(&self, loc: DefLoc) -> DefId { | ||
127 | self.inner.defs.lock().loc2id(&loc) | ||
128 | } | ||
129 | pub(crate) fn def_loc(&self, def_id: DefId) -> DefLoc { | ||
130 | self.inner.defs.lock().id2loc(def_id) | ||
131 | } | ||
132 | } | ||
133 | |||
134 | #[derive(Debug, Default)] | ||
135 | struct IdMapsInner { | ||
136 | fns: Mutex<Loc2IdMap<SyntaxPtr, FnId>>, | ||
137 | defs: Mutex<Loc2IdMap<DefLoc, DefId>>, | ||
138 | } | ||
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 8e8f969f4..0d9a7a147 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::{RelativePath, RelativePathBuf}; | 3 | use relative_path::{RelativePathBuf}; |
4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; |
5 | use ra_db::mock::FileMap; | ||
5 | 6 | ||
6 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition}; | 7 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition}; |
7 | 8 | ||
8 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | 9 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis |
9 | /// from a set of in-memory files. | 10 | /// from a set of in-memory files. |
@@ -76,16 +77,15 @@ impl MockAnalysis { | |||
76 | } | 77 | } |
77 | pub fn analysis_host(self) -> AnalysisHost { | 78 | pub fn analysis_host(self) -> AnalysisHost { |
78 | let mut host = AnalysisHost::default(); | 79 | let mut host = AnalysisHost::default(); |
79 | let mut file_map = Vec::new(); | 80 | let mut file_map = FileMap::default(); |
80 | let mut change = AnalysisChange::new(); | 81 | let mut change = AnalysisChange::new(); |
81 | for (id, (path, contents)) in self.files.into_iter().enumerate() { | 82 | for (path, contents) in self.files.into_iter() { |
82 | let file_id = FileId((id + 1) as u32); | ||
83 | assert!(path.starts_with('/')); | 83 | assert!(path.starts_with('/')); |
84 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | 84 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); |
85 | let file_id = file_map.add(path); | ||
85 | change.add_file(file_id, contents); | 86 | change.add_file(file_id, contents); |
86 | file_map.push((file_id, path)); | ||
87 | } | 87 | } |
88 | change.set_file_resolver(Arc::new(FileMap(file_map))); | 88 | change.set_file_resolver(Arc::new(file_map)); |
89 | host.apply_change(change); | 89 | host.apply_change(change); |
90 | host | 90 | host |
91 | } | 91 | } |
@@ -113,29 +113,3 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { | |||
113 | let pos = mock.add_file_with_position("/main.rs", code); | 113 | let pos = mock.add_file_with_position("/main.rs", code); |
114 | (mock.analysis(), pos) | 114 | (mock.analysis(), pos) |
115 | } | 115 | } |
116 | |||
117 | #[derive(Debug)] | ||
118 | struct FileMap(Vec<(FileId, RelativePathBuf)>); | ||
119 | |||
120 | impl FileMap { | ||
121 | fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a { | ||
122 | self.0 | ||
123 | .iter() | ||
124 | .map(|(id, path)| (*id, path.as_relative_path())) | ||
125 | } | ||
126 | |||
127 | fn path(&self, id: FileId) -> &RelativePath { | ||
128 | self.iter().find(|&(it, _)| it == id).unwrap().1 | ||
129 | } | ||
130 | } | ||
131 | |||
132 | impl FileResolver for FileMap { | ||
133 | fn file_stem(&self, id: FileId) -> String { | ||
134 | self.path(id).file_stem().unwrap().to_string() | ||
135 | } | ||
136 | fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> { | ||
137 | let path = self.path(id).join(rel).normalize(); | ||
138 | let id = self.iter().find(|&(_, p)| path == p)?.0; | ||
139 | Some(id) | ||
140 | } | ||
141 | } | ||
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs index 3a0667ecd..b48a37229 100644 --- a/crates/ra_analysis/src/symbol_index.rs +++ b/crates/ra_analysis/src/symbol_index.rs | |||
@@ -4,14 +4,36 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
7 | use ra_editor::{file_symbols, FileSymbol}; | 7 | use ra_editor::{self, FileSymbol}; |
8 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | SourceFileNode, | 9 | SourceFileNode, |
10 | SyntaxKind::{self, *}, | 10 | SyntaxKind::{self, *}, |
11 | }; | 11 | }; |
12 | use ra_db::{SyntaxDatabase, SourceRootId}; | ||
12 | use rayon::prelude::*; | 13 | use rayon::prelude::*; |
13 | 14 | ||
14 | use crate::{FileId, Query}; | 15 | use crate::{ |
16 | Cancelable, | ||
17 | FileId, Query, | ||
18 | }; | ||
19 | |||
20 | salsa::query_group! { | ||
21 | pub(crate) trait SymbolsDatabase: SyntaxDatabase { | ||
22 | fn file_symbols(file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { | ||
23 | type FileSymbolsQuery; | ||
24 | } | ||
25 | fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> { | ||
26 | type LibrarySymbolsQuery; | ||
27 | storage input; | ||
28 | } | ||
29 | } | ||
30 | } | ||
31 | |||
32 | fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<SymbolIndex>> { | ||
33 | db.check_canceled()?; | ||
34 | let syntax = db.source_file(file_id); | ||
35 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) | ||
36 | } | ||
15 | 37 | ||
16 | #[derive(Default, Debug)] | 38 | #[derive(Default, Debug)] |
17 | pub(crate) struct SymbolIndex { | 39 | pub(crate) struct SymbolIndex { |
@@ -39,7 +61,7 @@ impl SymbolIndex { | |||
39 | ) -> SymbolIndex { | 61 | ) -> SymbolIndex { |
40 | let mut symbols = files | 62 | let mut symbols = files |
41 | .flat_map(|(file_id, file)| { | 63 | .flat_map(|(file_id, file)| { |
42 | file_symbols(&file) | 64 | ra_editor::file_symbols(&file) |
43 | .into_iter() | 65 | .into_iter() |
44 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) | 66 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) |
45 | .collect::<Vec<_>>() | 67 | .collect::<Vec<_>>() |
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_analysis/src/syntax_ptr.rs deleted file mode 100644 index 194b94584..000000000 --- a/crates/ra_analysis/src/syntax_ptr.rs +++ /dev/null | |||
@@ -1,84 +0,0 @@ | |||
1 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; | ||
2 | |||
3 | use crate::db::SyntaxDatabase; | ||
4 | use crate::FileId; | ||
5 | |||
6 | pub(crate) fn resolve_syntax_ptr(db: &impl SyntaxDatabase, ptr: SyntaxPtr) -> SyntaxNode { | ||
7 | let syntax = db.file_syntax(ptr.file_id); | ||
8 | ptr.local.resolve(&syntax) | ||
9 | } | ||
10 | |||
11 | /// SyntaxPtr is a cheap `Copy` id which identifies a particular syntax node, | ||
12 | /// without retaining syntax tree in memory. You need to explicitly `resolve` | ||
13 | /// `SyntaxPtr` to get a `SyntaxNode` | ||
14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
15 | pub(crate) struct SyntaxPtr { | ||
16 | file_id: FileId, | ||
17 | local: LocalSyntaxPtr, | ||
18 | } | ||
19 | |||
20 | impl SyntaxPtr { | ||
21 | pub(crate) fn new(file_id: FileId, node: SyntaxNodeRef) -> SyntaxPtr { | ||
22 | let local = LocalSyntaxPtr::new(node); | ||
23 | SyntaxPtr { file_id, local } | ||
24 | } | ||
25 | |||
26 | pub(crate) fn file_id(self) -> FileId { | ||
27 | self.file_id | ||
28 | } | ||
29 | } | ||
30 | |||
31 | /// A pionter to a syntax node inside a file. | ||
32 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
33 | pub(crate) struct LocalSyntaxPtr { | ||
34 | range: TextRange, | ||
35 | kind: SyntaxKind, | ||
36 | } | ||
37 | |||
38 | impl LocalSyntaxPtr { | ||
39 | pub(crate) fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr { | ||
40 | LocalSyntaxPtr { | ||
41 | range: node.range(), | ||
42 | kind: node.kind(), | ||
43 | } | ||
44 | } | ||
45 | |||
46 | pub(crate) fn resolve(self, file: &SourceFileNode) -> SyntaxNode { | ||
47 | let mut curr = file.syntax(); | ||
48 | loop { | ||
49 | if curr.range() == self.range && curr.kind() == self.kind { | ||
50 | return curr.owned(); | ||
51 | } | ||
52 | curr = curr | ||
53 | .children() | ||
54 | .find(|it| self.range.is_subrange(&it.range())) | ||
55 | .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | pub(crate) fn into_global(self, file_id: FileId) -> SyntaxPtr { | ||
60 | SyntaxPtr { | ||
61 | file_id, | ||
62 | local: self, | ||
63 | } | ||
64 | } | ||
65 | |||
66 | // Seems unfortunate to expose | ||
67 | pub(crate) fn range(self) -> TextRange { | ||
68 | self.range | ||
69 | } | ||
70 | } | ||
71 | |||
72 | #[test] | ||
73 | fn test_local_syntax_ptr() { | ||
74 | use ra_syntax::{ast, AstNode}; | ||
75 | let file = SourceFileNode::parse("struct Foo { f: u32, }"); | ||
76 | let field = file | ||
77 | .syntax() | ||
78 | .descendants() | ||
79 | .find_map(ast::NamedFieldDef::cast) | ||
80 | .unwrap(); | ||
81 | let ptr = LocalSyntaxPtr::new(field.syntax()); | ||
82 | let field_syntax = ptr.resolve(&file); | ||
83 | assert_eq!(field.syntax(), field_syntax); | ||
84 | } | ||
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index 72b2ebf97..fbe89f444 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -10,10 +10,10 @@ use test_utils::assert_eq_dbg; | |||
10 | 10 | ||
11 | use ra_analysis::{ | 11 | use ra_analysis::{ |
12 | mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, | 12 | mock_analysis::{analysis_and_position, single_file, single_file_with_position, MockAnalysis}, |
13 | AnalysisChange, CrateGraph, FileId, FnDescriptor, | 13 | AnalysisChange, CrateGraph, FileId, FnSignatureInfo, |
14 | }; | 14 | }; |
15 | 15 | ||
16 | fn get_signature(text: &str) -> (FnDescriptor, Option<usize>) { | 16 | fn get_signature(text: &str) -> (FnSignatureInfo, Option<usize>) { |
17 | let (analysis, position) = single_file_with_position(text); | 17 | let (analysis, position) = single_file_with_position(text); |
18 | analysis.resolve_callable(position).unwrap().unwrap() | 18 | analysis.resolve_callable(position).unwrap().unwrap() |
19 | } | 19 | } |
@@ -126,7 +126,7 @@ fn test_resolve_crate_root() { | |||
126 | let mut host = mock.analysis_host(); | 126 | let mut host = mock.analysis_host(); |
127 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | 127 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); |
128 | 128 | ||
129 | let mut crate_graph = CrateGraph::new(); | 129 | let mut crate_graph = CrateGraph::default(); |
130 | let crate_id = crate_graph.add_crate_root(root_file); | 130 | let crate_id = crate_graph.add_crate_root(root_file); |
131 | let mut change = AnalysisChange::new(); | 131 | let mut change = AnalysisChange::new(); |
132 | change.set_crate_graph(crate_graph); | 132 | change.set_crate_graph(crate_graph); |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml new file mode 100644 index 000000000..f316c0ab2 --- /dev/null +++ b/crates/ra_db/Cargo.toml | |||
@@ -0,0 +1,14 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_db" | ||
4 | version = "0.1.0" | ||
5 | authors = ["Aleksey Kladov <[email protected]>"] | ||
6 | |||
7 | [dependencies] | ||
8 | relative-path = "0.4.0" | ||
9 | salsa = "0.8.0" | ||
10 | rustc-hash = "1.0" | ||
11 | parking_lot = "0.6.4" | ||
12 | ra_syntax = { path = "../ra_syntax" } | ||
13 | ra_editor = { path = "../ra_editor" } | ||
14 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/ra_db/src/file_resolver.rs b/crates/ra_db/src/file_resolver.rs new file mode 100644 index 000000000..f849ac752 --- /dev/null +++ b/crates/ra_db/src/file_resolver.rs | |||
@@ -0,0 +1,76 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | hash::{Hash, Hasher}, | ||
4 | fmt, | ||
5 | }; | ||
6 | |||
7 | use relative_path::RelativePath; | ||
8 | |||
9 | use crate::input::FileId; | ||
10 | |||
11 | pub trait FileResolver: fmt::Debug + Send + Sync + 'static { | ||
12 | fn file_stem(&self, file_id: FileId) -> String; | ||
13 | fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>; | ||
14 | fn debug_path(&self, _1file_id: FileId) -> Option<std::path::PathBuf> { | ||
15 | None | ||
16 | } | ||
17 | } | ||
18 | |||
19 | #[derive(Clone, Debug)] | ||
20 | pub struct FileResolverImp { | ||
21 | inner: Arc<FileResolver>, | ||
22 | } | ||
23 | |||
24 | impl PartialEq for FileResolverImp { | ||
25 | fn eq(&self, other: &FileResolverImp) -> bool { | ||
26 | self.inner() == other.inner() | ||
27 | } | ||
28 | } | ||
29 | |||
30 | impl Eq for FileResolverImp {} | ||
31 | |||
32 | impl Hash for FileResolverImp { | ||
33 | fn hash<H: Hasher>(&self, hasher: &mut H) { | ||
34 | self.inner().hash(hasher); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl FileResolverImp { | ||
39 | pub fn new(inner: Arc<FileResolver>) -> FileResolverImp { | ||
40 | FileResolverImp { inner } | ||
41 | } | ||
42 | pub fn file_stem(&self, file_id: FileId) -> String { | ||
43 | self.inner.file_stem(file_id) | ||
44 | } | ||
45 | pub fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId> { | ||
46 | self.inner.resolve(file_id, path) | ||
47 | } | ||
48 | pub fn debug_path(&self, file_id: FileId) -> Option<std::path::PathBuf> { | ||
49 | self.inner.debug_path(file_id) | ||
50 | } | ||
51 | fn inner(&self) -> *const FileResolver { | ||
52 | &*self.inner | ||
53 | } | ||
54 | } | ||
55 | |||
56 | impl Default for FileResolverImp { | ||
57 | fn default() -> FileResolverImp { | ||
58 | #[derive(Debug)] | ||
59 | struct DummyResolver; | ||
60 | impl FileResolver for DummyResolver { | ||
61 | fn file_stem(&self, _file_: FileId) -> String { | ||
62 | panic!("file resolver not set") | ||
63 | } | ||
64 | fn resolve( | ||
65 | &self, | ||
66 | _file_id: FileId, | ||
67 | _path: &::relative_path::RelativePath, | ||
68 | ) -> Option<FileId> { | ||
69 | panic!("file resolver not set") | ||
70 | } | ||
71 | } | ||
72 | FileResolverImp { | ||
73 | inner: Arc::new(DummyResolver), | ||
74 | } | ||
75 | } | ||
76 | } | ||
diff --git a/crates/ra_analysis/src/input.rs b/crates/ra_db/src/input.rs index a78b6e397..9101ac7a8 100644 --- a/crates/ra_analysis/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -1,11 +1,10 @@ | |||
1 | use std::{fmt, sync::Arc}; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::RelativePath; | ||
4 | use rustc_hash::FxHashMap; | 3 | use rustc_hash::FxHashMap; |
5 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
6 | use salsa; | 5 | use salsa; |
7 | 6 | ||
8 | use crate::{symbol_index::SymbolIndex, FileResolverImp}; | 7 | use crate::file_resolver::FileResolverImp; |
9 | 8 | ||
10 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
11 | pub struct FileId(pub u32); | 10 | pub struct FileId(pub u32); |
@@ -19,8 +18,8 @@ pub struct CrateGraph { | |||
19 | } | 18 | } |
20 | 19 | ||
21 | impl CrateGraph { | 20 | impl CrateGraph { |
22 | pub fn new() -> CrateGraph { | 21 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { |
23 | CrateGraph::default() | 22 | self.crate_roots[&crate_id] |
24 | } | 23 | } |
25 | pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId { | 24 | pub fn add_crate_root(&mut self, file_id: FileId) -> CrateId { |
26 | let crate_id = CrateId(self.crate_roots.len() as u32); | 25 | let crate_id = CrateId(self.crate_roots.len() as u32); |
@@ -28,18 +27,17 @@ impl CrateGraph { | |||
28 | assert!(prev.is_none()); | 27 | assert!(prev.is_none()); |
29 | crate_id | 28 | crate_id |
30 | } | 29 | } |
31 | } | 30 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
32 | 31 | let (&crate_id, _) = self | |
33 | pub trait FileResolver: fmt::Debug + Send + Sync + 'static { | 32 | .crate_roots |
34 | fn file_stem(&self, file_id: FileId) -> String; | 33 | .iter() |
35 | fn resolve(&self, file_id: FileId, path: &RelativePath) -> Option<FileId>; | 34 | .find(|(_crate_id, &root_id)| root_id == file_id)?; |
36 | fn debug_path(&self, _file_id: FileId) -> Option<std::path::PathBuf> { | 35 | Some(crate_id) |
37 | None | ||
38 | } | 36 | } |
39 | } | 37 | } |
40 | 38 | ||
41 | salsa::query_group! { | 39 | salsa::query_group! { |
42 | pub(crate) trait FilesDatabase: salsa::Database { | 40 | pub trait FilesDatabase: salsa::Database { |
43 | fn file_text(file_id: FileId) -> Arc<String> { | 41 | fn file_text(file_id: FileId) -> Arc<String> { |
44 | type FileTextQuery; | 42 | type FileTextQuery; |
45 | storage input; | 43 | storage input; |
@@ -56,10 +54,6 @@ salsa::query_group! { | |||
56 | type LibrariesQuery; | 54 | type LibrariesQuery; |
57 | storage input; | 55 | storage input; |
58 | } | 56 | } |
59 | fn library_symbols(id: SourceRootId) -> Arc<SymbolIndex> { | ||
60 | type LibrarySymbolsQuery; | ||
61 | storage input; | ||
62 | } | ||
63 | fn crate_graph() -> Arc<CrateGraph> { | 57 | fn crate_graph() -> Arc<CrateGraph> { |
64 | type CrateGraphQuery; | 58 | type CrateGraphQuery; |
65 | storage input; | 59 | storage input; |
@@ -68,12 +62,12 @@ salsa::query_group! { | |||
68 | } | 62 | } |
69 | 63 | ||
70 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] | 64 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] |
71 | pub(crate) struct SourceRootId(pub(crate) u32); | 65 | pub struct SourceRootId(pub u32); |
72 | 66 | ||
73 | #[derive(Default, Clone, Debug, PartialEq, Eq)] | 67 | #[derive(Default, Clone, Debug, PartialEq, Eq)] |
74 | pub(crate) struct SourceRoot { | 68 | pub struct SourceRoot { |
75 | pub(crate) file_resolver: FileResolverImp, | 69 | pub file_resolver: FileResolverImp, |
76 | pub(crate) files: FxHashSet<FileId>, | 70 | pub files: FxHashSet<FileId>, |
77 | } | 71 | } |
78 | 72 | ||
79 | pub(crate) const WORKSPACE: SourceRootId = SourceRootId(0); | 73 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs new file mode 100644 index 000000000..53805aada --- /dev/null +++ b/crates/ra_db/src/lib.rs | |||
@@ -0,0 +1,83 @@ | |||
1 | //! ra_db defines basic database traits. Concrete DB is defined by ra_analysis. | ||
2 | mod syntax_ptr; | ||
3 | mod file_resolver; | ||
4 | mod input; | ||
5 | mod loc2id; | ||
6 | pub mod mock; | ||
7 | |||
8 | use std::sync::Arc; | ||
9 | use ra_editor::LineIndex; | ||
10 | use ra_syntax::{TextUnit, SourceFileNode}; | ||
11 | |||
12 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | ||
13 | pub struct Canceled; | ||
14 | |||
15 | pub type Cancelable<T> = Result<T, Canceled>; | ||
16 | |||
17 | impl std::fmt::Display for Canceled { | ||
18 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
19 | fmt.write_str("Canceled") | ||
20 | } | ||
21 | } | ||
22 | |||
23 | impl std::error::Error for Canceled {} | ||
24 | |||
25 | pub use crate::{ | ||
26 | syntax_ptr::LocalSyntaxPtr, | ||
27 | file_resolver::{FileResolver, FileResolverImp}, | ||
28 | input::{ | ||
29 | FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, WORKSPACE, | ||
30 | FileTextQuery, FileSourceRootQuery, SourceRootQuery, LibrariesQuery, CrateGraphQuery, | ||
31 | }, | ||
32 | loc2id::{LocationIntener, NumericId}, | ||
33 | }; | ||
34 | |||
35 | #[macro_export] | ||
36 | macro_rules! impl_numeric_id { | ||
37 | ($id:ident) => { | ||
38 | impl $crate::NumericId for $id { | ||
39 | fn from_u32(id: u32) -> Self { | ||
40 | $id(id) | ||
41 | } | ||
42 | fn to_u32(self) -> u32 { | ||
43 | self.0 | ||
44 | } | ||
45 | } | ||
46 | }; | ||
47 | } | ||
48 | |||
49 | pub trait BaseDatabase: salsa::Database { | ||
50 | fn check_canceled(&self) -> Cancelable<()> { | ||
51 | if self.salsa_runtime().is_current_revision_canceled() { | ||
52 | Err(Canceled) | ||
53 | } else { | ||
54 | Ok(()) | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | salsa::query_group! { | ||
60 | pub trait SyntaxDatabase: crate::input::FilesDatabase + BaseDatabase { | ||
61 | fn source_file(file_id: FileId) -> SourceFileNode { | ||
62 | type SourceFileQuery; | ||
63 | } | ||
64 | fn file_lines(file_id: FileId) -> Arc<LineIndex> { | ||
65 | type FileLinesQuery; | ||
66 | } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | fn source_file(db: &impl SyntaxDatabase, file_id: FileId) -> SourceFileNode { | ||
71 | let text = db.file_text(file_id); | ||
72 | SourceFileNode::parse(&*text) | ||
73 | } | ||
74 | fn file_lines(db: &impl SyntaxDatabase, file_id: FileId) -> Arc<LineIndex> { | ||
75 | let text = db.file_text(file_id); | ||
76 | Arc::new(LineIndex::new(&*text)) | ||
77 | } | ||
78 | |||
79 | #[derive(Clone, Copy, Debug)] | ||
80 | pub struct FilePosition { | ||
81 | pub file_id: FileId, | ||
82 | pub offset: TextUnit, | ||
83 | } | ||
diff --git a/crates/ra_db/src/loc2id.rs b/crates/ra_db/src/loc2id.rs new file mode 100644 index 000000000..69ba43d0f --- /dev/null +++ b/crates/ra_db/src/loc2id.rs | |||
@@ -0,0 +1,100 @@ | |||
1 | use parking_lot::Mutex; | ||
2 | |||
3 | use std::hash::Hash; | ||
4 | |||
5 | use rustc_hash::FxHashMap; | ||
6 | |||
7 | /// There are two principle ways to refer to things: | ||
8 | /// - by their locatinon (module in foo/bar/baz.rs at line 42) | ||
9 | /// - by their numeric id (module `ModuleId(42)`) | ||
10 | /// | ||
11 | /// The first one is more powerful (you can actually find the thing in question | ||
12 | /// by id), but the second one is so much more compact. | ||
13 | /// | ||
14 | /// `Loc2IdMap` allows us to have a cake an eat it as well: by maintaining a | ||
15 | /// bidirectional mapping between positional and numeric ids, we can use compact | ||
16 | /// representation wich still allows us to get the actual item | ||
17 | #[derive(Debug)] | ||
18 | struct Loc2IdMap<LOC, ID> | ||
19 | where | ||
20 | ID: NumericId, | ||
21 | LOC: Clone + Eq + Hash, | ||
22 | { | ||
23 | loc2id: FxHashMap<LOC, ID>, | ||
24 | id2loc: FxHashMap<ID, LOC>, | ||
25 | } | ||
26 | |||
27 | impl<LOC, ID> Default for Loc2IdMap<LOC, ID> | ||
28 | where | ||
29 | ID: NumericId, | ||
30 | LOC: Clone + Eq + Hash, | ||
31 | { | ||
32 | fn default() -> Self { | ||
33 | Loc2IdMap { | ||
34 | loc2id: FxHashMap::default(), | ||
35 | id2loc: FxHashMap::default(), | ||
36 | } | ||
37 | } | ||
38 | } | ||
39 | |||
40 | impl<LOC, ID> Loc2IdMap<LOC, ID> | ||
41 | where | ||
42 | ID: NumericId, | ||
43 | LOC: Clone + Eq + Hash, | ||
44 | { | ||
45 | pub fn loc2id(&mut self, loc: &LOC) -> ID { | ||
46 | match self.loc2id.get(loc) { | ||
47 | Some(id) => return id.clone(), | ||
48 | None => (), | ||
49 | } | ||
50 | let id = self.loc2id.len(); | ||
51 | assert!(id < u32::max_value() as usize); | ||
52 | let id = ID::from_u32(id as u32); | ||
53 | self.loc2id.insert(loc.clone(), id.clone()); | ||
54 | self.id2loc.insert(id.clone(), loc.clone()); | ||
55 | id | ||
56 | } | ||
57 | |||
58 | pub fn id2loc(&self, id: ID) -> LOC { | ||
59 | self.id2loc[&id].clone() | ||
60 | } | ||
61 | } | ||
62 | |||
63 | pub trait NumericId: Clone + Eq + Hash { | ||
64 | fn from_u32(id: u32) -> Self; | ||
65 | fn to_u32(self) -> u32; | ||
66 | } | ||
67 | |||
68 | #[derive(Debug)] | ||
69 | pub struct LocationIntener<LOC, ID> | ||
70 | where | ||
71 | ID: NumericId, | ||
72 | LOC: Clone + Eq + Hash, | ||
73 | { | ||
74 | map: Mutex<Loc2IdMap<LOC, ID>>, | ||
75 | } | ||
76 | |||
77 | impl<LOC, ID> Default for LocationIntener<LOC, ID> | ||
78 | where | ||
79 | ID: NumericId, | ||
80 | LOC: Clone + Eq + Hash, | ||
81 | { | ||
82 | fn default() -> Self { | ||
83 | LocationIntener { | ||
84 | map: Default::default(), | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | impl<LOC, ID> LocationIntener<LOC, ID> | ||
90 | where | ||
91 | ID: NumericId, | ||
92 | LOC: Clone + Eq + Hash, | ||
93 | { | ||
94 | pub fn loc2id(&self, loc: &LOC) -> ID { | ||
95 | self.map.lock().loc2id(loc) | ||
96 | } | ||
97 | pub fn id2loc(&self, id: ID) -> LOC { | ||
98 | self.map.lock().id2loc(id) | ||
99 | } | ||
100 | } | ||
diff --git a/crates/ra_db/src/mock.rs b/crates/ra_db/src/mock.rs new file mode 100644 index 000000000..2840f9655 --- /dev/null +++ b/crates/ra_db/src/mock.rs | |||
@@ -0,0 +1,51 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use rustc_hash::FxHashSet; | ||
4 | use relative_path::{RelativePath, RelativePathBuf}; | ||
5 | |||
6 | use crate::{FileId, FileResolver, SourceRoot, FileResolverImp}; | ||
7 | |||
8 | #[derive(Default, Debug)] | ||
9 | pub struct FileMap(Vec<(FileId, RelativePathBuf)>); | ||
10 | |||
11 | impl FileMap { | ||
12 | pub fn add(&mut self, path: RelativePathBuf) -> FileId { | ||
13 | let file_id = FileId((self.0.len() + 1) as u32); | ||
14 | self.0.push((file_id, path)); | ||
15 | file_id | ||
16 | } | ||
17 | |||
18 | pub fn into_source_root(self) -> SourceRoot { | ||
19 | let files = self.files(); | ||
20 | let file_resolver = FileResolverImp::new(Arc::new(self)); | ||
21 | SourceRoot { | ||
22 | file_resolver, | ||
23 | files, | ||
24 | } | ||
25 | } | ||
26 | |||
27 | pub fn files(&self) -> FxHashSet<FileId> { | ||
28 | self.iter().map(|(id, _)| id).collect() | ||
29 | } | ||
30 | |||
31 | fn iter<'a>(&'a self) -> impl Iterator<Item = (FileId, &'a RelativePath)> + 'a { | ||
32 | self.0 | ||
33 | .iter() | ||
34 | .map(|(id, path)| (*id, path.as_relative_path())) | ||
35 | } | ||
36 | |||
37 | fn path(&self, id: FileId) -> &RelativePath { | ||
38 | self.iter().find(|&(it, _)| it == id).unwrap().1 | ||
39 | } | ||
40 | } | ||
41 | |||
42 | impl FileResolver for FileMap { | ||
43 | fn file_stem(&self, id: FileId) -> String { | ||
44 | self.path(id).file_stem().unwrap().to_string() | ||
45 | } | ||
46 | fn resolve(&self, id: FileId, rel: &RelativePath) -> Option<FileId> { | ||
47 | let path = self.path(id).join(rel).normalize(); | ||
48 | let id = self.iter().find(|&(_, p)| path == p)?.0; | ||
49 | Some(id) | ||
50 | } | ||
51 | } | ||
diff --git a/crates/ra_db/src/syntax_ptr.rs b/crates/ra_db/src/syntax_ptr.rs new file mode 100644 index 000000000..dac94dd36 --- /dev/null +++ b/crates/ra_db/src/syntax_ptr.rs | |||
@@ -0,0 +1,48 @@ | |||
1 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, TextRange}; | ||
2 | |||
3 | /// A pionter to a syntax node inside a file. | ||
4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
5 | pub struct LocalSyntaxPtr { | ||
6 | range: TextRange, | ||
7 | kind: SyntaxKind, | ||
8 | } | ||
9 | |||
10 | impl LocalSyntaxPtr { | ||
11 | pub fn new(node: SyntaxNodeRef) -> LocalSyntaxPtr { | ||
12 | LocalSyntaxPtr { | ||
13 | range: node.range(), | ||
14 | kind: node.kind(), | ||
15 | } | ||
16 | } | ||
17 | |||
18 | pub fn resolve(self, file: &SourceFileNode) -> SyntaxNode { | ||
19 | let mut curr = file.syntax(); | ||
20 | loop { | ||
21 | if curr.range() == self.range && curr.kind() == self.kind { | ||
22 | return curr.owned(); | ||
23 | } | ||
24 | curr = curr | ||
25 | .children() | ||
26 | .find(|it| self.range.is_subrange(&it.range())) | ||
27 | .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) | ||
28 | } | ||
29 | } | ||
30 | |||
31 | pub fn range(self) -> TextRange { | ||
32 | self.range | ||
33 | } | ||
34 | } | ||
35 | |||
36 | #[test] | ||
37 | fn test_local_syntax_ptr() { | ||
38 | use ra_syntax::{ast, AstNode}; | ||
39 | let file = SourceFileNode::parse("struct Foo { f: u32, }"); | ||
40 | let field = file | ||
41 | .syntax() | ||
42 | .descendants() | ||
43 | .find_map(ast::NamedFieldDef::cast) | ||
44 | .unwrap(); | ||
45 | let ptr = LocalSyntaxPtr::new(field.syntax()); | ||
46 | let field_syntax = ptr.resolve(&file); | ||
47 | assert_eq!(field.syntax(), field_syntax); | ||
48 | } | ||
diff --git a/crates/ra_editor/src/code_actions.rs b/crates/ra_editor/src/code_actions.rs index 0139b19d3..6979251d1 100644 --- a/crates/ra_editor/src/code_actions.rs +++ b/crates/ra_editor/src/code_actions.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | algo::{find_covering_node, find_leaf_at_offset}, | 4 | algo::{find_covering_node, find_leaf_at_offset}, |
5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, | 5 | ast::{self, AstNode, AttrsOwner, NameOwner, TypeParamsOwner}, |
6 | Direction, SourceFileNode, | 6 | Direction, SourceFileNode, |
7 | SyntaxKind::{COMMA, WHITESPACE}, | 7 | SyntaxKind::{COMMA, WHITESPACE, COMMENT}, |
8 | SyntaxNodeRef, TextRange, TextUnit, | 8 | SyntaxNodeRef, TextRange, TextUnit, |
9 | }; | 9 | }; |
10 | 10 | ||
@@ -41,7 +41,8 @@ pub fn add_derive<'a>( | |||
41 | offset: TextUnit, | 41 | offset: TextUnit, |
42 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { | 42 | ) -> Option<impl FnOnce() -> LocalEdit + 'a> { |
43 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; | 43 | let nominal = find_node_at_offset::<ast::NominalDef>(file.syntax(), offset)?; |
44 | Some(move || { | 44 | let node_start = derive_insertion_offset(nominal)?; |
45 | return Some(move || { | ||
45 | let derive_attr = nominal | 46 | let derive_attr = nominal |
46 | .attrs() | 47 | .attrs() |
47 | .filter_map(|x| x.as_call()) | 48 | .filter_map(|x| x.as_call()) |
@@ -51,7 +52,6 @@ pub fn add_derive<'a>( | |||
51 | let mut edit = EditBuilder::new(); | 52 | let mut edit = EditBuilder::new(); |
52 | let offset = match derive_attr { | 53 | let offset = match derive_attr { |
53 | None => { | 54 | None => { |
54 | let node_start = nominal.syntax().range().start(); | ||
55 | edit.insert(node_start, "#[derive()]\n".to_string()); | 55 | edit.insert(node_start, "#[derive()]\n".to_string()); |
56 | node_start + TextUnit::of_str("#[derive(") | 56 | node_start + TextUnit::of_str("#[derive(") |
57 | } | 57 | } |
@@ -61,7 +61,16 @@ pub fn add_derive<'a>( | |||
61 | edit: edit.finish(), | 61 | edit: edit.finish(), |
62 | cursor_position: Some(offset), | 62 | cursor_position: Some(offset), |
63 | } | 63 | } |
64 | }) | 64 | }); |
65 | |||
66 | // Insert `derive` after doc comments. | ||
67 | fn derive_insertion_offset(nominal: ast::NominalDef) -> Option<TextUnit> { | ||
68 | let non_ws_child = nominal | ||
69 | .syntax() | ||
70 | .children() | ||
71 | .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?; | ||
72 | Some(non_ws_child.range().start()) | ||
73 | } | ||
65 | } | 74 | } |
66 | 75 | ||
67 | pub fn add_impl<'a>( | 76 | pub fn add_impl<'a>( |
@@ -113,7 +122,7 @@ pub fn introduce_variable<'a>( | |||
113 | let node = find_covering_node(file.syntax(), range); | 122 | let node = find_covering_node(file.syntax(), range); |
114 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 123 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; |
115 | 124 | ||
116 | let anchor_stmt = ahchor_stmt(expr)?; | 125 | let anchor_stmt = anchor_stmt(expr)?; |
117 | let indent = anchor_stmt.prev_sibling()?; | 126 | let indent = anchor_stmt.prev_sibling()?; |
118 | if indent.kind() != WHITESPACE { | 127 | if indent.kind() != WHITESPACE { |
119 | return None; | 128 | return None; |
@@ -124,7 +133,12 @@ pub fn introduce_variable<'a>( | |||
124 | 133 | ||
125 | buf.push_str("let var_name = "); | 134 | buf.push_str("let var_name = "); |
126 | expr.syntax().text().push_to(&mut buf); | 135 | expr.syntax().text().push_to(&mut buf); |
127 | if expr.syntax().range().start() == anchor_stmt.range().start() { | 136 | let is_full_stmt = if let Some(expr_stmt) = ast::ExprStmt::cast(anchor_stmt) { |
137 | Some(expr.syntax()) == expr_stmt.expr().map(|e| e.syntax()) | ||
138 | } else { | ||
139 | false | ||
140 | }; | ||
141 | if is_full_stmt { | ||
128 | edit.replace(expr.syntax().range(), buf); | 142 | edit.replace(expr.syntax().range(), buf); |
129 | } else { | 143 | } else { |
130 | buf.push_str(";"); | 144 | buf.push_str(";"); |
@@ -141,7 +155,7 @@ pub fn introduce_variable<'a>( | |||
141 | 155 | ||
142 | /// Statement or last in the block expression, which will follow | 156 | /// Statement or last in the block expression, which will follow |
143 | /// the freshly introduced var. | 157 | /// the freshly introduced var. |
144 | fn ahchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { | 158 | fn anchor_stmt(expr: ast::Expr) -> Option<SyntaxNodeRef> { |
145 | expr.syntax().ancestors().find(|&node| { | 159 | expr.syntax().ancestors().find(|&node| { |
146 | if ast::Stmt::cast(node).is_some() { | 160 | if ast::Stmt::cast(node).is_some() { |
147 | return true; | 161 | return true; |
@@ -181,7 +195,7 @@ mod tests { | |||
181 | } | 195 | } |
182 | 196 | ||
183 | #[test] | 197 | #[test] |
184 | fn test_add_derive() { | 198 | fn add_derive_new() { |
185 | check_action( | 199 | check_action( |
186 | "struct Foo { a: i32, <|>}", | 200 | "struct Foo { a: i32, <|>}", |
187 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 201 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
@@ -192,6 +206,10 @@ mod tests { | |||
192 | "#[derive(<|>)]\nstruct Foo { a: i32, }", | 206 | "#[derive(<|>)]\nstruct Foo { a: i32, }", |
193 | |file, off| add_derive(file, off).map(|f| f()), | 207 | |file, off| add_derive(file, off).map(|f| f()), |
194 | ); | 208 | ); |
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn add_derive_existing() { | ||
195 | check_action( | 213 | check_action( |
196 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", | 214 | "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", |
197 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", | 215 | "#[derive(Clone<|>)]\nstruct Foo { a: i32, }", |
@@ -200,6 +218,24 @@ mod tests { | |||
200 | } | 218 | } |
201 | 219 | ||
202 | #[test] | 220 | #[test] |
221 | fn add_derive_new_with_doc_comment() { | ||
222 | check_action( | ||
223 | " | ||
224 | /// `Foo` is a pretty important struct. | ||
225 | /// It does stuff. | ||
226 | struct Foo { a: i32<|>, } | ||
227 | ", | ||
228 | " | ||
229 | /// `Foo` is a pretty important struct. | ||
230 | /// It does stuff. | ||
231 | #[derive(<|>)] | ||
232 | struct Foo { a: i32, } | ||
233 | ", | ||
234 | |file, off| add_derive(file, off).map(|f| f()), | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
203 | fn test_add_impl() { | 239 | fn test_add_impl() { |
204 | check_action( | 240 | check_action( |
205 | "struct Foo {<|>}\n", | 241 | "struct Foo {<|>}\n", |
@@ -219,7 +255,7 @@ mod tests { | |||
219 | } | 255 | } |
220 | 256 | ||
221 | #[test] | 257 | #[test] |
222 | fn test_intrdoduce_var_simple() { | 258 | fn test_introduce_var_simple() { |
223 | check_action_range( | 259 | check_action_range( |
224 | " | 260 | " |
225 | fn foo() { | 261 | fn foo() { |
@@ -235,7 +271,7 @@ fn foo() { | |||
235 | } | 271 | } |
236 | 272 | ||
237 | #[test] | 273 | #[test] |
238 | fn test_intrdoduce_var_expr_stmt() { | 274 | fn test_introduce_var_expr_stmt() { |
239 | check_action_range( | 275 | check_action_range( |
240 | " | 276 | " |
241 | fn foo() { | 277 | fn foo() { |
@@ -250,7 +286,23 @@ fn foo() { | |||
250 | } | 286 | } |
251 | 287 | ||
252 | #[test] | 288 | #[test] |
253 | fn test_intrdoduce_var_last_expr() { | 289 | fn test_introduce_var_part_of_expr_stmt() { |
290 | check_action_range( | ||
291 | " | ||
292 | fn foo() { | ||
293 | <|>1<|> + 1; | ||
294 | }", | ||
295 | " | ||
296 | fn foo() { | ||
297 | let <|>var_name = 1; | ||
298 | var_name + 1; | ||
299 | }", | ||
300 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
301 | ); | ||
302 | } | ||
303 | |||
304 | #[test] | ||
305 | fn test_introduce_var_last_expr() { | ||
254 | check_action_range( | 306 | check_action_range( |
255 | " | 307 | " |
256 | fn foo() { | 308 | fn foo() { |
@@ -265,4 +317,20 @@ fn foo() { | |||
265 | ); | 317 | ); |
266 | } | 318 | } |
267 | 319 | ||
320 | #[test] | ||
321 | fn test_introduce_var_last_full_expr() { | ||
322 | check_action_range( | ||
323 | " | ||
324 | fn foo() { | ||
325 | <|>bar(1 + 1)<|> | ||
326 | }", | ||
327 | " | ||
328 | fn foo() { | ||
329 | let <|>var_name = bar(1 + 1); | ||
330 | var_name | ||
331 | }", | ||
332 | |file, range| introduce_variable(file, range).map(|f| f()), | ||
333 | ); | ||
334 | } | ||
335 | |||
268 | } | 336 | } |
diff --git a/crates/ra_editor/src/edit.rs b/crates/ra_editor/src/edit.rs index c3149ec54..372b8d14c 100644 --- a/crates/ra_editor/src/edit.rs +++ b/crates/ra_editor/src/edit.rs | |||
@@ -26,7 +26,7 @@ impl EditBuilder { | |||
26 | } | 26 | } |
27 | pub fn finish(self) -> Edit { | 27 | pub fn finish(self) -> Edit { |
28 | let mut atoms = self.atoms; | 28 | let mut atoms = self.atoms; |
29 | atoms.sort_by_key(|a| a.delete.start()); | 29 | atoms.sort_by_key(|a| (a.delete.start(), a.delete.end())); |
30 | for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { | 30 | for (a1, a2) in atoms.iter().zip(atoms.iter().skip(1)) { |
31 | assert!(a1.delete.end() <= a2.delete.start()) | 31 | assert!(a1.delete.end() <= a2.delete.start()) |
32 | } | 32 | } |
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml new file mode 100644 index 000000000..1b9e148b2 --- /dev/null +++ b/crates/ra_hir/Cargo.toml | |||
@@ -0,0 +1,17 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_hir" | ||
4 | version = "0.1.0" | ||
5 | authors = ["Aleksey Kladov <[email protected]>"] | ||
6 | |||
7 | [dependencies] | ||
8 | log = "0.4.5" | ||
9 | relative-path = "0.4.0" | ||
10 | salsa = "0.8.0" | ||
11 | rustc-hash = "1.0" | ||
12 | parking_lot = "0.6.4" | ||
13 | id-arena = "2.0" | ||
14 | ra_syntax = { path = "../ra_syntax" } | ||
15 | ra_editor = { path = "../ra_editor" } | ||
16 | ra_db = { path = "../ra_db" } | ||
17 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/ra_hir/src/arena.rs b/crates/ra_hir/src/arena.rs new file mode 100644 index 000000000..d4f9d9cb9 --- /dev/null +++ b/crates/ra_hir/src/arena.rs | |||
@@ -0,0 +1,66 @@ | |||
1 | //! A simple id-based arena, similar to https://github.com/fitzgen/id-arena. | ||
2 | //! We use our own version for more compact id's and to allow inherent impls | ||
3 | //! on Ids. | ||
4 | |||
5 | use std::{ | ||
6 | fmt, | ||
7 | hash::{Hash, Hasher}, | ||
8 | marker::PhantomData, | ||
9 | }; | ||
10 | |||
11 | pub struct Id<T> { | ||
12 | idx: u32, | ||
13 | _ty: PhantomData<fn() -> T>, | ||
14 | } | ||
15 | |||
16 | impl<T> fmt::Debug for Id<T> { | ||
17 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
18 | f.debug_tuple("Id").field(&self.idx).finish() | ||
19 | } | ||
20 | } | ||
21 | impl<T> Copy for Id<T> {} | ||
22 | impl<T> Clone for Id<T> { | ||
23 | fn clone(&self) -> Id<T> { | ||
24 | *self | ||
25 | } | ||
26 | } | ||
27 | |||
28 | impl<T> PartialEq for Id<T> { | ||
29 | fn eq(&self, other: &Id<T>) -> bool { | ||
30 | self.idx == other.idx | ||
31 | } | ||
32 | } | ||
33 | |||
34 | impl<T> Eq for Id<T> {} | ||
35 | |||
36 | impl<T> Hash for Id<T> { | ||
37 | fn hash<H: Hasher>(&self, h: &mut H) { | ||
38 | self.idx.hash(h); | ||
39 | } | ||
40 | } | ||
41 | |||
42 | #[derive(Debug, PartialEq, Eq)] | ||
43 | pub(crate) struct ArenaBehavior<T> { | ||
44 | _ty: PhantomData<T>, | ||
45 | } | ||
46 | |||
47 | impl<T> id_arena::ArenaBehavior for ArenaBehavior<T> { | ||
48 | type Id = Id<T>; | ||
49 | fn new_arena_id() -> u32 { | ||
50 | 0 | ||
51 | } | ||
52 | fn new_id(_arena_id: u32, index: usize) -> Id<T> { | ||
53 | Id { | ||
54 | idx: index as u32, | ||
55 | _ty: PhantomData, | ||
56 | } | ||
57 | } | ||
58 | fn index(id: Id<T>) -> usize { | ||
59 | id.idx as usize | ||
60 | } | ||
61 | fn arena_id(_id: Id<T>) -> u32 { | ||
62 | 0 | ||
63 | } | ||
64 | } | ||
65 | |||
66 | pub(crate) type Arena<T> = id_arena::Arena<T, ArenaBehavior<T>>; | ||
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs new file mode 100644 index 000000000..2f01bae6d --- /dev/null +++ b/crates/ra_hir/src/db.rs | |||
@@ -0,0 +1,66 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | SyntaxNode, | ||
5 | ast::FnDefNode, | ||
6 | }; | ||
7 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; | ||
8 | |||
9 | use crate::{ | ||
10 | DefLoc, DefId, FnId, | ||
11 | SourceFileItems, SourceItemId, | ||
12 | query_definitions, | ||
13 | FnScopes, | ||
14 | module::{ModuleId, ModuleTree, ModuleSource, | ||
15 | nameres::{ItemMap, InputModuleItems}}, | ||
16 | }; | ||
17 | |||
18 | salsa::query_group! { | ||
19 | |||
20 | pub trait HirDatabase: SyntaxDatabase | ||
21 | + AsRef<LocationIntener<DefLoc, DefId>> | ||
22 | + AsRef<LocationIntener<SourceItemId, FnId>> | ||
23 | { | ||
24 | fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { | ||
25 | type FnScopesQuery; | ||
26 | use fn query_definitions::fn_scopes; | ||
27 | } | ||
28 | fn fn_syntax(fn_id: FnId) -> FnDefNode { | ||
29 | type FnSyntaxQuery; | ||
30 | // Don't retain syntax trees in memory | ||
31 | storage dependencies; | ||
32 | use fn query_definitions::fn_syntax; | ||
33 | } | ||
34 | |||
35 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | ||
36 | type SourceFileItemsQuery; | ||
37 | storage dependencies; | ||
38 | use fn query_definitions::file_items; | ||
39 | } | ||
40 | |||
41 | fn file_item(source_item_id: SourceItemId) -> SyntaxNode { | ||
42 | type FileItemQuery; | ||
43 | storage dependencies; | ||
44 | use fn query_definitions::file_item; | ||
45 | } | ||
46 | |||
47 | fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module::imp::Submodule>>> { | ||
48 | type SubmodulesQuery; | ||
49 | use fn query_definitions::submodules; | ||
50 | } | ||
51 | |||
52 | fn input_module_items(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<InputModuleItems>> { | ||
53 | type InputModuleItemsQuery; | ||
54 | use fn query_definitions::input_module_items; | ||
55 | } | ||
56 | fn item_map(source_root_id: SourceRootId) -> Cancelable<Arc<ItemMap>> { | ||
57 | type ItemMapQuery; | ||
58 | use fn query_definitions::item_map; | ||
59 | } | ||
60 | fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { | ||
61 | type ModuleTreeQuery; | ||
62 | use fn crate::module::imp::module_tree; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | } | ||
diff --git a/crates/ra_analysis/src/descriptors/function/mod.rs b/crates/ra_hir/src/function/mod.rs index 86eee5e93..c8af2e54f 100644 --- a/crates/ra_analysis/src/descriptors/function/mod.rs +++ b/crates/ra_hir/src/function/mod.rs | |||
@@ -1,30 +1,83 @@ | |||
1 | pub(super) mod imp; | ||
2 | mod scope; | 1 | mod scope; |
3 | 2 | ||
4 | use std::cmp::{max, min}; | 3 | use std::{ |
4 | cmp::{max, min}, | ||
5 | sync::Arc, | ||
6 | }; | ||
5 | 7 | ||
6 | use ra_syntax::{ | 8 | use ra_syntax::{ |
9 | TextRange, TextUnit, SyntaxNodeRef, | ||
7 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, | 10 | ast::{self, AstNode, DocCommentsOwner, NameOwner}, |
8 | TextRange, TextUnit, | ||
9 | }; | 11 | }; |
12 | use ra_db::FileId; | ||
10 | 13 | ||
11 | use crate::{ | 14 | use crate::{ |
12 | syntax_ptr::SyntaxPtr, FileId, | 15 | FnId, HirDatabase, SourceItemId, |
13 | loc2id::IdDatabase, | ||
14 | }; | 16 | }; |
15 | 17 | ||
16 | pub(crate) use self::scope::{resolve_local_name, FnScopes}; | 18 | pub use self::scope::FnScopes; |
17 | pub(crate) use crate::loc2id::FnId; | ||
18 | 19 | ||
19 | impl FnId { | 20 | impl FnId { |
20 | pub(crate) fn get(db: &impl IdDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId { | 21 | pub fn get(db: &impl HirDatabase, file_id: FileId, fn_def: ast::FnDef) -> FnId { |
21 | let ptr = SyntaxPtr::new(file_id, fn_def.syntax()); | 22 | let file_items = db.file_items(file_id); |
22 | db.id_maps().fn_id(ptr) | 23 | let item_id = file_items.id_of(fn_def.syntax()); |
24 | let item_id = SourceItemId { file_id, item_id }; | ||
25 | FnId::from_loc(db, &item_id) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | pub struct Function { | ||
30 | fn_id: FnId, | ||
31 | } | ||
32 | |||
33 | impl Function { | ||
34 | pub fn guess_from_source( | ||
35 | db: &impl HirDatabase, | ||
36 | file_id: FileId, | ||
37 | fn_def: ast::FnDef, | ||
38 | ) -> Function { | ||
39 | let fn_id = FnId::get(db, file_id, fn_def); | ||
40 | Function { fn_id } | ||
41 | } | ||
42 | |||
43 | pub fn guess_for_name_ref( | ||
44 | db: &impl HirDatabase, | ||
45 | file_id: FileId, | ||
46 | name_ref: ast::NameRef, | ||
47 | ) -> Option<Function> { | ||
48 | Function::guess_for_node(db, file_id, name_ref.syntax()) | ||
49 | } | ||
50 | |||
51 | pub fn guess_for_bind_pat( | ||
52 | db: &impl HirDatabase, | ||
53 | file_id: FileId, | ||
54 | bind_pat: ast::BindPat, | ||
55 | ) -> Option<Function> { | ||
56 | Function::guess_for_node(db, file_id, bind_pat.syntax()) | ||
57 | } | ||
58 | |||
59 | fn guess_for_node( | ||
60 | db: &impl HirDatabase, | ||
61 | file_id: FileId, | ||
62 | node: SyntaxNodeRef, | ||
63 | ) -> Option<Function> { | ||
64 | let fn_def = node.ancestors().find_map(ast::FnDef::cast)?; | ||
65 | let res = Function::guess_from_source(db, file_id, fn_def); | ||
66 | Some(res) | ||
67 | } | ||
68 | |||
69 | pub fn scope(&self, db: &impl HirDatabase) -> Arc<FnScopes> { | ||
70 | db.fn_scopes(self.fn_id) | ||
71 | } | ||
72 | |||
73 | pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { | ||
74 | let syntax = db.fn_syntax(self.fn_id); | ||
75 | FnSignatureInfo::new(syntax.borrowed()) | ||
23 | } | 76 | } |
24 | } | 77 | } |
25 | 78 | ||
26 | #[derive(Debug, Clone)] | 79 | #[derive(Debug, Clone)] |
27 | pub struct FnDescriptor { | 80 | pub struct FnSignatureInfo { |
28 | pub name: String, | 81 | pub name: String, |
29 | pub label: String, | 82 | pub label: String, |
30 | pub ret_type: Option<String>, | 83 | pub ret_type: Option<String>, |
@@ -32,8 +85,8 @@ pub struct FnDescriptor { | |||
32 | pub doc: Option<String>, | 85 | pub doc: Option<String>, |
33 | } | 86 | } |
34 | 87 | ||
35 | impl FnDescriptor { | 88 | impl FnSignatureInfo { |
36 | pub fn new(node: ast::FnDef) -> Option<Self> { | 89 | fn new(node: ast::FnDef) -> Option<Self> { |
37 | let name = node.name()?.text().to_string(); | 90 | let name = node.name()?.text().to_string(); |
38 | 91 | ||
39 | let mut doc = None; | 92 | let mut doc = None; |
@@ -52,7 +105,7 @@ impl FnDescriptor { | |||
52 | node.syntax().text().to_string() | 105 | node.syntax().text().to_string() |
53 | }; | 106 | }; |
54 | 107 | ||
55 | if let Some((comment_range, docs)) = FnDescriptor::extract_doc_comments(node) { | 108 | if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) { |
56 | let comment_range = comment_range | 109 | let comment_range = comment_range |
57 | .checked_sub(node.syntax().range().start()) | 110 | .checked_sub(node.syntax().range().start()) |
58 | .unwrap(); | 111 | .unwrap(); |
@@ -84,10 +137,10 @@ impl FnDescriptor { | |||
84 | } | 137 | } |
85 | } | 138 | } |
86 | 139 | ||
87 | let params = FnDescriptor::param_list(node); | 140 | let params = FnSignatureInfo::param_list(node); |
88 | let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); | 141 | let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); |
89 | 142 | ||
90 | Some(FnDescriptor { | 143 | Some(FnSignatureInfo { |
91 | name, | 144 | name, |
92 | ret_type, | 145 | ret_type, |
93 | params, | 146 | params, |
diff --git a/crates/ra_analysis/src/descriptors/function/scope.rs b/crates/ra_hir/src/function/scope.rs index 54d7fa456..863453291 100644 --- a/crates/ra_analysis/src/descriptors/function/scope.rs +++ b/crates/ra_hir/src/function/scope.rs | |||
@@ -1,13 +1,13 @@ | |||
1 | use rustc_hash::{FxHashMap, FxHashSet}; | 1 | use rustc_hash::{FxHashMap, FxHashSet}; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::{ |
4 | AstNode, SmolStr, SyntaxNodeRef, TextRange, | ||
4 | algo::generate, | 5 | algo::generate, |
5 | ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, | 6 | ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, |
6 | AstNode, SmolStr, SyntaxNodeRef, | ||
7 | }; | 7 | }; |
8 | use ra_db::LocalSyntaxPtr; | ||
8 | 9 | ||
9 | use crate::{ | 10 | use crate::{ |
10 | syntax_ptr::LocalSyntaxPtr, | ||
11 | arena::{Arena, Id}, | 11 | arena::{Arena, Id}, |
12 | }; | 12 | }; |
13 | 13 | ||
@@ -15,7 +15,7 @@ pub(crate) type ScopeId = Id<ScopeData>; | |||
15 | 15 | ||
16 | #[derive(Debug, PartialEq, Eq)] | 16 | #[derive(Debug, PartialEq, Eq)] |
17 | pub struct FnScopes { | 17 | pub struct FnScopes { |
18 | pub(crate) self_param: Option<LocalSyntaxPtr>, | 18 | pub self_param: Option<LocalSyntaxPtr>, |
19 | scopes: Arena<ScopeData>, | 19 | scopes: Arena<ScopeData>, |
20 | scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, | 20 | scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>, |
21 | } | 21 | } |
@@ -27,13 +27,13 @@ pub struct ScopeEntry { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | #[derive(Debug, PartialEq, Eq)] | 29 | #[derive(Debug, PartialEq, Eq)] |
30 | pub(crate) struct ScopeData { | 30 | pub struct ScopeData { |
31 | parent: Option<ScopeId>, | 31 | parent: Option<ScopeId>, |
32 | entries: Vec<ScopeEntry>, | 32 | entries: Vec<ScopeEntry>, |
33 | } | 33 | } |
34 | 34 | ||
35 | impl FnScopes { | 35 | impl FnScopes { |
36 | pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes { | 36 | pub fn new(fn_def: ast::FnDef) -> FnScopes { |
37 | let mut scopes = FnScopes { | 37 | let mut scopes = FnScopes { |
38 | self_param: fn_def | 38 | self_param: fn_def |
39 | .param_list() | 39 | .param_list() |
@@ -49,7 +49,7 @@ impl FnScopes { | |||
49 | } | 49 | } |
50 | scopes | 50 | scopes |
51 | } | 51 | } |
52 | pub(crate) fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { | 52 | pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { |
53 | &self.scopes[scope].entries | 53 | &self.scopes[scope].entries |
54 | } | 54 | } |
55 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { | 55 | pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a { |
@@ -57,14 +57,45 @@ impl FnScopes { | |||
57 | self.scopes[scope].parent | 57 | self.scopes[scope].parent |
58 | }) | 58 | }) |
59 | } | 59 | } |
60 | pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { | ||
61 | let mut shadowed = FxHashSet::default(); | ||
62 | let ret = self | ||
63 | .scope_chain(name_ref.syntax()) | ||
64 | .flat_map(|scope| self.entries(scope).iter()) | ||
65 | .filter(|entry| shadowed.insert(entry.name())) | ||
66 | .filter(|entry| entry.name() == &name_ref.text()) | ||
67 | .nth(0); | ||
68 | ret | ||
69 | } | ||
70 | |||
71 | pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> { | ||
72 | let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap(); | ||
73 | let name_ptr = LocalSyntaxPtr::new(pat.syntax()); | ||
74 | let refs: Vec<_> = fn_def | ||
75 | .syntax() | ||
76 | .descendants() | ||
77 | .filter_map(ast::NameRef::cast) | ||
78 | .filter(|name_ref| match self.resolve_local_name(*name_ref) { | ||
79 | None => false, | ||
80 | Some(entry) => entry.ptr() == name_ptr, | ||
81 | }) | ||
82 | .map(|name_ref| ReferenceDescriptor { | ||
83 | name: name_ref.syntax().text().to_string(), | ||
84 | range: name_ref.syntax().range(), | ||
85 | }) | ||
86 | .collect(); | ||
87 | |||
88 | refs | ||
89 | } | ||
90 | |||
60 | fn root_scope(&mut self) -> ScopeId { | 91 | fn root_scope(&mut self) -> ScopeId { |
61 | self.scopes.push(ScopeData { | 92 | self.scopes.alloc(ScopeData { |
62 | parent: None, | 93 | parent: None, |
63 | entries: vec![], | 94 | entries: vec![], |
64 | }) | 95 | }) |
65 | } | 96 | } |
66 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { | 97 | fn new_scope(&mut self, parent: ScopeId) -> ScopeId { |
67 | self.scopes.push(ScopeData { | 98 | self.scopes.alloc(ScopeData { |
68 | parent: Some(parent), | 99 | parent: Some(parent), |
69 | entries: vec![], | 100 | entries: vec![], |
70 | }) | 101 | }) |
@@ -104,10 +135,10 @@ impl ScopeEntry { | |||
104 | }; | 135 | }; |
105 | Some(res) | 136 | Some(res) |
106 | } | 137 | } |
107 | pub(crate) fn name(&self) -> &SmolStr { | 138 | pub fn name(&self) -> &SmolStr { |
108 | &self.name | 139 | &self.name |
109 | } | 140 | } |
110 | pub(crate) fn ptr(&self) -> LocalSyntaxPtr { | 141 | pub fn ptr(&self) -> LocalSyntaxPtr { |
111 | self.ptr | 142 | self.ptr |
112 | } | 143 | } |
113 | } | 144 | } |
@@ -249,18 +280,10 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { | |||
249 | } | 280 | } |
250 | } | 281 | } |
251 | 282 | ||
252 | pub fn resolve_local_name<'a>( | 283 | #[derive(Debug)] |
253 | name_ref: ast::NameRef, | 284 | pub struct ReferenceDescriptor { |
254 | scopes: &'a FnScopes, | 285 | pub range: TextRange, |
255 | ) -> Option<&'a ScopeEntry> { | 286 | pub name: String, |
256 | let mut shadowed = FxHashSet::default(); | ||
257 | let ret = scopes | ||
258 | .scope_chain(name_ref.syntax()) | ||
259 | .flat_map(|scope| scopes.entries(scope).iter()) | ||
260 | .filter(|entry| shadowed.insert(entry.name())) | ||
261 | .filter(|entry| entry.name() == &name_ref.text()) | ||
262 | .nth(0); | ||
263 | ret | ||
264 | } | 287 | } |
265 | 288 | ||
266 | #[cfg(test)] | 289 | #[cfg(test)] |
@@ -376,7 +399,7 @@ mod tests { | |||
376 | 399 | ||
377 | let scopes = FnScopes::new(fn_def); | 400 | let scopes = FnScopes::new(fn_def); |
378 | 401 | ||
379 | let local_name_entry = resolve_local_name(name_ref, &scopes).unwrap(); | 402 | let local_name_entry = scopes.resolve_local_name(name_ref).unwrap(); |
380 | let local_name = local_name_entry.ptr().resolve(&file); | 403 | let local_name = local_name_entry.ptr().resolve(&file); |
381 | let expected_name = | 404 | let expected_name = |
382 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); | 405 | find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap(); |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs new file mode 100644 index 000000000..e7b6a81f4 --- /dev/null +++ b/crates/ra_hir/src/lib.rs | |||
@@ -0,0 +1,141 @@ | |||
1 | //! HIR (previsouly known as descriptors) provides a high-level OO acess to Rust | ||
2 | //! code. | ||
3 | //! | ||
4 | //! The principal difference between HIR and syntax trees is that HIR is bound | ||
5 | //! to a particular crate instance. That is, it has cfg flags and features | ||
6 | //! applied. So, there relation between syntax and HIR is many-to-one. | ||
7 | |||
8 | macro_rules! ctry { | ||
9 | ($expr:expr) => { | ||
10 | match $expr { | ||
11 | None => return Ok(None), | ||
12 | Some(it) => it, | ||
13 | } | ||
14 | }; | ||
15 | } | ||
16 | |||
17 | pub mod db; | ||
18 | #[cfg(test)] | ||
19 | mod mock; | ||
20 | mod query_definitions; | ||
21 | mod function; | ||
22 | mod module; | ||
23 | mod path; | ||
24 | mod arena; | ||
25 | |||
26 | use std::ops::Index; | ||
27 | |||
28 | use ra_syntax::{SyntaxNodeRef, SyntaxNode}; | ||
29 | use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; | ||
30 | |||
31 | use crate::{ | ||
32 | db::HirDatabase, | ||
33 | arena::{Arena, Id}, | ||
34 | }; | ||
35 | |||
36 | pub use self::{ | ||
37 | path::{Path, PathKind}, | ||
38 | module::{Module, ModuleId, Problem, nameres::ItemMap}, | ||
39 | function::{Function, FnScopes}, | ||
40 | }; | ||
41 | |||
42 | pub use self::function::FnSignatureInfo; | ||
43 | |||
44 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
45 | pub struct FnId(u32); | ||
46 | ra_db::impl_numeric_id!(FnId); | ||
47 | |||
48 | impl FnId { | ||
49 | pub fn from_loc( | ||
50 | db: &impl AsRef<LocationIntener<SourceItemId, FnId>>, | ||
51 | loc: &SourceItemId, | ||
52 | ) -> FnId { | ||
53 | db.as_ref().loc2id(loc) | ||
54 | } | ||
55 | pub fn loc(self, db: &impl AsRef<LocationIntener<SourceItemId, FnId>>) -> SourceItemId { | ||
56 | db.as_ref().id2loc(self) | ||
57 | } | ||
58 | } | ||
59 | |||
60 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
61 | pub struct DefId(u32); | ||
62 | ra_db::impl_numeric_id!(DefId); | ||
63 | |||
64 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
65 | pub enum DefLoc { | ||
66 | Module { | ||
67 | id: ModuleId, | ||
68 | source_root: SourceRootId, | ||
69 | }, | ||
70 | Item { | ||
71 | source_item_id: SourceItemId, | ||
72 | }, | ||
73 | } | ||
74 | |||
75 | impl DefId { | ||
76 | pub fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { | ||
77 | db.as_ref().id2loc(self) | ||
78 | } | ||
79 | } | ||
80 | |||
81 | impl DefLoc { | ||
82 | pub fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId { | ||
83 | db.as_ref().loc2id(&self) | ||
84 | } | ||
85 | } | ||
86 | |||
87 | pub enum Def { | ||
88 | Module(Module), | ||
89 | Item, | ||
90 | } | ||
91 | |||
92 | impl DefId { | ||
93 | pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> { | ||
94 | let loc = self.loc(db); | ||
95 | let res = match loc { | ||
96 | DefLoc::Module { id, source_root } => { | ||
97 | let descr = Module::new(db, source_root, id)?; | ||
98 | Def::Module(descr) | ||
99 | } | ||
100 | DefLoc::Item { .. } => Def::Item, | ||
101 | }; | ||
102 | Ok(res) | ||
103 | } | ||
104 | } | ||
105 | |||
106 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
107 | /// it's OK to use it as a salsa key/value. | ||
108 | pub(crate) type SourceFileItemId = Id<SyntaxNode>; | ||
109 | |||
110 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
111 | pub struct SourceItemId { | ||
112 | file_id: FileId, | ||
113 | item_id: SourceFileItemId, | ||
114 | } | ||
115 | |||
116 | /// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. | ||
117 | #[derive(Debug, PartialEq, Eq, Default)] | ||
118 | pub struct SourceFileItems { | ||
119 | arena: Arena<SyntaxNode>, | ||
120 | } | ||
121 | |||
122 | impl SourceFileItems { | ||
123 | fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { | ||
124 | self.arena.alloc(item) | ||
125 | } | ||
126 | pub fn id_of(&self, item: SyntaxNodeRef) -> SourceFileItemId { | ||
127 | let (id, _item) = self | ||
128 | .arena | ||
129 | .iter() | ||
130 | .find(|(_id, i)| i.borrowed() == item) | ||
131 | .unwrap(); | ||
132 | id | ||
133 | } | ||
134 | } | ||
135 | |||
136 | impl Index<SourceFileItemId> for SourceFileItems { | ||
137 | type Output = SyntaxNode; | ||
138 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { | ||
139 | &self.arena[idx] | ||
140 | } | ||
141 | } | ||
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs new file mode 100644 index 000000000..8e256b89f --- /dev/null +++ b/crates/ra_hir/src/mock.rs | |||
@@ -0,0 +1,172 @@ | |||
1 | use std::sync::Arc; | ||
2 | |||
3 | use parking_lot::Mutex; | ||
4 | use salsa::{self, Database}; | ||
5 | use ra_db::{LocationIntener, BaseDatabase, FilePosition, mock::FileMap, FileId, WORKSPACE}; | ||
6 | use relative_path::RelativePathBuf; | ||
7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | ||
8 | |||
9 | use crate::{db, DefId, DefLoc, FnId, SourceItemId}; | ||
10 | |||
11 | #[derive(Debug)] | ||
12 | pub(crate) struct MockDatabase { | ||
13 | events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>, | ||
14 | runtime: salsa::Runtime<MockDatabase>, | ||
15 | id_maps: Arc<IdMaps>, | ||
16 | } | ||
17 | |||
18 | impl MockDatabase { | ||
19 | pub(crate) fn with_position(fixture: &str) -> (MockDatabase, FilePosition) { | ||
20 | let mut db = MockDatabase::default(); | ||
21 | |||
22 | let mut position = None; | ||
23 | let mut file_map = FileMap::default(); | ||
24 | for entry in parse_fixture(fixture) { | ||
25 | if entry.text.contains(CURSOR_MARKER) { | ||
26 | assert!( | ||
27 | position.is_none(), | ||
28 | "only one marker (<|>) per fixture is allowed" | ||
29 | ); | ||
30 | position = Some(db.add_file_with_position(&mut file_map, &entry.meta, &entry.text)); | ||
31 | } else { | ||
32 | db.add_file(&mut file_map, &entry.meta, &entry.text); | ||
33 | } | ||
34 | } | ||
35 | let position = position.expect("expected a marker (<|>)"); | ||
36 | let source_root = file_map.into_source_root(); | ||
37 | db.query_mut(ra_db::SourceRootQuery) | ||
38 | .set(WORKSPACE, Arc::new(source_root)); | ||
39 | (db, position) | ||
40 | } | ||
41 | |||
42 | fn add_file(&mut self, file_map: &mut FileMap, path: &str, text: &str) -> FileId { | ||
43 | assert!(path.starts_with('/')); | ||
44 | let path = RelativePathBuf::from_path(&path[1..]).unwrap(); | ||
45 | |||
46 | let file_id = file_map.add(path); | ||
47 | let text = Arc::new(text.to_string()); | ||
48 | self.query_mut(ra_db::FileTextQuery).set(file_id, text); | ||
49 | self.query_mut(ra_db::FileSourceRootQuery) | ||
50 | .set(file_id, WORKSPACE); | ||
51 | file_id | ||
52 | } | ||
53 | |||
54 | fn add_file_with_position( | ||
55 | &mut self, | ||
56 | file_map: &mut FileMap, | ||
57 | path: &str, | ||
58 | text: &str, | ||
59 | ) -> FilePosition { | ||
60 | let (offset, text) = extract_offset(text); | ||
61 | let file_id = self.add_file(file_map, path, &text); | ||
62 | FilePosition { file_id, offset } | ||
63 | } | ||
64 | } | ||
65 | |||
66 | #[derive(Debug, Default)] | ||
67 | struct IdMaps { | ||
68 | fns: LocationIntener<SourceItemId, FnId>, | ||
69 | defs: LocationIntener<DefLoc, DefId>, | ||
70 | } | ||
71 | |||
72 | impl salsa::Database for MockDatabase { | ||
73 | fn salsa_runtime(&self) -> &salsa::Runtime<MockDatabase> { | ||
74 | &self.runtime | ||
75 | } | ||
76 | |||
77 | fn salsa_event(&self, event: impl Fn() -> salsa::Event<MockDatabase>) { | ||
78 | let mut events = self.events.lock(); | ||
79 | if let Some(events) = &mut *events { | ||
80 | events.push(event()); | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl Default for MockDatabase { | ||
86 | fn default() -> MockDatabase { | ||
87 | let mut db = MockDatabase { | ||
88 | events: Default::default(), | ||
89 | runtime: salsa::Runtime::default(), | ||
90 | id_maps: Default::default(), | ||
91 | }; | ||
92 | db.query_mut(ra_db::SourceRootQuery) | ||
93 | .set(ra_db::WORKSPACE, Default::default()); | ||
94 | db.query_mut(ra_db::CrateGraphQuery) | ||
95 | .set((), Default::default()); | ||
96 | db.query_mut(ra_db::LibrariesQuery) | ||
97 | .set((), Default::default()); | ||
98 | db | ||
99 | } | ||
100 | } | ||
101 | |||
102 | impl salsa::ParallelDatabase for MockDatabase { | ||
103 | fn snapshot(&self) -> salsa::Snapshot<MockDatabase> { | ||
104 | salsa::Snapshot::new(MockDatabase { | ||
105 | events: Default::default(), | ||
106 | runtime: self.runtime.snapshot(self), | ||
107 | id_maps: self.id_maps.clone(), | ||
108 | }) | ||
109 | } | ||
110 | } | ||
111 | |||
112 | impl BaseDatabase for MockDatabase {} | ||
113 | |||
114 | impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase { | ||
115 | fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { | ||
116 | &self.id_maps.defs | ||
117 | } | ||
118 | } | ||
119 | |||
120 | impl AsRef<LocationIntener<SourceItemId, FnId>> for MockDatabase { | ||
121 | fn as_ref(&self) -> &LocationIntener<SourceItemId, FnId> { | ||
122 | &self.id_maps.fns | ||
123 | } | ||
124 | } | ||
125 | |||
126 | impl MockDatabase { | ||
127 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { | ||
128 | *self.events.lock() = Some(Vec::new()); | ||
129 | f(); | ||
130 | let events = self.events.lock().take().unwrap(); | ||
131 | events | ||
132 | } | ||
133 | |||
134 | pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> { | ||
135 | let events = self.log(f); | ||
136 | events | ||
137 | .into_iter() | ||
138 | .filter_map(|e| match e.kind { | ||
139 | // This pretty horrible, but `Debug` is the only way to inspect | ||
140 | // QueryDescriptor at the moment. | ||
141 | salsa::EventKind::WillExecute { descriptor } => Some(format!("{:?}", descriptor)), | ||
142 | _ => None, | ||
143 | }) | ||
144 | .collect() | ||
145 | } | ||
146 | } | ||
147 | |||
148 | salsa::database_storage! { | ||
149 | pub(crate) struct MockDatabaseStorage for MockDatabase { | ||
150 | impl ra_db::FilesDatabase { | ||
151 | fn file_text() for ra_db::FileTextQuery; | ||
152 | fn file_source_root() for ra_db::FileSourceRootQuery; | ||
153 | fn source_root() for ra_db::SourceRootQuery; | ||
154 | fn libraries() for ra_db::LibrariesQuery; | ||
155 | fn crate_graph() for ra_db::CrateGraphQuery; | ||
156 | } | ||
157 | impl ra_db::SyntaxDatabase { | ||
158 | fn source_file() for ra_db::SourceFileQuery; | ||
159 | fn file_lines() for ra_db::FileLinesQuery; | ||
160 | } | ||
161 | impl db::HirDatabase { | ||
162 | fn module_tree() for db::ModuleTreeQuery; | ||
163 | fn fn_scopes() for db::FnScopesQuery; | ||
164 | fn file_items() for db::SourceFileItemsQuery; | ||
165 | fn file_item() for db::FileItemQuery; | ||
166 | fn input_module_items() for db::InputModuleItemsQuery; | ||
167 | fn item_map() for db::ItemMapQuery; | ||
168 | fn fn_syntax() for db::FnSyntaxQuery; | ||
169 | fn submodules() for db::SubmodulesQuery; | ||
170 | } | ||
171 | } | ||
172 | } | ||
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_hir/src/module/imp.rs index 80892acb7..76ea129a7 100644 --- a/crates/ra_analysis/src/descriptors/module/imp.rs +++ b/crates/ra_hir/src/module/imp.rs | |||
@@ -6,21 +6,19 @@ use ra_syntax::{ | |||
6 | }; | 6 | }; |
7 | use relative_path::RelativePathBuf; | 7 | use relative_path::RelativePathBuf; |
8 | use rustc_hash::{FxHashMap, FxHashSet}; | 8 | use rustc_hash::{FxHashMap, FxHashSet}; |
9 | use ra_db::{SourceRoot, SourceRootId, FileResolverImp, Cancelable, FileId,}; | ||
9 | 10 | ||
10 | use crate::{ | 11 | use crate::{ |
11 | db, | 12 | HirDatabase, |
12 | descriptors::DescriptorDatabase, | ||
13 | input::{SourceRoot, SourceRootId}, | ||
14 | Cancelable, FileId, FileResolverImp, | ||
15 | }; | 13 | }; |
16 | 14 | ||
17 | use super::{ | 15 | use super::{ |
18 | LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode, | 16 | LinkData, LinkId, ModuleData, ModuleId, ModuleSource, |
19 | ModuleTree, Problem, | 17 | ModuleTree, Problem, |
20 | }; | 18 | }; |
21 | 19 | ||
22 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | 20 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] |
23 | pub(crate) enum Submodule { | 21 | pub enum Submodule { |
24 | Declaration(SmolStr), | 22 | Declaration(SmolStr), |
25 | Definition(SmolStr, ModuleSource), | 23 | Definition(SmolStr, ModuleSource), |
26 | } | 24 | } |
@@ -34,39 +32,6 @@ impl Submodule { | |||
34 | } | 32 | } |
35 | } | 33 | } |
36 | 34 | ||
37 | pub(crate) fn submodules( | ||
38 | db: &impl DescriptorDatabase, | ||
39 | source: ModuleSource, | ||
40 | ) -> Cancelable<Arc<Vec<Submodule>>> { | ||
41 | db::check_canceled(db)?; | ||
42 | let file_id = source.file_id(); | ||
43 | let submodules = match source.resolve(db) { | ||
44 | ModuleSourceNode::SourceFile(it) => collect_submodules(file_id, it.borrowed()), | ||
45 | ModuleSourceNode::Module(it) => it | ||
46 | .borrowed() | ||
47 | .item_list() | ||
48 | .map(|it| collect_submodules(file_id, it)) | ||
49 | .unwrap_or_else(Vec::new), | ||
50 | }; | ||
51 | return Ok(Arc::new(submodules)); | ||
52 | |||
53 | fn collect_submodules<'a>( | ||
54 | file_id: FileId, | ||
55 | root: impl ast::ModuleItemOwner<'a>, | ||
56 | ) -> Vec<Submodule> { | ||
57 | modules(root) | ||
58 | .map(|(name, m)| { | ||
59 | if m.has_semi() { | ||
60 | Submodule::Declaration(name) | ||
61 | } else { | ||
62 | let src = ModuleSource::new_inline(file_id, m); | ||
63 | Submodule::Definition(name, src) | ||
64 | } | ||
65 | }) | ||
66 | .collect() | ||
67 | } | ||
68 | } | ||
69 | |||
70 | pub(crate) fn modules<'a>( | 35 | pub(crate) fn modules<'a>( |
71 | root: impl ast::ModuleItemOwner<'a>, | 36 | root: impl ast::ModuleItemOwner<'a>, |
72 | ) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { | 37 | ) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { |
@@ -82,16 +47,16 @@ pub(crate) fn modules<'a>( | |||
82 | } | 47 | } |
83 | 48 | ||
84 | pub(crate) fn module_tree( | 49 | pub(crate) fn module_tree( |
85 | db: &impl DescriptorDatabase, | 50 | db: &impl HirDatabase, |
86 | source_root: SourceRootId, | 51 | source_root: SourceRootId, |
87 | ) -> Cancelable<Arc<ModuleTree>> { | 52 | ) -> Cancelable<Arc<ModuleTree>> { |
88 | db::check_canceled(db)?; | 53 | db.check_canceled()?; |
89 | let res = create_module_tree(db, source_root)?; | 54 | let res = create_module_tree(db, source_root)?; |
90 | Ok(Arc::new(res)) | 55 | Ok(Arc::new(res)) |
91 | } | 56 | } |
92 | 57 | ||
93 | fn create_module_tree<'a>( | 58 | fn create_module_tree<'a>( |
94 | db: &impl DescriptorDatabase, | 59 | db: &impl HirDatabase, |
95 | source_root: SourceRootId, | 60 | source_root: SourceRootId, |
96 | ) -> Cancelable<ModuleTree> { | 61 | ) -> Cancelable<ModuleTree> { |
97 | let mut tree = ModuleTree::default(); | 62 | let mut tree = ModuleTree::default(); |
@@ -121,7 +86,7 @@ fn create_module_tree<'a>( | |||
121 | } | 86 | } |
122 | 87 | ||
123 | fn build_subtree( | 88 | fn build_subtree( |
124 | db: &impl DescriptorDatabase, | 89 | db: &impl HirDatabase, |
125 | source_root: &SourceRoot, | 90 | source_root: &SourceRoot, |
126 | tree: &mut ModuleTree, | 91 | tree: &mut ModuleTree, |
127 | visited: &mut FxHashSet<ModuleSource>, | 92 | visited: &mut FxHashSet<ModuleSource>, |
@@ -135,7 +100,7 @@ fn build_subtree( | |||
135 | parent, | 100 | parent, |
136 | children: Vec::new(), | 101 | children: Vec::new(), |
137 | }); | 102 | }); |
138 | for sub in db._submodules(source)?.iter() { | 103 | for sub in db.submodules(source)?.iter() { |
139 | let link = tree.push_link(LinkData { | 104 | let link = tree.push_link(LinkData { |
140 | name: sub.name().clone(), | 105 | name: sub.name().clone(), |
141 | owner: id, | 106 | owner: id, |
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_hir/src/module/mod.rs index 54ff95b66..3ae83d8cb 100644 --- a/crates/ra_analysis/src/descriptors/module/mod.rs +++ b/crates/ra_hir/src/module/mod.rs | |||
@@ -10,64 +10,63 @@ use ra_syntax::{ | |||
10 | ast::{self, AstNode, NameOwner}, | 10 | ast::{self, AstNode, NameOwner}, |
11 | SmolStr, SyntaxNode, | 11 | SmolStr, SyntaxNode, |
12 | }; | 12 | }; |
13 | use ra_db::{SourceRootId, FileId, FilePosition, Cancelable}; | ||
13 | use relative_path::RelativePathBuf; | 14 | use relative_path::RelativePathBuf; |
14 | 15 | ||
15 | use crate::{ | 16 | use crate::{ |
16 | db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, | 17 | DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, |
17 | descriptors::{Path, PathKind, DescriptorDatabase}, | ||
18 | input::SourceRootId, | ||
19 | arena::{Arena, Id}, | 18 | arena::{Arena, Id}, |
20 | }; | 19 | }; |
21 | 20 | ||
22 | pub(crate) use self::nameres::ModuleScope; | 21 | pub use self::nameres::ModuleScope; |
23 | 22 | ||
24 | /// `ModuleDescriptor` is API entry point to get all the information | 23 | /// `Module` is API entry point to get all the information |
25 | /// about a particular module. | 24 | /// about a particular module. |
26 | #[derive(Debug, Clone)] | 25 | #[derive(Debug, Clone)] |
27 | pub(crate) struct ModuleDescriptor { | 26 | pub struct Module { |
28 | tree: Arc<ModuleTree>, | 27 | tree: Arc<ModuleTree>, |
29 | source_root_id: SourceRootId, | 28 | source_root_id: SourceRootId, |
30 | module_id: ModuleId, | 29 | module_id: ModuleId, |
31 | } | 30 | } |
32 | 31 | ||
33 | impl ModuleDescriptor { | 32 | impl Module { |
34 | /// Lookup `ModuleDescriptor` by `FileId`. Note that this is inherently | 33 | /// Lookup `Module` by `FileId`. Note that this is inherently |
35 | /// lossy transformation: in general, a single source might correspond to | 34 | /// lossy transformation: in general, a single source might correspond to |
36 | /// several modules. | 35 | /// several modules. |
37 | pub fn guess_from_file_id( | 36 | pub fn guess_from_file_id( |
38 | db: &impl DescriptorDatabase, | 37 | db: &impl HirDatabase, |
39 | file_id: FileId, |