diff options
Diffstat (limited to 'crates')
41 files changed, 1680 insertions, 1223 deletions
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, | 38 | file_id: FileId, |
40 | ) -> Cancelable<Option<ModuleDescriptor>> { | 39 | ) -> Cancelable<Option<Module>> { |
41 | ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id)) | 40 | Module::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id)) |
42 | } | 41 | } |
43 | 42 | ||
44 | /// Lookup `ModuleDescriptor` by position in the source code. Note that this | 43 | /// Lookup `Module` by position in the source code. Note that this |
45 | /// is inherently lossy transformation: in general, a single source might | 44 | /// is inherently lossy transformation: in general, a single source might |
46 | /// correspond to several modules. | 45 | /// correspond to several modules. |
47 | pub fn guess_from_position( | 46 | pub fn guess_from_position( |
48 | db: &impl DescriptorDatabase, | 47 | db: &impl HirDatabase, |
49 | position: FilePosition, | 48 | position: FilePosition, |
50 | ) -> Cancelable<Option<ModuleDescriptor>> { | 49 | ) -> Cancelable<Option<Module>> { |
51 | let file = db.file_syntax(position.file_id); | 50 | let file = db.source_file(position.file_id); |
52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) | 51 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) |
53 | { | 52 | { |
54 | Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), | 53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), |
55 | _ => ModuleSource::SourceFile(position.file_id), | 54 | _ => ModuleSource::SourceFile(position.file_id), |
56 | }; | 55 | }; |
57 | ModuleDescriptor::guess_from_source(db, position.file_id, module_source) | 56 | Module::guess_from_source(db, position.file_id, module_source) |
58 | } | 57 | } |
59 | 58 | ||
60 | fn guess_from_source( | 59 | fn guess_from_source( |
61 | db: &impl DescriptorDatabase, | 60 | db: &impl HirDatabase, |
62 | file_id: FileId, | 61 | file_id: FileId, |
63 | module_source: ModuleSource, | 62 | module_source: ModuleSource, |
64 | ) -> Cancelable<Option<ModuleDescriptor>> { | 63 | ) -> Cancelable<Option<Module>> { |
65 | let source_root_id = db.file_source_root(file_id); | 64 | let source_root_id = db.file_source_root(file_id); |
66 | let module_tree = db._module_tree(source_root_id)?; | 65 | let module_tree = db.module_tree(source_root_id)?; |
67 | 66 | ||
68 | let res = match module_tree.any_module_for_source(module_source) { | 67 | let res = match module_tree.any_module_for_source(module_source) { |
69 | None => None, | 68 | None => None, |
70 | Some(module_id) => Some(ModuleDescriptor { | 69 | Some(module_id) => Some(Module { |
71 | tree: module_tree, | 70 | tree: module_tree, |
72 | source_root_id, | 71 | source_root_id, |
73 | module_id, | 72 | module_id, |
@@ -76,12 +75,23 @@ impl ModuleDescriptor { | |||
76 | Ok(res) | 75 | Ok(res) |
77 | } | 76 | } |
78 | 77 | ||
78 | pub(super) fn new( | ||
79 | db: &impl HirDatabase, | ||
80 | source_root_id: SourceRootId, | ||
81 | module_id: ModuleId, | ||
82 | ) -> Cancelable<Module> { | ||
83 | let module_tree = db.module_tree(source_root_id)?; | ||
84 | let res = Module { | ||
85 | tree: module_tree, | ||
86 | source_root_id, | ||
87 | module_id, | ||
88 | }; | ||
89 | Ok(res) | ||
90 | } | ||
91 | |||
79 | /// Returns `mod foo;` or `mod foo {}` node whihc declared this module. | 92 | /// Returns `mod foo;` or `mod foo {}` node whihc declared this module. |
80 | /// Returns `None` for the root module | 93 | /// Returns `None` for the root module |
81 | pub fn parent_link_source( | 94 | pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { |
82 | &self, | ||
83 | db: &impl DescriptorDatabase, | ||
84 | ) -> Option<(FileId, ast::ModuleNode)> { | ||
85 | let link = self.module_id.parent_link(&self.tree)?; | 95 | let link = self.module_id.parent_link(&self.tree)?; |
86 | let file_id = link.owner(&self.tree).source(&self.tree).file_id(); | 96 | let file_id = link.owner(&self.tree).source(&self.tree).file_id(); |
87 | let src = link.bind_source(&self.tree, db); | 97 | let src = link.bind_source(&self.tree, db); |
@@ -93,60 +103,74 @@ impl ModuleDescriptor { | |||
93 | } | 103 | } |
94 | 104 | ||
95 | /// Parent module. Returns `None` if this is a root module. | 105 | /// Parent module. Returns `None` if this is a root module. |
96 | pub fn parent(&self) -> Option<ModuleDescriptor> { | 106 | pub fn parent(&self) -> Option<Module> { |
97 | let parent_id = self.module_id.parent(&self.tree)?; | 107 | let parent_id = self.module_id.parent(&self.tree)?; |
98 | Some(ModuleDescriptor { | 108 | Some(Module { |
99 | module_id: parent_id, | 109 | module_id: parent_id, |
100 | ..self.clone() | 110 | ..self.clone() |
101 | }) | 111 | }) |
102 | } | 112 | } |
103 | 113 | ||
104 | /// The root of the tree this module is part of | 114 | /// The root of the tree this module is part of |
105 | pub fn crate_root(&self) -> ModuleDescriptor { | 115 | pub fn crate_root(&self) -> Module { |
106 | let root_id = self.module_id.crate_root(&self.tree); | 116 | let root_id = self.module_id.crate_root(&self.tree); |
107 | ModuleDescriptor { | 117 | Module { |
108 | module_id: root_id, | 118 | module_id: root_id, |
109 | ..self.clone() | 119 | ..self.clone() |
110 | } | 120 | } |
111 | } | 121 | } |
112 | 122 | ||
113 | /// `name` is `None` for the crate's root module | 123 | /// `name` is `None` for the crate's root module |
114 | #[allow(unused)] | ||
115 | pub fn name(&self) -> Option<SmolStr> { | 124 | pub fn name(&self) -> Option<SmolStr> { |
116 | let link = self.module_id.parent_link(&self.tree)?; | 125 | let link = self.module_id.parent_link(&self.tree)?; |
117 | Some(link.name(&self.tree)) | 126 | Some(link.name(&self.tree)) |
118 | } | 127 | } |
119 | 128 | ||
129 | pub fn def_id(&self, db: &impl HirDatabase) -> DefId { | ||
130 | let def_loc = DefLoc::Module { | ||
131 | id: self.module_id, | ||
132 | source_root: self.source_root_id, | ||
133 | }; | ||
134 | def_loc.id(db) | ||
135 | } | ||
136 | |||
120 | /// Finds a child module with the specified name. | 137 | /// Finds a child module with the specified name. |
121 | pub fn child(&self, name: &str) -> Option<ModuleDescriptor> { | 138 | pub fn child(&self, name: &str) -> Option<Module> { |
122 | let child_id = self.module_id.child(&self.tree, name)?; | 139 | let child_id = self.module_id.child(&self.tree, name)?; |
123 | Some(ModuleDescriptor { | 140 | Some(Module { |
124 | module_id: child_id, | 141 | module_id: child_id, |
125 | ..self.clone() | 142 | ..self.clone() |
126 | }) | 143 | }) |
127 | } | 144 | } |
128 | 145 | ||
129 | /// Returns a `ModuleScope`: a set of items, visible in this module. | 146 | /// Returns a `ModuleScope`: a set of items, visible in this module. |
130 | pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> { | 147 | pub fn scope(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> { |
131 | let item_map = db._item_map(self.source_root_id)?; | 148 | let item_map = db.item_map(self.source_root_id)?; |
132 | let res = item_map.per_module[&self.module_id].clone(); | 149 | let res = item_map.per_module[&self.module_id].clone(); |
133 | Ok(res) | 150 | Ok(res) |
134 | } | 151 | } |
135 | 152 | ||
136 | pub(crate) fn resolve_path(&self, path: Path) -> Option<ModuleDescriptor> { | 153 | pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable<Option<DefId>> { |
137 | let mut curr = match path.kind { | 154 | let mut curr = match path.kind { |
138 | PathKind::Crate => self.crate_root(), | 155 | PathKind::Crate => self.crate_root(), |
139 | PathKind::Self_ | PathKind::Plain => self.clone(), | 156 | PathKind::Self_ | PathKind::Plain => self.clone(), |
140 | PathKind::Super => self.parent()?, | 157 | PathKind::Super => ctry!(self.parent()), |
141 | }; | 158 | } |
159 | .def_id(db); | ||
160 | |||
142 | let segments = path.segments; | 161 | let segments = path.segments; |
143 | for name in segments { | 162 | for name in segments.iter() { |
144 | curr = curr.child(&name)?; | 163 | let module = match curr.loc(db) { |
164 | DefLoc::Module { id, source_root } => Module::new(db, source_root, id)?, | ||
165 | _ => return Ok(None), | ||
166 | }; | ||
167 | let scope = module.scope(db)?; | ||
168 | curr = ctry!(ctry!(scope.get(&name)).def_id); | ||
145 | } | 169 | } |
146 | Some(curr) | 170 | Ok(Some(curr)) |
147 | } | 171 | } |
148 | 172 | ||
149 | pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { | 173 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
150 | self.module_id.problems(&self.tree, db) | 174 | self.module_id.problems(&self.tree, db) |
151 | } | 175 | } |
152 | } | 176 | } |
@@ -159,19 +183,19 @@ impl ModuleDescriptor { | |||
159 | /// (which can have multiple parents) to the precise world of modules (which | 183 | /// (which can have multiple parents) to the precise world of modules (which |
160 | /// always have one parent). | 184 | /// always have one parent). |
161 | #[derive(Default, Debug, PartialEq, Eq)] | 185 | #[derive(Default, Debug, PartialEq, Eq)] |
162 | pub(crate) struct ModuleTree { | 186 | pub struct ModuleTree { |
163 | mods: Arena<ModuleData>, | 187 | mods: Arena<ModuleData>, |
164 | links: Arena<LinkData>, | 188 | links: Arena<LinkData>, |
165 | } | 189 | } |
166 | 190 | ||
167 | impl ModuleTree { | 191 | impl ModuleTree { |
168 | fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { | 192 | pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { |
169 | self.mods.keys() | 193 | self.mods.iter().map(|(id, _)| id) |
170 | } | 194 | } |
171 | 195 | ||
172 | fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { | 196 | fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> { |
173 | self.mods | 197 | self.mods |
174 | .items() | 198 | .iter() |
175 | .filter(|(_idx, it)| it.source == source) | 199 | .filter(|(_idx, it)| it.source == source) |
176 | .map(|(idx, _)| idx) | 200 | .map(|(idx, _)| idx) |
177 | .collect() | 201 | .collect() |
@@ -185,20 +209,19 @@ impl ModuleTree { | |||
185 | /// `ModuleSource` is the syntax tree element that produced this module: | 209 | /// `ModuleSource` is the syntax tree element that produced this module: |
186 | /// either a file, or an inlinde module. | 210 | /// either a file, or an inlinde module. |
187 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 211 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
188 | pub(crate) enum ModuleSource { | 212 | pub enum ModuleSource { |
189 | SourceFile(FileId), | 213 | SourceFile(FileId), |
190 | #[allow(dead_code)] | 214 | Module(SourceItemId), |
191 | Module(SyntaxPtr), | ||
192 | } | 215 | } |
193 | 216 | ||
194 | /// An owned syntax node for a module. Unlike `ModuleSource`, | 217 | /// An owned syntax node for a module. Unlike `ModuleSource`, |
195 | /// this holds onto the AST for the whole file. | 218 | /// this holds onto the AST for the whole file. |
196 | enum ModuleSourceNode { | 219 | pub(crate) enum ModuleSourceNode { |
197 | SourceFile(ast::SourceFileNode), | 220 | SourceFile(ast::SourceFileNode), |
198 | Module(ast::ModuleNode), | 221 | Module(ast::ModuleNode), |
199 | } | 222 | } |
200 | 223 | ||
201 | pub(crate) type ModuleId = Id<ModuleData>; | 224 | pub type ModuleId = Id<ModuleData>; |
202 | type LinkId = Id<LinkData>; | 225 | type LinkId = Id<LinkData>; |
203 | 226 | ||
204 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] | 227 | #[derive(Clone, Debug, Hash, PartialEq, Eq)] |
@@ -213,7 +236,7 @@ pub enum Problem { | |||
213 | } | 236 | } |
214 | 237 | ||
215 | impl ModuleId { | 238 | impl ModuleId { |
216 | fn source(self, tree: &ModuleTree) -> ModuleSource { | 239 | pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource { |
217 | tree.mods[self].source | 240 | tree.mods[self].source |
218 | } | 241 | } |
219 | fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { | 242 | fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> { |
@@ -243,7 +266,7 @@ impl ModuleId { | |||
243 | Some((link.name.clone(), module)) | 266 | Some((link.name.clone(), module)) |
244 | }) | 267 | }) |
245 | } | 268 | } |
246 | fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> { | 269 | fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
247 | tree.mods[self] | 270 | tree.mods[self] |
248 | .children | 271 | .children |
249 | .iter() | 272 | .iter() |
@@ -264,7 +287,7 @@ impl LinkId { | |||
264 | fn name(self, tree: &ModuleTree) -> SmolStr { | 287 | fn name(self, tree: &ModuleTree) -> SmolStr { |
265 | tree.links[self].name.clone() | 288 | tree.links[self].name.clone() |
266 | } | 289 | } |
267 | fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode { | 290 | fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode { |
268 | let owner = self.owner(tree); | 291 | let owner = self.owner(tree); |
269 | match owner.source(tree).resolve(db) { | 292 | match owner.source(tree).resolve(db) { |
270 | ModuleSourceNode::SourceFile(root) => { | 293 | ModuleSourceNode::SourceFile(root) => { |
@@ -280,41 +303,47 @@ impl LinkId { | |||
280 | } | 303 | } |
281 | 304 | ||
282 | #[derive(Debug, PartialEq, Eq, Hash)] | 305 | #[derive(Debug, PartialEq, Eq, Hash)] |
283 | pub(crate) struct ModuleData { | 306 | pub struct ModuleData { |
284 | source: ModuleSource, | 307 | source: ModuleSource, |
285 | parent: Option<LinkId>, | 308 | parent: Option<LinkId>, |
286 | children: Vec<LinkId>, | 309 | children: Vec<LinkId>, |
287 | } | 310 | } |
288 | 311 | ||
289 | impl ModuleSource { | 312 | impl ModuleSource { |
290 | fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource { | 313 | pub(crate) fn new_inline( |
314 | db: &impl HirDatabase, | ||
315 | file_id: FileId, | ||
316 | module: ast::Module, | ||
317 | ) -> ModuleSource { | ||
291 | assert!(!module.has_semi()); | 318 | assert!(!module.has_semi()); |
292 | let ptr = SyntaxPtr::new(file_id, module.syntax()); | 319 | let items = db.file_items(file_id); |
293 | ModuleSource::Module(ptr) | 320 | let item_id = items.id_of(module.syntax()); |
321 | let id = SourceItemId { file_id, item_id }; | ||
322 | ModuleSource::Module(id) | ||
294 | } | 323 | } |
295 | 324 | ||
296 | pub(crate) fn as_file(self) -> Option<FileId> { | 325 | pub fn as_file(self) -> Option<FileId> { |
297 | match self { | 326 | match self { |
298 | ModuleSource::SourceFile(f) => Some(f), | 327 | ModuleSource::SourceFile(f) => Some(f), |
299 | ModuleSource::Module(..) => None, | 328 | ModuleSource::Module(..) => None, |
300 | } | 329 | } |
301 | } | 330 | } |
302 | 331 | ||
303 | pub(crate) fn file_id(self) -> FileId { | 332 | pub fn file_id(self) -> FileId { |
304 | match self { | 333 | match self { |
305 | ModuleSource::SourceFile(f) => f, | 334 | ModuleSource::SourceFile(f) => f, |
306 | ModuleSource::Module(ptr) => ptr.file_id(), | 335 | ModuleSource::Module(source_item_id) => source_item_id.file_id, |
307 | } | 336 | } |
308 | } | 337 | } |
309 | 338 | ||
310 | fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode { | 339 | pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode { |
311 | match self { | 340 | match self { |
312 | ModuleSource::SourceFile(file_id) => { | 341 | ModuleSource::SourceFile(file_id) => { |
313 | let syntax = db.file_syntax(file_id); | 342 | let syntax = db.source_file(file_id); |
314 | ModuleSourceNode::SourceFile(syntax.ast().owned()) | 343 | ModuleSourceNode::SourceFile(syntax.ast().owned()) |
315 | } | 344 | } |
316 | ModuleSource::Module(ptr) => { | 345 | ModuleSource::Module(item_id) => { |
317 | let syntax = db.resolve_syntax_ptr(ptr); | 346 | let syntax = db.file_item(item_id); |
318 | let syntax = syntax.borrowed(); | 347 | let syntax = syntax.borrowed(); |
319 | let module = ast::Module::cast(syntax).unwrap(); | 348 | let module = ast::Module::cast(syntax).unwrap(); |
320 | ModuleSourceNode::Module(module.owned()) | 349 | ModuleSourceNode::Module(module.owned()) |
@@ -333,11 +362,11 @@ struct LinkData { | |||
333 | 362 | ||
334 | impl ModuleTree { | 363 | impl ModuleTree { |
335 | fn push_mod(&mut self, data: ModuleData) -> ModuleId { | 364 | fn push_mod(&mut self, data: ModuleData) -> ModuleId { |
336 | self.mods.push(data) | 365 | self.mods.alloc(data) |
337 | } | 366 | } |
338 | fn push_link(&mut self, data: LinkData) -> LinkId { | 367 | fn push_link(&mut self, data: LinkData) -> LinkId { |
339 | let owner = data.owner; | 368 | let owner = data.owner; |
340 | let id = self.links.push(data); | 369 | let id = self.links.alloc(data); |
341 | self.mods[owner].children.push(id); | 370 | self.mods[owner].children.push(id); |
342 | id | 371 | id |
343 | } | 372 | } |
diff --git a/crates/ra_analysis/src/descriptors/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 648ec5e43..8529e16b3 100644 --- a/crates/ra_analysis/src/descriptors/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -16,38 +16,44 @@ | |||
16 | //! structure itself is modified. | 16 | //! structure itself is modified. |
17 | use std::{ | 17 | use std::{ |
18 | sync::Arc, | 18 | sync::Arc, |
19 | time::Instant, | ||
20 | }; | 19 | }; |
21 | 20 | ||
22 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
23 | |||
24 | use ra_syntax::{ | 22 | use ra_syntax::{ |
23 | TextRange, | ||
25 | SmolStr, SyntaxKind::{self, *}, | 24 | SmolStr, SyntaxKind::{self, *}, |
26 | ast::{self, ModuleItemOwner} | 25 | ast::{self, AstNode} |
27 | }; | 26 | }; |
27 | use ra_db::SourceRootId; | ||
28 | 28 | ||
29 | use crate::{ | 29 | use crate::{ |
30 | Cancelable, | 30 | Cancelable, FileId, |
31 | loc2id::{DefId, DefLoc}, | 31 | DefId, DefLoc, |
32 | descriptors::{ | 32 | SourceItemId, SourceFileItemId, SourceFileItems, |
33 | Path, PathKind, | 33 | Path, PathKind, |
34 | DescriptorDatabase, | 34 | HirDatabase, |
35 | module::{ModuleId, ModuleTree, ModuleSourceNode}, | 35 | module::{ModuleId, ModuleTree}, |
36 | }, | ||
37 | syntax_ptr::{LocalSyntaxPtr}, | ||
38 | input::SourceRootId, | ||
39 | }; | 36 | }; |
40 | 37 | ||
41 | /// Item map is the result of the name resolution. Item map contains, for each | 38 | /// Item map is the result of the name resolution. Item map contains, for each |
42 | /// module, the set of visible items. | 39 | /// module, the set of visible items. |
43 | #[derive(Default, Debug, PartialEq, Eq)] | 40 | #[derive(Default, Debug, PartialEq, Eq)] |
44 | pub(crate) struct ItemMap { | 41 | pub struct ItemMap { |
45 | pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>, | 42 | pub per_module: FxHashMap<ModuleId, ModuleScope>, |
46 | } | 43 | } |
47 | 44 | ||
48 | #[derive(Debug, Default, PartialEq, Eq, Clone)] | 45 | #[derive(Debug, Default, PartialEq, Eq, Clone)] |
49 | pub(crate) struct ModuleScope { | 46 | pub struct ModuleScope { |
50 | pub(crate) items: FxHashMap<SmolStr, Resolution>, | 47 | items: FxHashMap<SmolStr, Resolution>, |
48 | } | ||
49 | |||
50 | impl ModuleScope { | ||
51 | pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &Resolution)> + 'a { | ||
52 | self.items.iter() | ||
53 | } | ||
54 | pub fn get(&self, name: &SmolStr) -> Option<&Resolution> { | ||
55 | self.items.get(name) | ||
56 | } | ||
51 | } | 57 | } |
52 | 58 | ||
53 | /// A set of items and imports declared inside a module, without relation to | 59 | /// A set of items and imports declared inside a module, without relation to |
@@ -57,85 +63,63 @@ pub(crate) struct ModuleScope { | |||
57 | /// recomputing name res: if `InputModuleItems` are the same, we can avoid | 63 | /// recomputing name res: if `InputModuleItems` are the same, we can avoid |
58 | /// running name resolution. | 64 | /// running name resolution. |
59 | #[derive(Debug, Default, PartialEq, Eq)] | 65 | #[derive(Debug, Default, PartialEq, Eq)] |
60 | pub(crate) struct InputModuleItems { | 66 | pub struct InputModuleItems { |
61 | items: Vec<ModuleItem>, | 67 | items: Vec<ModuleItem>, |
62 | imports: Vec<Import>, | 68 | imports: Vec<Import>, |
63 | } | 69 | } |
64 | 70 | ||
71 | #[derive(Debug, PartialEq, Eq)] | ||
72 | struct ModuleItem { | ||
73 | id: SourceFileItemId, | ||
74 | name: SmolStr, | ||
75 | kind: SyntaxKind, | ||
76 | vis: Vis, | ||
77 | } | ||
78 | |||
79 | #[derive(Debug, PartialEq, Eq)] | ||
80 | enum Vis { | ||
81 | // Priv, | ||
82 | Other, | ||
83 | } | ||
84 | |||
65 | #[derive(Debug, Clone, PartialEq, Eq)] | 85 | #[derive(Debug, Clone, PartialEq, Eq)] |
66 | struct Import { | 86 | struct Import { |
67 | path: Path, | 87 | path: Path, |
68 | kind: ImportKind, | 88 | kind: ImportKind, |
69 | } | 89 | } |
70 | 90 | ||
71 | #[derive(Debug, Clone, PartialEq, Eq)] | 91 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
72 | enum ImportKind { | 92 | pub struct NamedImport { |
73 | Glob, | 93 | pub file_item_id: SourceFileItemId, |
74 | // TODO: make offset independent | 94 | pub relative_range: TextRange, |
75 | Named(LocalSyntaxPtr), | ||
76 | } | 95 | } |
77 | 96 | ||
78 | pub(crate) fn input_module_items( | 97 | impl NamedImport { |
79 | db: &impl DescriptorDatabase, | 98 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { |
80 | source_root: SourceRootId, | 99 | let source_item_id = SourceItemId { |
81 | module_id: ModuleId, | 100 | file_id, |
82 | ) -> Cancelable<Arc<InputModuleItems>> { | 101 | item_id: self.file_item_id, |
83 | let module_tree = db._module_tree(source_root)?; | 102 | }; |
84 | let source = module_id.source(&module_tree); | 103 | let syntax = db.file_item(source_item_id); |
85 | let res = match source.resolve(db) { | 104 | let offset = syntax.borrowed().range().start(); |
86 | ModuleSourceNode::SourceFile(it) => { | 105 | self.relative_range + offset |
87 | let items = it.borrowed().items(); | 106 | } |
88 | InputModuleItems::new(items) | ||
89 | } | ||
90 | ModuleSourceNode::Module(it) => { | ||
91 | let items = it | ||
92 | .borrowed() | ||
93 | .item_list() | ||
94 | .into_iter() | ||
95 | .flat_map(|it| it.items()); | ||
96 | InputModuleItems::new(items) | ||
97 | } | ||
98 | }; | ||
99 | Ok(Arc::new(res)) | ||
100 | } | 107 | } |
101 | 108 | ||
102 | pub(crate) fn item_map( | 109 | #[derive(Debug, Clone, PartialEq, Eq)] |
103 | db: &impl DescriptorDatabase, | 110 | enum ImportKind { |
104 | source_root: SourceRootId, | 111 | Glob, |
105 | ) -> Cancelable<Arc<ItemMap>> { | 112 | Named(NamedImport), |
106 | let start = Instant::now(); | ||
107 | let module_tree = db._module_tree(source_root)?; | ||
108 | let input = module_tree | ||
109 | .modules() | ||
110 | .map(|id| { | ||
111 | let items = db._input_module_items(source_root, id)?; | ||
112 | Ok((id, items)) | ||
113 | }) | ||
114 | .collect::<Cancelable<FxHashMap<_, _>>>()?; | ||
115 | |||
116 | let mut resolver = Resolver { | ||
117 | db: db, | ||
118 | input: &input, | ||
119 | source_root, | ||
120 | module_tree, | ||
121 | result: ItemMap::default(), | ||
122 | }; | ||
123 | resolver.resolve()?; | ||
124 | let res = resolver.result; | ||
125 | let elapsed = start.elapsed(); | ||
126 | log::info!("item_map: {:?}", elapsed); | ||
127 | Ok(Arc::new(res)) | ||
128 | } | 113 | } |
129 | 114 | ||
130 | /// Resolution is basically `DefId` atm, but it should account for stuff like | 115 | /// Resolution is basically `DefId` atm, but it should account for stuff like |
131 | /// multiple namespaces, ambiguity and errors. | 116 | /// multiple namespaces, ambiguity and errors. |
132 | #[derive(Debug, Clone, PartialEq, Eq)] | 117 | #[derive(Debug, Clone, PartialEq, Eq)] |
133 | pub(crate) struct Resolution { | 118 | pub struct Resolution { |
134 | /// None for unresolved | 119 | /// None for unresolved |
135 | pub(crate) def_id: Option<DefId>, | 120 | pub def_id: Option<DefId>, |
136 | /// ident by whitch this is imported into local scope. | 121 | /// ident by whitch this is imported into local scope. |
137 | /// TODO: make this offset-independent. | 122 | pub import: Option<NamedImport>, |
138 | pub(crate) import_name: Option<LocalSyntaxPtr>, | ||
139 | } | 123 | } |
140 | 124 | ||
141 | // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 125 | // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
@@ -150,55 +134,49 @@ pub(crate) struct Resolution { | |||
150 | // values: Option<T>, | 134 | // values: Option<T>, |
151 | // } | 135 | // } |
152 | 136 | ||
153 | #[derive(Debug, PartialEq, Eq)] | ||
154 | struct ModuleItem { | ||
155 | ptr: LocalSyntaxPtr, | ||
156 | name: SmolStr, | ||
157 | kind: SyntaxKind, | ||
158 | vis: Vis, | ||
159 | } | ||
160 | |||
161 | #[derive(Debug, PartialEq, Eq)] | ||
162 | enum Vis { | ||
163 | // Priv, | ||
164 | Other, | ||
165 | } | ||
166 | |||
167 | impl InputModuleItems { | 137 | impl InputModuleItems { |
168 | fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems { | 138 | pub(crate) fn new<'a>( |
139 | file_items: &SourceFileItems, | ||
140 | items: impl Iterator<Item = ast::ModuleItem<'a>>, | ||
141 | ) -> InputModuleItems { | ||
169 | let mut res = InputModuleItems::default(); | 142 | let mut res = InputModuleItems::default(); |
170 | for item in items { | 143 | for item in items { |
171 | res.add_item(item); | 144 | res.add_item(file_items, item); |
172 | } | 145 | } |
173 | res | 146 | res |
174 | } | 147 | } |
175 | 148 | ||
176 | fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> { | 149 | fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> { |
177 | match item { | 150 | match item { |
178 | ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?), | 151 | ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
179 | ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?), | 152 | ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
180 | ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?), | 153 | ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
181 | ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?), | 154 | ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
182 | ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?), | 155 | ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
183 | ast::ModuleItem::ImplItem(_) => { | 156 | ast::ModuleItem::ImplItem(_) => { |
184 | // impls don't define items | 157 | // impls don't define items |
185 | } | 158 | } |
186 | ast::ModuleItem::UseItem(it) => self.add_use_item(it), | 159 | ast::ModuleItem::UseItem(it) => self.add_use_item(file_items, it), |
187 | ast::ModuleItem::ExternCrateItem(_) => { | 160 | ast::ModuleItem::ExternCrateItem(_) => { |
188 | // TODO | 161 | // TODO |
189 | } | 162 | } |
190 | ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?), | 163 | ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
191 | ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?), | 164 | ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?), |
192 | ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?), | 165 | ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?), |
193 | } | 166 | } |
194 | Some(()) | 167 | Some(()) |
195 | } | 168 | } |
196 | 169 | ||
197 | fn add_use_item(&mut self, item: ast::UseItem) { | 170 | fn add_use_item(&mut self, file_items: &SourceFileItems, item: ast::UseItem) { |
198 | Path::expand_use_item(item, |path, ptr| { | 171 | let file_item_id = file_items.id_of(item.syntax()); |
199 | let kind = match ptr { | 172 | let start_offset = item.syntax().range().start(); |
173 | Path::expand_use_item(item, |path, range| { | ||
174 | let kind = match range { | ||
200 | None => ImportKind::Glob, | 175 | None => ImportKind::Glob, |
201 | Some(ptr) => ImportKind::Named(ptr), | 176 | Some(range) => ImportKind::Named(NamedImport { |
177 | file_item_id, | ||
178 | relative_range: range - start_offset, | ||
179 | }), | ||
202 | }; | 180 | }; |
203 | self.imports.push(Import { kind, path }) | 181 | self.imports.push(Import { kind, path }) |
204 | }) | 182 | }) |
@@ -206,13 +184,13 @@ impl InputModuleItems { | |||
206 | } | 184 | } |
207 | 185 | ||
208 | impl ModuleItem { | 186 | impl ModuleItem { |
209 | fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { | 187 | fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { |
210 | let name = item.name()?.text(); | 188 | let name = item.name()?.text(); |
211 | let ptr = LocalSyntaxPtr::new(item.syntax()); | ||
212 | let kind = item.syntax().kind(); | 189 | let kind = item.syntax().kind(); |
213 | let vis = Vis::Other; | 190 | let vis = Vis::Other; |
191 | let id = file_items.id_of(item.syntax()); | ||
214 | let res = ModuleItem { | 192 | let res = ModuleItem { |
215 | ptr, | 193 | id, |
216 | name, | 194 | name, |
217 | kind, | 195 | kind, |
218 | vis, | 196 | vis, |
@@ -221,28 +199,28 @@ impl ModuleItem { | |||
221 | } | 199 | } |
222 | } | 200 | } |
223 | 201 | ||
224 | struct Resolver<'a, DB> { | 202 | pub(crate) struct Resolver<'a, DB> { |
225 | db: &'a DB, | 203 | pub(crate) db: &'a DB, |
226 | input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, | 204 | pub(crate) input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>, |
227 | source_root: SourceRootId, | 205 | pub(crate) source_root: SourceRootId, |
228 | module_tree: Arc<ModuleTree>, | 206 | pub(crate) module_tree: Arc<ModuleTree>, |
229 | result: ItemMap, | 207 | pub(crate) result: ItemMap, |
230 | } | 208 | } |
231 | 209 | ||
232 | impl<'a, DB> Resolver<'a, DB> | 210 | impl<'a, DB> Resolver<'a, DB> |
233 | where | 211 | where |
234 | DB: DescriptorDatabase, | 212 | DB: HirDatabase, |
235 | { | 213 | { |
236 | fn resolve(&mut self) -> Cancelable<()> { | 214 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { |
237 | for (&module_id, items) in self.input.iter() { | 215 | for (&module_id, items) in self.input.iter() { |
238 | self.populate_module(module_id, items) | 216 | self.populate_module(module_id, items) |
239 | } | 217 | } |
240 | 218 | ||
241 | for &module_id in self.input.keys() { | 219 | for &module_id in self.input.keys() { |
242 | crate::db::check_canceled(self.db)?; | 220 | self.db.check_canceled()?; |
243 | self.resolve_imports(module_id); | 221 | self.resolve_imports(module_id); |
244 | } | 222 | } |
245 | Ok(()) | 223 | Ok(self.result) |
246 | } | 224 | } |
247 | 225 | ||
248 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { | 226 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { |
@@ -252,12 +230,12 @@ where | |||
252 | 230 | ||
253 | for import in input.imports.iter() { | 231 | for import in input.imports.iter() { |
254 | if let Some(name) = import.path.segments.iter().last() { | 232 | if let Some(name) = import.path.segments.iter().last() { |
255 | if let ImportKind::Named(ptr) = import.kind { | 233 | if let ImportKind::Named(import) = import.kind { |
256 | module_items.items.insert( | 234 | module_items.items.insert( |
257 | name.clone(), | 235 | name.clone(), |
258 | Resolution { | 236 | Resolution { |
259 | def_id: None, | 237 | def_id: None, |
260 | import_name: Some(ptr), | 238 | import: Some(import), |
261 | }, | 239 | }, |
262 | ); | 240 | ); |
263 | } | 241 | } |
@@ -269,12 +247,16 @@ where | |||
269 | // handle submodules separatelly | 247 | // handle submodules separatelly |
270 | continue; | 248 | continue; |
271 | } | 249 | } |
272 | let ptr = item.ptr.into_global(file_id); | 250 | let def_loc = DefLoc::Item { |
273 | let def_loc = DefLoc::Item { ptr }; | 251 | source_item_id: SourceItemId { |
274 | let def_id = self.db.id_maps().def_id(def_loc); | 252 | file_id, |
253 | item_id: item.id, | ||
254 | }, | ||
255 | }; | ||
256 | let def_id = def_loc.id(self.db); | ||
275 | let resolution = Resolution { | 257 | let resolution = Resolution { |
276 | def_id: Some(def_id), | 258 | def_id: Some(def_id), |
277 | import_name: None, | 259 | import: None, |
278 | }; | 260 | }; |
279 | module_items.items.insert(item.name.clone(), resolution); | 261 | module_items.items.insert(item.name.clone(), resolution); |
280 | } | 262 | } |
@@ -284,10 +266,10 @@ where | |||
284 | id: mod_id, | 266 | id: mod_id, |
285 | source_root: self.source_root, | 267 | source_root: self.source_root, |
286 | }; | 268 | }; |
287 | let def_id = self.db.id_maps().def_id(def_loc); | 269 | let def_id = def_loc.id(self.db); |
288 | let resolution = Resolution { | 270 | let resolution = Resolution { |
289 | def_id: Some(def_id), | 271 | def_id: Some(def_id), |
290 | import_name: None, | 272 | import: None, |
291 | }; | 273 | }; |
292 | module_items.items.insert(name, resolution); | 274 | module_items.items.insert(name, resolution); |
293 | } | 275 | } |
@@ -333,7 +315,7 @@ where | |||
333 | }; | 315 | }; |
334 | 316 | ||
335 | if !is_last { | 317 | if !is_last { |
336 | curr = match self.db.id_maps().def_loc(def_id) { | 318 | curr = match def_id.loc(self.db) { |
337 | DefLoc::Module { id, .. } => id, | 319 | DefLoc::Module { id, .. } => id, |
338 | _ => return, | 320 | _ => return, |
339 | } | 321 | } |
@@ -341,7 +323,7 @@ where | |||
341 | self.update(module_id, |items| { | 323 | self.update(module_id, |items| { |
342 | let res = Resolution { | 324 | let res = Resolution { |
343 | def_id: Some(def_id), | 325 | def_id: Some(def_id), |
344 | import_name: Some(ptr), | 326 | import: Some(ptr), |
345 | }; | 327 | }; |
346 | items.items.insert(name.clone(), res); | 328 | items.items.insert(name.clone(), res); |
347 | }) | 329 | }) |
@@ -357,23 +339,24 @@ where | |||
357 | 339 | ||
358 | #[cfg(test)] | 340 | #[cfg(test)] |
359 | mod tests { | 341 | mod tests { |
342 | use std::sync::Arc; | ||
343 | |||
344 | use salsa::Database; | ||
345 | use ra_db::FilesDatabase; | ||
346 | use ra_syntax::SmolStr; | ||
347 | |||
360 | use crate::{ | 348 | use crate::{ |
361 | AnalysisChange, | 349 | self as hir, |
362 | mock_analysis::{MockAnalysis, analysis_and_position}, | 350 | db::HirDatabase, |
363 | descriptors::{DescriptorDatabase, module::ModuleDescriptor}, | 351 | mock::MockDatabase, |
364 | input::FilesDatabase, | ||
365 | }; | 352 | }; |
366 | use super::*; | ||
367 | 353 | ||
368 | fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) { | 354 | fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) { |
369 | let (analysis, pos) = analysis_and_position(fixture); | 355 | let (db, pos) = MockDatabase::with_position(fixture); |
370 | let db = analysis.imp.db; | ||
371 | let source_root = db.file_source_root(pos.file_id); | 356 | let source_root = db.file_source_root(pos.file_id); |
372 | let descr = ModuleDescriptor::guess_from_position(&*db, pos) | 357 | let module = hir::Module::guess_from_position(&db, pos).unwrap().unwrap(); |
373 | .unwrap() | 358 | let module_id = module.module_id; |
374 | .unwrap(); | 359 | (db.item_map(source_root).unwrap(), module_id) |
375 | let module_id = descr.module_id; | ||
376 | (db._item_map(source_root).unwrap(), module_id) | ||
377 | } | 360 | } |
378 | 361 | ||
379 | #[test] | 362 | #[test] |
@@ -400,10 +383,10 @@ mod tests { | |||
400 | 383 | ||
401 | #[test] | 384 | #[test] |
402 | fn typing_inside_a_function_should_not_invalidate_item_map() { | 385 | fn typing_inside_a_function_should_not_invalidate_item_map() { |
403 | let mock_analysis = MockAnalysis::with_files( | 386 | let (mut db, pos) = MockDatabase::with_position( |
404 | " | 387 | " |
405 | //- /lib.rs | 388 | //- /lib.rs |
406 | mod foo; | 389 | mod foo;<|> |
407 | 390 | ||
408 | use crate::foo::bar::Baz; | 391 | use crate::foo::bar::Baz; |
409 | 392 | ||
@@ -417,45 +400,35 @@ mod tests { | |||
417 | pub struct Baz; | 400 | pub struct Baz; |
418 | ", | 401 | ", |
419 | ); | 402 | ); |
420 | 403 | let source_root = db.file_source_root(pos.file_id); | |
421 | let file_id = mock_analysis.id_of("/lib.rs"); | ||
422 | let mut host = mock_analysis.analysis_host(); | ||
423 | |||
424 | let source_root = host.analysis().imp.db.file_source_root(file_id); | ||
425 | |||
426 | { | 404 | { |
427 | let db = host.analysis().imp.db; | ||
428 | let events = db.log_executed(|| { | 405 | let events = db.log_executed(|| { |
429 | db._item_map(source_root).unwrap(); | 406 | db.item_map(source_root).unwrap(); |
430 | }); | 407 | }); |
431 | assert!(format!("{:?}", events).contains("_item_map")) | 408 | assert!(format!("{:?}", events).contains("item_map")) |
432 | } | 409 | } |
433 | 410 | ||
434 | let mut change = AnalysisChange::new(); | 411 | let new_text = " |
435 | |||
436 | change.change_file( | ||
437 | file_id, | ||
438 | " | ||
439 | mod foo; | 412 | mod foo; |
440 | 413 | ||
441 | use crate::foo::bar::Baz; | 414 | use crate::foo::bar::Baz; |
442 | 415 | ||
443 | fn foo() -> i32 { 92 } | 416 | fn foo() -> i32 { 92 } |
444 | " | 417 | " |
445 | .to_string(), | 418 | .to_string(); |
446 | ); | ||
447 | 419 | ||
448 | host.apply_change(change); | 420 | db.query_mut(ra_db::FileTextQuery) |
421 | .set(pos.file_id, Arc::new(new_text)); | ||
449 | 422 | ||
450 | { | 423 | { |
451 | let db = host.analysis().imp.db; | ||
452 | let events = db.log_executed(|| { | 424 | let events = db.log_executed(|| { |
453 | db._item_map(source_root).unwrap(); | 425 | db.item_map(source_root).unwrap(); |
454 | }); | 426 | }); |
455 | // assert!( | 427 | assert!( |
456 | // !format!("{:?}", events).contains("_item_map"), | 428 | !format!("{:?}", events).contains("_item_map"), |
457 | // "{:#?}", events | 429 | "{:#?}", |
458 | // ) | 430 | events |
431 | ) | ||
459 | } | 432 | } |
460 | } | 433 | } |
461 | } | 434 | } |
diff --git a/crates/ra_analysis/src/descriptors/path.rs b/crates/ra_hir/src/path.rs index 99fca18b1..4a2e427cd 100644 --- a/crates/ra_analysis/src/descriptors/path.rs +++ b/crates/ra_hir/src/path.rs | |||
@@ -1,15 +1,13 @@ | |||
1 | use ra_syntax::{SmolStr, ast, AstNode}; | 1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; |
2 | |||
3 | use crate::syntax_ptr::LocalSyntaxPtr; | ||
4 | 2 | ||
5 | #[derive(Debug, Clone, PartialEq, Eq)] | 3 | #[derive(Debug, Clone, PartialEq, Eq)] |
6 | pub(crate) struct Path { | 4 | pub struct Path { |
7 | pub(crate) kind: PathKind, | 5 | pub kind: PathKind, |
8 | pub(crate) segments: Vec<SmolStr>, | 6 | pub segments: Vec<SmolStr>, |
9 | } | 7 | } |
10 | 8 | ||
11 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
12 | pub(crate) enum PathKind { | 10 | pub enum PathKind { |
13 | Plain, | 11 | Plain, |
14 | Self_, | 12 | Self_, |
15 | Super, | 13 | Super, |
@@ -18,17 +16,14 @@ pub(crate) enum PathKind { | |||
18 | 16 | ||
19 | impl Path { | 17 | impl Path { |
20 | /// Calls `cb` with all paths, represented by this use item. | 18 | /// Calls `cb` with all paths, represented by this use item. |
21 | pub(crate) fn expand_use_item( | 19 | pub fn expand_use_item(item: ast::UseItem, mut cb: impl FnMut(Path, Option<TextRange>)) { |
22 | item: ast::UseItem, | ||
23 | mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>), | ||
24 | ) { | ||
25 | if let Some(tree) = item.use_tree() { | 20 | if let Some(tree) = item.use_tree() { |
26 | expand_use_tree(None, tree, &mut cb); | 21 | expand_use_tree(None, tree, &mut cb); |
27 | } | 22 | } |
28 | } | 23 | } |
29 | 24 | ||
30 | /// Converts an `ast::Path` to `Path`. Works with use trees. | 25 | /// Converts an `ast::Path` to `Path`. Works with use trees. |
31 | pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> { | 26 | pub fn from_ast(mut path: ast::Path) -> Option<Path> { |
32 | let mut kind = PathKind::Plain; | 27 | let mut kind = PathKind::Plain; |
33 | let mut segments = Vec::new(); | 28 | let mut segments = Vec::new(); |
34 | loop { | 29 | loop { |
@@ -69,7 +64,7 @@ impl Path { | |||
69 | } | 64 | } |
70 | 65 | ||
71 | /// `true` is this path is a single identifier, like `foo` | 66 | /// `true` is this path is a single identifier, like `foo` |
72 | pub(crate) fn is_ident(&self) -> bool { | 67 | pub fn is_ident(&self) -> bool { |
73 | self.kind == PathKind::Plain && self.segments.len() == 1 | 68 | self.kind == PathKind::Plain && self.segments.len() == 1 |
74 | } | 69 | } |
75 | } | 70 | } |
@@ -77,7 +72,7 @@ impl Path { | |||
77 | fn expand_use_tree( | 72 | fn expand_use_tree( |
78 | prefix: Option<Path>, | 73 | prefix: Option<Path>, |
79 | tree: ast::UseTree, | 74 | tree: ast::UseTree, |
80 | cb: &mut impl FnMut(Path, Option<LocalSyntaxPtr>), | 75 | cb: &mut impl FnMut(Path, Option<TextRange>), |
81 | ) { | 76 | ) { |
82 | if let Some(use_tree_list) = tree.use_tree_list() { | 77 | if let Some(use_tree_list) = tree.use_tree_list() { |
83 | let prefix = match tree.path() { | 78 | let prefix = match tree.path() { |
@@ -93,13 +88,13 @@ fn expand_use_tree( | |||
93 | } else { | 88 | } else { |
94 | if let Some(ast_path) = tree.path() { | 89 | if let Some(ast_path) = tree.path() { |
95 | if let Some(path) = convert_path(prefix, ast_path) { | 90 | if let Some(path) = convert_path(prefix, ast_path) { |
96 | let ptr = if tree.has_star() { | 91 | let range = if tree.has_star() { |
97 | None | 92 | None |
98 | } else { | 93 | } else { |
99 | let ptr = LocalSyntaxPtr::new(ast_path.segment().unwrap().syntax()); | 94 | let range = ast_path.segment().unwrap().syntax().range(); |
100 | Some(ptr) | 95 | Some(range) |
101 | }; | 96 | }; |
102 | cb(path, ptr) | 97 | cb(path, range) |
103 | } | 98 | } |
104 | } | 99 | } |
105 | } | 100 | } |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs new file mode 100644 index 000000000..6f602878c --- /dev/null +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -0,0 +1,154 @@ | |||
1 | use std::{ | ||
2 | sync::Arc, | ||
3 | time::Instant, | ||
4 | }; | ||
5 | |||
6 | use rustc_hash::FxHashMap; | ||
7 | use ra_syntax::{ | ||
8 | AstNode, SyntaxNode, SmolStr, | ||
9 | ast::{self, FnDef, FnDefNode, NameOwner, ModuleItemOwner} | ||
10 | }; | ||
11 | use ra_db::{SourceRootId, FileId, Cancelable,}; | ||
12 | |||
13 | use crate::{ | ||
14 | FnId, | ||
15 | SourceFileItems, SourceItemId, | ||
16 | db::HirDatabase, | ||
17 | function::FnScopes, | ||
18 | module::{ | ||
19 | ModuleSource, ModuleSourceNode, ModuleId, | ||
20 | imp::Submodule, | ||
21 | nameres::{InputModuleItems, ItemMap, Resolver}, | ||
22 | }, | ||
23 | }; | ||
24 | |||
25 | /// Resolve `FnId` to the corresponding `SyntaxNode` | ||
26 | pub(super) fn fn_syntax(db: &impl HirDatabase, fn_id: FnId) -> FnDefNode { | ||
27 | let item_id = fn_id.loc(db); | ||
28 | let syntax = db.file_item(item_id); | ||
29 | FnDef::cast(syntax.borrowed()).unwrap().owned() | ||
30 | } | ||
31 | |||
32 | pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> { | ||
33 | let syntax = db.fn_syntax(fn_id); | ||
34 | let res = FnScopes::new(syntax.borrowed()); | ||
35 | Arc::new(res) | ||
36 | } | ||
37 | |||
38 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | ||
39 | let source_file = db.source_file(file_id); | ||
40 | let source_file = source_file.borrowed(); | ||
41 | let mut res = SourceFileItems::default(); | ||
42 | source_file | ||
43 | .syntax() | ||
44 | .descendants() | ||
45 | .filter_map(ast::ModuleItem::cast) | ||
46 | .map(|it| it.syntax().owned()) | ||
47 | .for_each(|it| { | ||
48 | res.alloc(it); | ||
49 | }); | ||
50 | Arc::new(res) | ||
51 | } | ||
52 | |||
53 | pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { | ||
54 | db.file_items(source_item_id.file_id)[source_item_id.item_id].clone() | ||
55 | } | ||
56 | |||
57 | pub(crate) fn submodules( | ||
58 | db: &impl HirDatabase, | ||
59 | source: ModuleSource, | ||
60 | ) -> Cancelable<Arc<Vec<Submodule>>> { | ||
61 | db.check_canceled()?; | ||
62 | let file_id = source.file_id(); | ||
63 | let submodules = match source.resolve(db) { | ||
64 | ModuleSourceNode::SourceFile(it) => collect_submodules(db, file_id, it.borrowed()), | ||
65 | ModuleSourceNode::Module(it) => it | ||
66 | .borrowed() | ||
67 | .item_list() | ||
68 | .map(|it| collect_submodules(db, file_id, it)) | ||
69 | .unwrap_or_else(Vec::new), | ||
70 | }; | ||
71 | return Ok(Arc::new(submodules)); | ||
72 | |||
73 | fn collect_submodules<'a>( | ||
74 | db: &impl HirDatabase, | ||
75 | file_id: FileId, | ||
76 | root: impl ast::ModuleItemOwner<'a>, | ||
77 | ) -> Vec<Submodule> { | ||
78 | modules(root) | ||
79 | .map(|(name, m)| { | ||
80 | if m.has_semi() { | ||
81 | Submodule::Declaration(name) | ||
82 | } else { | ||
83 | let src = ModuleSource::new_inline(db, file_id, m); | ||
84 | Submodule::Definition(name, src) | ||
85 | } | ||
86 | }) | ||
87 | .collect() | ||
88 | } | ||
89 | } | ||
90 | |||
91 | pub(crate) fn modules<'a>( | ||
92 | root: impl ast::ModuleItemOwner<'a>, | ||
93 | ) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { | ||
94 | root.items() | ||
95 | .filter_map(|item| match item { | ||
96 | ast::ModuleItem::Module(m) => Some(m), | ||
97 | _ => None, | ||
98 | }) | ||
99 | .filter_map(|module| { | ||
100 | let name = module.name()?.text(); | ||
101 | Some((name, module)) | ||
102 | }) | ||
103 | } | ||
104 | |||
105 | pub(super) fn input_module_items( | ||
106 | db: &impl HirDatabase, | ||
107 | source_root: SourceRootId, | ||
108 | module_id: ModuleId, | ||
109 | ) -> Cancelable<Arc<InputModuleItems>> { | ||
110 | let module_tree = db.module_tree(source_root)?; | ||
111 | let source = module_id.source(&module_tree); | ||
112 | let file_items = db.file_items(source.file_id()); | ||
113 | let res = match source.resolve(db) { | ||
114 | ModuleSourceNode::SourceFile(it) => { | ||
115 | let items = it.borrowed().items(); | ||
116 | InputModuleItems::new(&file_items, items) | ||
117 | } | ||
118 | ModuleSourceNode::Module(it) => { | ||
119 | let items = it | ||
120 | .borrowed() | ||
121 | .item_list() | ||
122 | .into_iter() | ||
123 | .flat_map(|it| it.items()); | ||
124 | InputModuleItems::new(&file_items, items) | ||
125 | } | ||
126 | }; | ||
127 | Ok(Arc::new(res)) | ||
128 | } | ||
129 | |||
130 | pub(super) fn item_map( | ||
131 | db: &impl HirDatabase, | ||
132 | source_root: SourceRootId, | ||
133 | ) -> Cancelable<Arc<ItemMap>> { | ||
134 | let start = Instant::now(); | ||
135 | let module_tree = db.module_tree(source_root)?; | ||
136 | let input = module_tree | ||
137 | .modules() | ||
138 | .map(|id| { | ||
139 | let items = db.input_module_items(source_root, id)?; | ||
140 | Ok((id, items)) | ||
141 | }) | ||
142 | .collect::<Cancelable<FxHashMap<_, _>>>()?; | ||
143 | let resolver = Resolver { | ||
144 | db: db, | ||
145 | input: &input, | ||
146 | source_root, | ||
147 | module_tree, | ||
148 | result: ItemMap::default(), | ||
149 | }; | ||
150 | let res = resolver.resolve()?; | ||
151 | let elapsed = start.elapsed(); | ||
152 | log::info!("item_map: {:?}", elapsed); | ||
153 | Ok(Arc::new(res)) | ||
154 | } | ||
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 5d5a0c55e..28368787c 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -63,10 +63,7 @@ impl ConvWith for TextUnit { | |||
63 | 63 | ||
64 | fn conv_with(self, line_index: &LineIndex) -> Position { | 64 | fn conv_with(self, line_index: &LineIndex) -> Position { |
65 | let line_col = line_index.line_col(self); | 65 | let line_col = line_index.line_col(self); |
66 | Position::new( | 66 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)) |
67 | u64::from(line_col.line), | ||
68 | u64::from(u32::from(line_col.col_utf16)), | ||
69 | ) | ||
70 | } | 67 | } |
71 | } | 68 | } |
72 | 69 | ||
@@ -204,10 +201,8 @@ impl TryConvWith for SourceChange { | |||
204 | .map(|it| it.edits.as_slice()) | 201 | .map(|it| it.edits.as_slice()) |
205 | .unwrap_or(&[]); | 202 | .unwrap_or(&[]); |
206 | let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); | 203 | let line_col = translate_offset_with_edit(&*line_index, pos.offset, edits); |
207 | let position = Position::new( | 204 | let position = |
208 | u64::from(line_col.line), | 205 | Position::new(u64::from(line_col.line), u64::from(line_col.col_utf16)); |
209 | u64::from(u32::from(line_col.col_utf16)), | ||
210 | ); | ||
211 | Some(TextDocumentPositionParams { | 206 | Some(TextDocumentPositionParams { |
212 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), | 207 | text_document: TextDocumentIdentifier::new(pos.file_id.try_conv_with(world)?), |
213 | position, | 208 | position, |
diff --git a/crates/ra_lsp_server/src/main.rs b/crates/ra_lsp_server/src/main.rs index 26bcddd8e..8301a1044 100644 --- a/crates/ra_lsp_server/src/main.rs +++ b/crates/ra_lsp_server/src/main.rs | |||
@@ -65,27 +65,3 @@ fn main_inner() -> Result<()> { | |||
65 | info!("... IO is down"); | 65 | info!("... IO is down"); |
66 | Ok(()) | 66 | Ok(()) |
67 | } | 67 | } |
68 | |||
69 | /* | ||
70 | (let ((backend (eglot-xref-backend))) | ||
71 | (mapcar | ||
72 | (lambda (xref) | ||
73 | (let ((loc (xref-item-location xref))) | ||
74 | (propertize | ||
75 | (concat | ||
76 | (when (xref-file-location-p loc) | ||
77 | (with-slots (file line column) loc | ||
78 | (format "%s:%s:%s:" | ||
79 | (propertize (file-relative-name file) | ||
80 | 'face 'compilation-info) | ||
81 | (propertize (format "%s" line) | ||
82 | 'face 'compilation-line | ||
83 | ) | ||
84 | column))) | ||
85 | (xref-item-summary xref)) | ||
86 | 'xref xref))) | ||
87 | (xref-backend-apropos backend "Analysis")) | ||
88 | ) | ||
89 | |||
90 | |||
91 | */ | ||
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 27933a7ae..6d5622b15 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -384,7 +384,7 @@ pub fn handle_completion( | |||
384 | let completion_triggered_after_single_colon = { | 384 | let completion_triggered_after_single_colon = { |
385 | let mut res = false; | 385 | let mut res = false; |
386 | if let Some(ctx) = params.context { | 386 | if let Some(ctx) = params.context { |
387 | if ctx.trigger_character.unwrap_or(String::new()) == ":" { | 387 | if ctx.trigger_character.unwrap_or_default() == ":" { |
388 | let source_file = world.analysis().file_syntax(position.file_id); | 388 | let source_file = world.analysis().file_syntax(position.file_id); |
389 | let syntax = source_file.syntax(); | 389 | let syntax = source_file.syntax(); |
390 | let text = syntax.text(); | 390 | let text = syntax.text(); |
@@ -567,10 +567,13 @@ pub fn handle_rename(world: ServerWorld, params: RenameParams) -> Result<Option< | |||
567 | let mut changes = HashMap::new(); | 567 | let mut changes = HashMap::new(); |
568 | for r in refs { | 568 | for r in refs { |
569 | if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) { | 569 | if let Ok(loc) = to_location(r.0, r.1, &world, &line_index) { |
570 | changes.entry(loc.uri).or_insert(Vec::new()).push(TextEdit { | 570 | changes |
571 | range: loc.range, | 571 | .entry(loc.uri) |
572 | new_text: params.new_name.clone(), | 572 | .or_insert_with(Vec::new) |
573 | }); | 573 | .push(TextEdit { |
574 | range: loc.range, | ||
575 | new_text: params.new_name.clone(), | ||
576 | }); | ||
574 | } | 577 | } |
575 | } | 578 | } |
576 | 579 | ||
diff --git a/crates/ra_lsp_server/src/path_map.rs b/crates/ra_lsp_server/src/path_map.rs index a624043d8..02e54629c 100644 --- a/crates/ra_lsp_server/src/path_map.rs +++ b/crates/ra_lsp_server/src/path_map.rs | |||
@@ -43,7 +43,7 @@ impl PathMap { | |||
43 | (inserted, file_id) | 43 | (inserted, file_id) |
44 | } | 44 | } |
45 | pub fn get_id(&self, path: &Path) -> Option<FileId> { | 45 | pub fn get_id(&self, path: &Path) -> Option<FileId> { |
46 | self.path2id.get(path).map(|&id| id) | 46 | self.path2id.get(path).cloned() |
47 | } | 47 | } |
48 | pub fn get_path(&self, file_id: FileId) -> &Path { | 48 | pub fn get_path(&self, file_id: FileId) -> &Path { |
49 | self.id2path.get(&file_id).unwrap().as_path() | 49 | self.id2path.get(&file_id).unwrap().as_path() |
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 3e7670fcc..12faeb93a 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -140,7 +140,7 @@ impl ServerWorldState { | |||
140 | Ok(file_id) | 140 | Ok(file_id) |
141 | } | 141 | } |
142 | pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { | 142 | pub fn set_workspaces(&mut self, ws: Vec<CargoWorkspace>) { |
143 | let mut crate_graph = CrateGraph::new(); | 143 | let mut crate_graph = CrateGraph::default(); |
144 | ws.iter() | 144 | ws.iter() |
145 | .flat_map(|ws| { | 145 | .flat_map(|ws| { |
146 | ws.packages() | 146 | ws.packages() |