diff options
35 files changed, 1382 insertions, 975 deletions
diff --git a/Cargo.lock b/Cargo.lock index 6435bfb47..5f82e92f4 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -667,6 +667,7 @@ name = "ra_analysis" | |||
667 | version = "0.1.0" | 667 | version = "0.1.0" |
668 | dependencies = [ | 668 | dependencies = [ |
669 | "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", | 669 | "fst 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", |
670 | "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
670 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", | 671 | "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", |
671 | "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | 672 | "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", |
672 | "ra_db 0.1.0", | 673 | "ra_db 0.1.0", |
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml index a5d4f65ab..c0174cdc5 100644 --- a/crates/ra_analysis/Cargo.toml +++ b/crates/ra_analysis/Cargo.toml | |||
@@ -5,6 +5,7 @@ version = "0.1.0" | |||
5 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | itertools = "0.8.0" | ||
8 | log = "0.4.5" | 9 | log = "0.4.5" |
9 | relative-path = "0.4.0" | 10 | relative-path = "0.4.0" |
10 | rayon = "1.0.2" | 11 | rayon = "1.0.2" |
@@ -12,6 +13,7 @@ fst = "0.3.1" | |||
12 | salsa = "0.9.0" | 13 | salsa = "0.9.0" |
13 | rustc-hash = "1.0" | 14 | rustc-hash = "1.0" |
14 | parking_lot = "0.7.0" | 15 | parking_lot = "0.7.0" |
16 | |||
15 | ra_syntax = { path = "../ra_syntax" } | 17 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_editor = { path = "../ra_editor" } | 18 | ra_editor = { path = "../ra_editor" } |
17 | ra_text_edit = { path = "../ra_text_edit" } | 19 | ra_text_edit = { path = "../ra_text_edit" } |
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index b4ee092b5..739ca6ae8 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -58,6 +58,6 @@ fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind | |||
58 | } else { | 58 | } else { |
59 | single_file_with_position(code) | 59 | single_file_with_position(code) |
60 | }; | 60 | }; |
61 | let completions = completions(&analysis.imp.db, position).unwrap().unwrap(); | 61 | let completions = completions(&analysis.db, position).unwrap().unwrap(); |
62 | completions.assert_match(expected_completions, kind); | 62 | completions.assert_match(expected_completions, kind); |
63 | } | 63 | } |
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs index daf666505..4dead3689 100644 --- a/crates/ra_analysis/src/completion/complete_scope.rs +++ b/crates/ra_analysis/src/completion/complete_scope.rs | |||
@@ -27,7 +27,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> | |||
27 | match res.import { | 27 | match res.import { |
28 | None => true, | 28 | None => true, |
29 | Some(import) => { | 29 | Some(import) => { |
30 | let range = import.range(ctx.db, module.source().file_id()); | 30 | let range = import.range(ctx.db, module.file_id()); |
31 | !range.is_subrange(&ctx.leaf.range()) | 31 | !range.is_subrange(&ctx.leaf.range()) |
32 | } | 32 | } |
33 | } | 33 | } |
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs index b072a5eba..d7740f0c4 100644 --- a/crates/ra_analysis/src/db.rs +++ b/crates/ra_analysis/src/db.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::{fmt, sync::Arc}; | 1 | use std::{fmt, sync::Arc}; |
2 | use salsa::{self, Database}; | 2 | use salsa::{self, Database}; |
3 | use ra_db::{LocationIntener, BaseDatabase}; | 3 | use ra_db::{LocationIntener, BaseDatabase}; |
4 | use hir::{self, DefId, DefLoc}; | ||
5 | 4 | ||
6 | use crate::{ | 5 | use crate::{ |
7 | symbol_index, | 6 | symbol_index, |
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase { | |||
15 | 14 | ||
16 | #[derive(Default)] | 15 | #[derive(Default)] |
17 | struct IdMaps { | 16 | struct IdMaps { |
18 | defs: LocationIntener<DefLoc, DefId>, | 17 | defs: LocationIntener<hir::DefLoc, hir::DefId>, |
18 | macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>, | ||
19 | } | 19 | } |
20 | 20 | ||
21 | impl fmt::Debug for IdMaps { | 21 | impl fmt::Debug for IdMaps { |
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase { | |||
59 | 59 | ||
60 | impl BaseDatabase for RootDatabase {} | 60 | impl BaseDatabase for RootDatabase {} |
61 | 61 | ||
62 | impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { | 62 | impl AsRef<LocationIntener<hir::DefLoc, hir::DefId>> for RootDatabase { |
63 | fn as_ref(&self) -> &LocationIntener<DefLoc, DefId> { | 63 | fn as_ref(&self) -> &LocationIntener<hir::DefLoc, hir::DefId> { |
64 | &self.id_maps.defs | 64 | &self.id_maps.defs |
65 | } | 65 | } |
66 | } | 66 | } |
67 | 67 | ||
68 | impl AsRef<LocationIntener<hir::MacroCallLoc, hir::MacroCallId>> for RootDatabase { | ||
69 | fn as_ref(&self) -> &LocationIntener<hir::MacroCallLoc, hir::MacroCallId> { | ||
70 | &self.id_maps.macros | ||
71 | } | ||
72 | } | ||
73 | |||
68 | salsa::database_storage! { | 74 | salsa::database_storage! { |
69 | pub(crate) struct RootDatabaseStorage for RootDatabase { | 75 | pub(crate) struct RootDatabaseStorage for RootDatabase { |
70 | impl ra_db::FilesDatabase { | 76 | impl ra_db::FilesDatabase { |
@@ -85,6 +91,8 @@ salsa::database_storage! { | |||
85 | fn library_symbols() for symbol_index::LibrarySymbolsQuery; | 91 | fn library_symbols() for symbol_index::LibrarySymbolsQuery; |
86 | } | 92 | } |
87 | impl hir::db::HirDatabase { | 93 | impl hir::db::HirDatabase { |
94 | fn hir_source_file() for hir::db::HirSourceFileQuery; | ||
95 | fn expand_macro_invocation() for hir::db::ExpandMacroCallQuery; | ||
88 | fn module_tree() for hir::db::ModuleTreeQuery; | 96 | fn module_tree() for hir::db::ModuleTreeQuery; |
89 | fn fn_scopes() for hir::db::FnScopesQuery; | 97 | fn fn_scopes() for hir::db::FnScopesQuery; |
90 | fn file_items() for hir::db::SourceFileItemsQuery; | 98 | fn file_items() for hir::db::SourceFileItemsQuery; |
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs index 805e9059e..f1b77f981 100644 --- a/crates/ra_analysis/src/extend_selection.rs +++ b/crates/ra_analysis/src/extend_selection.rs | |||
@@ -18,15 +18,15 @@ pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRang | |||
18 | } | 18 | } |
19 | 19 | ||
20 | fn extend_selection_in_macro( | 20 | fn extend_selection_in_macro( |
21 | db: &RootDatabase, | 21 | _db: &RootDatabase, |
22 | source_file: &SourceFileNode, | 22 | source_file: &SourceFileNode, |
23 | frange: FileRange, | 23 | frange: FileRange, |
24 | ) -> Option<TextRange> { | 24 | ) -> Option<TextRange> { |
25 | let macro_call = find_macro_call(source_file.syntax(), frange.range)?; | 25 | let macro_call = find_macro_call(source_file.syntax(), frange.range)?; |
26 | let exp = crate::macros::expand(db, frange.file_id, macro_call)?; | 26 | let (off, exp) = hir::MacroDef::ast_expand(macro_call)?; |
27 | let dst_range = exp.map_range_forward(frange.range)?; | 27 | let dst_range = exp.map_range_forward(frange.range - off)?; |
28 | let dst_range = ra_editor::extend_selection(exp.source_file().syntax(), dst_range)?; | 28 | let dst_range = ra_editor::extend_selection(exp.syntax().borrowed(), dst_range)?; |
29 | let src_range = exp.map_range_back(dst_range)?; | 29 | let src_range = exp.map_range_back(dst_range)? + off; |
30 | Some(src_range) | 30 | Some(src_range) |
31 | } | 31 | } |
32 | 32 | ||
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 5669aa94d..ff13247de 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs | |||
@@ -1,16 +1,12 @@ | |||
1 | use std::{ | 1 | use std::sync::Arc; |
2 | fmt, | ||
3 | sync::Arc, | ||
4 | }; | ||
5 | 2 | ||
6 | use rayon::prelude::*; | 3 | use salsa::Database; |
7 | use salsa::{Database, ParallelDatabase}; | ||
8 | 4 | ||
9 | use hir::{ | 5 | use hir::{ |
10 | self, FnSignatureInfo, Problem, source_binder, | 6 | self, FnSignatureInfo, Problem, source_binder, |
11 | }; | 7 | }; |
12 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 8 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
13 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | 9 | use ra_editor::{self, find_node_at_offset, LocalEdit, Severity}; |
14 | use ra_syntax::{ | 10 | use ra_syntax::{ |
15 | algo::find_covering_node, | 11 | algo::find_covering_node, |
16 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, | 12 | ast::{self, ArgListOwner, Expr, FnDef, NameOwner}, |
@@ -21,39 +17,26 @@ use ra_syntax::{ | |||
21 | 17 | ||
22 | use crate::{ | 18 | use crate::{ |
23 | AnalysisChange, | 19 | AnalysisChange, |
24 | Cancelable, | 20 | Cancelable, NavigationTarget, |
25 | completion::{CompletionItem, completions}, | ||
26 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, | 21 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, |
27 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, | 22 | Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, |
28 | symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, | 23 | symbol_index::{LibrarySymbolsQuery, FileSymbol}, |
29 | }; | 24 | }; |
30 | 25 | ||
31 | #[derive(Debug, Default)] | 26 | impl db::RootDatabase { |
32 | pub(crate) struct AnalysisHostImpl { | 27 | pub(crate) fn apply_change(&mut self, change: AnalysisChange) { |
33 | db: db::RootDatabase, | ||
34 | } | ||
35 | |||
36 | impl AnalysisHostImpl { | ||
37 | pub fn analysis(&self) -> AnalysisImpl { | ||
38 | AnalysisImpl { | ||
39 | db: self.db.snapshot(), | ||
40 | } | ||
41 | } | ||
42 | pub fn apply_change(&mut self, change: AnalysisChange) { | ||
43 | log::info!("apply_change {:?}", change); | 28 | log::info!("apply_change {:?}", change); |
44 | // self.gc_syntax_trees(); | 29 | // self.gc_syntax_trees(); |
45 | if !change.new_roots.is_empty() { | 30 | if !change.new_roots.is_empty() { |
46 | let mut local_roots = Vec::clone(&self.db.local_roots()); | 31 | let mut local_roots = Vec::clone(&self.local_roots()); |
47 | for (root_id, is_local) in change.new_roots { | 32 | for (root_id, is_local) in change.new_roots { |
48 | self.db | 33 | self.query_mut(ra_db::SourceRootQuery) |
49 | .query_mut(ra_db::SourceRootQuery) | ||
50 | .set(root_id, Default::default()); | 34 | .set(root_id, Default::default()); |
51 | if is_local { | 35 | if is_local { |
52 | local_roots.push(root_id); | 36 | local_roots.push(root_id); |
53 | } | 37 | } |
54 | } | 38 | } |
55 | self.db | 39 | self.query_mut(ra_db::LocalRootsQuery) |
56 | .query_mut(ra_db::LocalRootsQuery) | ||
57 | .set((), Arc::new(local_roots)); | 40 | .set((), Arc::new(local_roots)); |
58 | } | 41 | } |
59 | 42 | ||
@@ -61,53 +44,44 @@ impl AnalysisHostImpl { | |||
61 | self.apply_root_change(root_id, root_change); | 44 | self.apply_root_change(root_id, root_change); |
62 | } | 45 | } |
63 | for (file_id, text) in change.files_changed { | 46 | for (file_id, text) in change.files_changed { |
64 | self.db.query_mut(ra_db::FileTextQuery).set(file_id, text) | 47 | self.query_mut(ra_db::FileTextQuery).set(file_id, text) |
65 | } | 48 | } |
66 | if !change.libraries_added.is_empty() { | 49 | if !change.libraries_added.is_empty() { |
67 | let mut libraries = Vec::clone(&self.db.library_roots()); | 50 | let mut libraries = Vec::clone(&self.library_roots()); |
68 | for library in change.libraries_added { | 51 | for library in change.libraries_added { |
69 | libraries.push(library.root_id); | 52 | libraries.push(library.root_id); |
70 | self.db | 53 | self.query_mut(ra_db::SourceRootQuery) |
71 | .query_mut(ra_db::SourceRootQuery) | ||
72 | .set(library.root_id, Default::default()); | 54 | .set(library.root_id, Default::default()); |
73 | self.db | 55 | self.query_mut(LibrarySymbolsQuery) |
74 | .query_mut(LibrarySymbolsQuery) | ||
75 | .set_constant(library.root_id, Arc::new(library.symbol_index)); | 56 | .set_constant(library.root_id, Arc::new(library.symbol_index)); |
76 | self.apply_root_change(library.root_id, library.root_change); | 57 | self.apply_root_change(library.root_id, library.root_change); |
77 | } | 58 | } |
78 | self.db | 59 | self.query_mut(ra_db::LibraryRootsQuery) |
79 | .query_mut(ra_db::LibraryRootsQuery) | ||
80 | .set((), Arc::new(libraries)); | 60 | .set((), Arc::new(libraries)); |
81 | } | 61 | } |
82 | if let Some(crate_graph) = change.crate_graph { | 62 | if let Some(crate_graph) = change.crate_graph { |
83 | self.db | 63 | self.query_mut(ra_db::CrateGraphQuery) |
84 | .query_mut(ra_db::CrateGraphQuery) | ||
85 | .set((), Arc::new(crate_graph)) | 64 | .set((), Arc::new(crate_graph)) |
86 | } | 65 | } |
87 | } | 66 | } |
88 | 67 | ||
89 | fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { | 68 | fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { |
90 | let mut source_root = SourceRoot::clone(&self.db.source_root(root_id)); | 69 | let mut source_root = SourceRoot::clone(&self.source_root(root_id)); |
91 | for add_file in root_change.added { | 70 | for add_file in root_change.added { |
92 | self.db | 71 | self.query_mut(ra_db::FileTextQuery) |
93 | .query_mut(ra_db::FileTextQuery) | ||
94 | .set(add_file.file_id, add_file.text); | 72 | .set(add_file.file_id, add_file.text); |
95 | self.db | 73 | self.query_mut(ra_db::FileRelativePathQuery) |
96 | .query_mut(ra_db::FileRelativePathQuery) | ||
97 | .set(add_file.file_id, add_file.path.clone()); | 74 | .set(add_file.file_id, add_file.path.clone()); |
98 | self.db | 75 | self.query_mut(ra_db::FileSourceRootQuery) |
99 | .query_mut(ra_db::FileSourceRootQuery) | ||
100 | .set(add_file.file_id, root_id); | 76 | .set(add_file.file_id, root_id); |
101 | source_root.files.insert(add_file.path, add_file.file_id); | 77 | source_root.files.insert(add_file.path, add_file.file_id); |
102 | } | 78 | } |
103 | for remove_file in root_change.removed { | 79 | for remove_file in root_change.removed { |
104 | self.db | 80 | self.query_mut(ra_db::FileTextQuery) |
105 | .query_mut(ra_db::FileTextQuery) | ||
106 | .set(remove_file.file_id, Default::default()); | 81 | .set(remove_file.file_id, Default::default()); |
107 | source_root.files.remove(&remove_file.path); | 82 | source_root.files.remove(&remove_file.path); |
108 | } | 83 | } |
109 | self.db | 84 | self.query_mut(ra_db::SourceRootQuery) |
110 | .query_mut(ra_db::SourceRootQuery) | ||
111 | .set(root_id, Arc::new(source_root)); | 85 | .set(root_id, Arc::new(source_root)); |
112 | } | 86 | } |
113 | 87 | ||
@@ -116,147 +90,67 @@ impl AnalysisHostImpl { | |||
116 | /// syntax trees. However, if we actually do that, everything is recomputed | 90 | /// syntax trees. However, if we actually do that, everything is recomputed |
117 | /// for some reason. Needs investigation. | 91 | /// for some reason. Needs investigation. |
118 | fn gc_syntax_trees(&mut self) { | 92 | fn gc_syntax_trees(&mut self) { |
119 | self.db | 93 | self.query(ra_db::SourceFileQuery) |
120 | .query(ra_db::SourceFileQuery) | ||
121 | .sweep(salsa::SweepStrategy::default().discard_values()); | 94 | .sweep(salsa::SweepStrategy::default().discard_values()); |
122 | self.db | 95 | self.query(hir::db::SourceFileItemsQuery) |
123 | .query(hir::db::SourceFileItemsQuery) | ||
124 | .sweep(salsa::SweepStrategy::default().discard_values()); | 96 | .sweep(salsa::SweepStrategy::default().discard_values()); |
125 | self.db | 97 | self.query(hir::db::FileItemQuery) |
126 | .query(hir::db::FileItemQuery) | ||
127 | .sweep(salsa::SweepStrategy::default().discard_values()); | 98 | .sweep(salsa::SweepStrategy::default().discard_values()); |
128 | } | 99 | } |
129 | } | 100 | } |
130 | 101 | ||
131 | pub(crate) struct AnalysisImpl { | 102 | impl db::RootDatabase { |
132 | pub(crate) db: salsa::Snapshot<db::RootDatabase>, | ||
133 | } | ||
134 | |||
135 | impl fmt::Debug for AnalysisImpl { | ||
136 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
137 | let db: &db::RootDatabase = &self.db; | ||
138 | fmt.debug_struct("AnalysisImpl").field("db", db).finish() | ||
139 | } | ||
140 | } | ||
141 | |||
142 | impl AnalysisImpl { | ||
143 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { | ||
144 | self.db.file_text(file_id) | ||
145 | } | ||
146 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { | ||
147 | self.db.source_file(file_id) | ||
148 | } | ||
149 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { | ||
150 | self.db.file_lines(file_id) | ||
151 | } | ||
152 | pub fn world_symbols(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { | ||
153 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | ||
154 | struct Snap(salsa::Snapshot<db::RootDatabase>); | ||
155 | impl Clone for Snap { | ||
156 | fn clone(&self) -> Snap { | ||
157 | Snap(self.0.snapshot()) | ||
158 | } | ||
159 | } | ||
160 | |||
161 | let buf: Vec<Arc<SymbolIndex>> = if query.libs { | ||
162 | let snap = Snap(self.db.snapshot()); | ||
163 | self.db | ||
164 | .library_roots() | ||
165 | .par_iter() | ||
166 | .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) | ||
167 | .collect() | ||
168 | } else { | ||
169 | let mut files = Vec::new(); | ||
170 | for &root in self.db.local_roots().iter() { | ||
171 | let sr = self.db.source_root(root); | ||
172 | files.extend(sr.files.values().map(|&it| it)) | ||
173 | } | ||
174 | |||
175 | let snap = Snap(self.db.snapshot()); | ||
176 | files | ||
177 | .par_iter() | ||
178 | .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) | ||
179 | .filter_map(|it| it.ok()) | ||
180 | .collect() | ||
181 | }; | ||
182 | Ok(query.search(&buf)) | ||
183 | } | ||
184 | |||
185 | pub(crate) fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { | ||
186 | let descr = match source_binder::module_from_position(&*self.db, position)? { | ||
187 | None => return Ok(None), | ||
188 | Some(it) => it, | ||
189 | }; | ||
190 | let name = match descr.name() { | ||
191 | None => return Ok(None), | ||
192 | Some(it) => it.to_string(), | ||
193 | }; | ||
194 | |||
195 | let modules = descr.path_to_root(); | ||
196 | |||
197 | let path = modules | ||
198 | .iter() | ||
199 | .filter_map(|s| s.name()) | ||
200 | .skip(1) // name is already part of the string. | ||
201 | .fold(name, |path, it| format!("{}::{}", it, path)); | ||
202 | |||
203 | Ok(Some(path.to_string())) | ||
204 | } | ||
205 | |||
206 | /// This returns `Vec` because a module may be included from several places. We | 103 | /// This returns `Vec` because a module may be included from several places. We |
207 | /// don't handle this case yet though, so the Vec has length at most one. | 104 | /// don't handle this case yet though, so the Vec has length at most one. |
208 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 105 | pub(crate) fn parent_module( |
209 | let descr = match source_binder::module_from_position(&*self.db, position)? { | 106 | &self, |
107 | position: FilePosition, | ||
108 | ) -> Cancelable<Vec<NavigationTarget>> { | ||
109 | let descr = match source_binder::module_from_position(self, position)? { | ||
210 | None => return Ok(Vec::new()), | 110 | None => return Ok(Vec::new()), |
211 | Some(it) => it, | 111 | Some(it) => it, |
212 | }; | 112 | }; |
213 | let (file_id, decl) = match descr.parent_link_source(&*self.db) { | 113 | let (file_id, decl) = match descr.parent_link_source(self) { |
214 | None => return Ok(Vec::new()), | 114 | None => return Ok(Vec::new()), |
215 | Some(it) => it, | 115 | Some(it) => it, |
216 | }; | 116 | }; |
217 | let decl = decl.borrowed(); | 117 | let decl = decl.borrowed(); |
218 | let decl_name = decl.name().unwrap(); | 118 | let decl_name = decl.name().unwrap(); |
219 | let sym = FileSymbol { | 119 | let symbol = FileSymbol { |
220 | name: decl_name.text(), | 120 | name: decl_name.text(), |
221 | node_range: decl_name.syntax().range(), | 121 | node_range: decl_name.syntax().range(), |
222 | kind: MODULE, | 122 | kind: MODULE, |
223 | }; | 123 | }; |
224 | Ok(vec![(file_id, sym)]) | 124 | Ok(vec![NavigationTarget { file_id, symbol }]) |
225 | } | 125 | } |
226 | /// Returns `Vec` for the same reason as `parent_module` | 126 | /// Returns `Vec` for the same reason as `parent_module` |
227 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 127 | pub(crate) fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
228 | let descr = match source_binder::module_from_file_id(&*self.db, file_id)? { | 128 | let descr = match source_binder::module_from_file_id(self, file_id)? { |
229 | None => return Ok(Vec::new()), | 129 | None => return Ok(Vec::new()), |
230 | Some(it) => it, | 130 | Some(it) => it, |
231 | }; | 131 | }; |
232 | let root = descr.crate_root(); | 132 | let root = descr.crate_root(); |
233 | let file_id = root.source().file_id(); | 133 | let file_id = root.file_id(); |
234 | 134 | ||
235 | let crate_graph = self.db.crate_graph(); | 135 | let crate_graph = self.crate_graph(); |
236 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); | 136 | let crate_id = crate_graph.crate_id_for_crate_root(file_id); |
237 | Ok(crate_id.into_iter().collect()) | 137 | Ok(crate_id.into_iter().collect()) |
238 | } | 138 | } |
239 | pub fn crate_root(&self, crate_id: CrateId) -> FileId { | 139 | pub(crate) fn crate_root(&self, crate_id: CrateId) -> FileId { |
240 | self.db.crate_graph().crate_root(crate_id) | 140 | self.crate_graph().crate_root(crate_id) |
241 | } | ||
242 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { | ||
243 | let completions = completions(&self.db, position)?; | ||
244 | Ok(completions.map(|it| it.into())) | ||
245 | } | 141 | } |
246 | pub fn approximately_resolve_symbol( | 142 | pub(crate) fn approximately_resolve_symbol( |
247 | &self, | 143 | &self, |
248 | position: FilePosition, | 144 | position: FilePosition, |
249 | ) -> Cancelable<Option<ReferenceResolution>> { | 145 | ) -> Cancelable<Option<ReferenceResolution>> { |
250 | let file = self.db.source_file(position.file_id); | 146 | let file = self.source_file(position.file_id); |
251 | let syntax = file.syntax(); | 147 | let syntax = file.syntax(); |
252 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { | 148 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) { |
253 | let mut rr = ReferenceResolution::new(name_ref.syntax().range()); | 149 | let mut rr = ReferenceResolution::new(name_ref.syntax().range()); |
254 | if let Some(fn_descr) = source_binder::function_from_child_node( | 150 | if let Some(fn_descr) = |
255 | &*self.db, | 151 | source_binder::function_from_child_node(self, position.file_id, name_ref.syntax())? |
256 | position.file_id, | 152 | { |
257 | name_ref.syntax(), | 153 | let scope = fn_descr.scopes(self); |
258 | )? { | ||
259 | let scope = fn_descr.scopes(&*self.db); | ||
260 | // First try to resolve the symbol locally | 154 | // First try to resolve the symbol locally |
261 | if let Some(entry) = scope.resolve_local_name(name_ref) { | 155 | if let Some(entry) = scope.resolve_local_name(name_ref) { |
262 | rr.add_resolution( | 156 | rr.add_resolution( |
@@ -281,9 +175,9 @@ impl AnalysisImpl { | |||
281 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { | 175 | if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { |
282 | if module.has_semi() { | 176 | if module.has_semi() { |
283 | if let Some(child_module) = | 177 | if let Some(child_module) = |
284 | source_binder::module_from_declaration(&*self.db, position.file_id, module)? | 178 | source_binder::module_from_declaration(self, position.file_id, module)? |
285 | { | 179 | { |
286 | let file_id = child_module.source().file_id(); | 180 | let file_id = child_module.file_id(); |
287 | let name = match child_module.name() { | 181 | let name = match child_module.name() { |
288 | Some(name) => name.to_string().into(), | 182 | Some(name) => name.to_string().into(), |
289 | None => "".into(), | 183 | None => "".into(), |
@@ -302,10 +196,13 @@ impl AnalysisImpl { | |||
302 | Ok(None) | 196 | Ok(None) |
303 | } | 197 | } |
304 | 198 | ||
305 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 199 | pub(crate) fn find_all_refs( |
306 | let file = self.db.source_file(position.file_id); | 200 | &self, |
201 | position: FilePosition, | ||
202 | ) -> Cancelable<Vec<(FileId, TextRange)>> { | ||
203 | let file = self.source_file(position.file_id); | ||
307 | // Find the binding associated with the offset | 204 | // Find the binding associated with the offset |
308 | let (binding, descr) = match find_binding(&self.db, &file, position)? { | 205 | let (binding, descr) = match find_binding(self, &file, position)? { |
309 | None => return Ok(Vec::new()), | 206 | None => return Ok(Vec::new()), |
310 | Some(it) => it, | 207 | Some(it) => it, |
311 | }; | 208 | }; |
@@ -317,7 +214,7 @@ impl AnalysisImpl { | |||
317 | .collect::<Vec<_>>(); | 214 | .collect::<Vec<_>>(); |
318 | ret.extend( | 215 | ret.extend( |
319 | descr | 216 | descr |
320 | .scopes(&*self.db) | 217 | .scopes(self) |
321 | .find_all_refs(binding) | 218 | .find_all_refs(binding) |
322 | .into_iter() | 219 | .into_iter() |
323 | .map(|ref_desc| (position.file_id, ref_desc.range)), | 220 | .map(|ref_desc| (position.file_id, ref_desc.range)), |
@@ -355,9 +252,9 @@ impl AnalysisImpl { | |||
355 | Ok(Some((binding, descr))) | 252 | Ok(Some((binding, descr))) |
356 | } | 253 | } |
357 | } | 254 | } |
358 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | 255 | pub(crate) fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
359 | let file = self.db.source_file(file_id); | 256 | let file = self.source_file(nav.file_id); |
360 | let result = match (symbol.description(&file), symbol.docs(&file)) { | 257 | let result = match (nav.symbol.description(&file), nav.symbol.docs(&file)) { |
361 | (Some(desc), Some(docs)) => { | 258 | (Some(desc), Some(docs)) => { |
362 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) | 259 | Some("```rust\n".to_string() + &*desc + "\n```\n\n" + &*docs) |
363 | } | 260 | } |
@@ -369,8 +266,8 @@ impl AnalysisImpl { | |||
369 | Ok(result) | 266 | Ok(result) |
370 | } | 267 | } |
371 | 268 | ||
372 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 269 | pub(crate) fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
373 | let syntax = self.db.source_file(file_id); | 270 | let syntax = self.source_file(file_id); |
374 | 271 | ||
375 | let mut res = ra_editor::diagnostics(&syntax) | 272 | let mut res = ra_editor::diagnostics(&syntax) |
376 | .into_iter() | 273 | .into_iter() |
@@ -381,9 +278,9 @@ impl AnalysisImpl { | |||
381 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), | 278 | fix: d.fix.map(|fix| SourceChange::from_local_edit(file_id, fix)), |
382 | }) | 279 | }) |
383 | .collect::<Vec<_>>(); | 280 | .collect::<Vec<_>>(); |
384 | if let Some(m) = source_binder::module_from_file_id(&*self.db, file_id)? { | 281 | if let Some(m) = source_binder::module_from_file_id(self, file_id)? { |
385 | for (name_node, problem) in m.problems(&*self.db) { | 282 | for (name_node, problem) in m.problems(self) { |
386 | let source_root = self.db.file_source_root(file_id); | 283 | let source_root = self.file_source_root(file_id); |
387 | let diag = match problem { | 284 | let diag = match problem { |
388 | Problem::UnresolvedModule { candidate } => { | 285 | Problem::UnresolvedModule { candidate } => { |
389 | let create_file = FileSystemEdit::CreateFile { | 286 | let create_file = FileSystemEdit::CreateFile { |
@@ -433,8 +330,8 @@ impl AnalysisImpl { | |||
433 | Ok(res) | 330 | Ok(res) |
434 | } | 331 | } |
435 | 332 | ||
436 | pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> { | 333 | pub(crate) fn assists(&self, frange: FileRange) -> Vec<SourceChange> { |
437 | let file = self.file_syntax(frange.file_id); | 334 | let file = self.source_file(frange.file_id); |
438 | let offset = frange.range.start(); | 335 | let offset = frange.range.start(); |
439 | let actions = vec![ | 336 | let actions = vec![ |
440 | ra_editor::flip_comma(&file, offset).map(|f| f()), | 337 | ra_editor::flip_comma(&file, offset).map(|f| f()), |
@@ -451,11 +348,11 @@ impl AnalysisImpl { | |||
451 | .collect() | 348 | .collect() |
452 | } | 349 | } |
453 | 350 | ||
454 | pub fn resolve_callable( | 351 | pub(crate) fn resolve_callable( |
455 | &self, | 352 | &self, |
456 | position: FilePosition, | 353 | position: FilePosition, |
457 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { | 354 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { |
458 | let file = self.db.source_file(position.file_id); | 355 | let file = self.source_file(position.file_id); |
459 | let syntax = file.syntax(); | 356 | let syntax = file.syntax(); |
460 | 357 | ||
461 | // Find the calling expression and it's NameRef | 358 | // Find the calling expression and it's NameRef |
@@ -466,12 +363,12 @@ impl AnalysisImpl { | |||
466 | let file_symbols = self.index_resolve(name_ref)?; | 363 | let file_symbols = self.index_resolve(name_ref)?; |
467 | for (fn_file_id, fs) in file_symbols { | 364 | for (fn_file_id, fs) in file_symbols { |
468 | if fs.kind == FN_DEF { | 365 | if fs.kind == FN_DEF { |
469 | let fn_file = self.db.source_file(fn_file_id); | 366 | let fn_file = self.source_file(fn_file_id); |
470 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { | 367 | if let Some(fn_def) = find_node_at_offset(fn_file.syntax(), fs.node_range.start()) { |
471 | let descr = ctry!(source_binder::function_from_source( | 368 | let descr = ctry!(source_binder::function_from_source( |
472 | &*self.db, fn_file_id, fn_def | 369 | self, fn_file_id, fn_def |
473 | )?); | 370 | )?); |
474 | if let Some(descriptor) = descr.signature_info(&*self.db) { | 371 | if let Some(descriptor) = descr.signature_info(self) { |
475 | // If we have a calling expression let's find which argument we are on | 372 | // If we have a calling expression let's find which argument we are on |
476 | let mut current_parameter = None; | 373 | let mut current_parameter = None; |
477 | 374 | ||
@@ -518,20 +415,20 @@ impl AnalysisImpl { | |||
518 | Ok(None) | 415 | Ok(None) |
519 | } | 416 | } |
520 | 417 | ||
521 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { | 418 | pub(crate) fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { |
522 | let file = self.db.source_file(frange.file_id); | 419 | let file = self.source_file(frange.file_id); |
523 | let syntax = file.syntax(); | 420 | let syntax = file.syntax(); |
524 | let node = find_covering_node(syntax, frange.range); | 421 | let node = find_covering_node(syntax, frange.range); |
525 | let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); | 422 | let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast)); |
526 | let function = ctry!(source_binder::function_from_source( | 423 | let function = ctry!(source_binder::function_from_source( |
527 | &*self.db, | 424 | self, |
528 | frange.file_id, | 425 | frange.file_id, |
529 | parent_fn | 426 | parent_fn |
530 | )?); | 427 | )?); |
531 | let infer = function.infer(&*self.db)?; | 428 | let infer = function.infer(self)?; |
532 | Ok(infer.type_of_node(node).map(|t| t.to_string())) | 429 | Ok(infer.type_of_node(node).map(|t| t.to_string())) |
533 | } | 430 | } |
534 | pub fn rename( | 431 | pub(crate) fn rename( |
535 | &self, | 432 | &self, |
536 | position: FilePosition, | 433 | position: FilePosition, |
537 | new_name: &str, | 434 | new_name: &str, |
@@ -555,7 +452,7 @@ impl AnalysisImpl { | |||
555 | let mut query = Query::new(name.to_string()); | 452 | let mut query = Query::new(name.to_string()); |
556 | query.exact(); | 453 | query.exact(); |
557 | query.limit(4); | 454 | query.limit(4); |
558 | self.world_symbols(query) | 455 | crate::symbol_index::world_symbols(self, query) |
559 | } | 456 | } |
560 | } | 457 | } |
561 | 458 | ||
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs index e6cfaecc3..a01febf4e 100644 --- a/crates/ra_analysis/src/lib.rs +++ b/crates/ra_analysis/src/lib.rs | |||
@@ -19,33 +19,30 @@ mod runnables; | |||
19 | 19 | ||
20 | mod extend_selection; | 20 | mod extend_selection; |
21 | mod syntax_highlighting; | 21 | mod syntax_highlighting; |
22 | mod macros; | ||
23 | 22 | ||
24 | use std::{fmt, sync::Arc}; | 23 | use std::{fmt, sync::Arc}; |
25 | 24 | ||
26 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
27 | use ra_syntax::{SourceFileNode, TextRange, TextUnit}; | 26 | use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind}; |
28 | use ra_text_edit::TextEdit; | 27 | use ra_text_edit::TextEdit; |
29 | use rayon::prelude::*; | 28 | use rayon::prelude::*; |
30 | use relative_path::RelativePathBuf; | 29 | use relative_path::RelativePathBuf; |
30 | use salsa::ParallelDatabase; | ||
31 | 31 | ||
32 | use crate::{ | 32 | use crate::symbol_index::{SymbolIndex, FileSymbol}; |
33 | imp::{AnalysisHostImpl, AnalysisImpl}, | ||
34 | symbol_index::SymbolIndex, | ||
35 | }; | ||
36 | 33 | ||
37 | pub use crate::{ | 34 | pub use crate::{ |
38 | completion::{CompletionItem, CompletionItemKind, InsertText}, | 35 | completion::{CompletionItem, CompletionItemKind, InsertText}, |
39 | runnables::{Runnable, RunnableKind} | 36 | runnables::{Runnable, RunnableKind}, |
40 | }; | 37 | }; |
41 | pub use ra_editor::{ | 38 | pub use ra_editor::{ |
42 | FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity | 39 | Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity |
43 | }; | 40 | }; |
44 | pub use hir::FnSignatureInfo; | 41 | pub use hir::FnSignatureInfo; |
45 | 42 | ||
46 | pub use ra_db::{ | 43 | pub use ra_db::{ |
47 | Canceled, Cancelable, FilePosition, FileRange, | 44 | Canceled, Cancelable, FilePosition, FileRange, |
48 | CrateGraph, CrateId, SourceRootId, FileId | 45 | CrateGraph, CrateId, SourceRootId, FileId, SyntaxDatabase, FilesDatabase |
49 | }; | 46 | }; |
50 | 47 | ||
51 | #[derive(Default)] | 48 | #[derive(Default)] |
@@ -151,27 +148,6 @@ impl AnalysisChange { | |||
151 | } | 148 | } |
152 | } | 149 | } |
153 | 150 | ||
154 | /// `AnalysisHost` stores the current state of the world. | ||
155 | #[derive(Debug, Default)] | ||
156 | pub struct AnalysisHost { | ||
157 | imp: AnalysisHostImpl, | ||
158 | } | ||
159 | |||
160 | impl AnalysisHost { | ||
161 | /// Returns a snapshot of the current state, which you can query for | ||
162 | /// semantic information. | ||
163 | pub fn analysis(&self) -> Analysis { | ||
164 | Analysis { | ||
165 | imp: self.imp.analysis(), | ||
166 | } | ||
167 | } | ||
168 | /// Applies changes to the current state of the world. If there are | ||
169 | /// outstanding snapshots, they will be canceled. | ||
170 | pub fn apply_change(&mut self, change: AnalysisChange) { | ||
171 | self.imp.apply_change(change) | ||
172 | } | ||
173 | } | ||
174 | |||
175 | #[derive(Debug)] | 151 | #[derive(Debug)] |
176 | pub struct SourceChange { | 152 | pub struct SourceChange { |
177 | pub label: String, | 153 | pub label: String, |
@@ -243,6 +219,27 @@ impl Query { | |||
243 | } | 219 | } |
244 | } | 220 | } |
245 | 221 | ||
222 | #[derive(Debug)] | ||
223 | pub struct NavigationTarget { | ||
224 | file_id: FileId, | ||
225 | symbol: FileSymbol, | ||
226 | } | ||
227 | |||
228 | impl NavigationTarget { | ||
229 | pub fn name(&self) -> SmolStr { | ||
230 | self.symbol.name.clone() | ||
231 | } | ||
232 | pub fn kind(&self) -> SyntaxKind { | ||
233 | self.symbol.kind | ||
234 | } | ||
235 | pub fn file_id(&self) -> FileId { | ||
236 | self.file_id | ||
237 | } | ||
238 | pub fn range(&self) -> TextRange { | ||
239 | self.symbol.node_range | ||
240 | } | ||
241 | } | ||
242 | |||
246 | /// Result of "goto def" query. | 243 | /// Result of "goto def" query. |
247 | #[derive(Debug)] | 244 | #[derive(Debug)] |
248 | pub struct ReferenceResolution { | 245 | pub struct ReferenceResolution { |
@@ -251,7 +248,7 @@ pub struct ReferenceResolution { | |||
251 | /// client where the reference was. | 248 | /// client where the reference was. |
252 | pub reference_range: TextRange, | 249 | pub reference_range: TextRange, |
253 | /// What this reference resolves to. | 250 | /// What this reference resolves to. |
254 | pub resolves_to: Vec<(FileId, FileSymbol)>, | 251 | pub resolves_to: Vec<NavigationTarget>, |
255 | } | 252 | } |
256 | 253 | ||
257 | impl ReferenceResolution { | 254 | impl ReferenceResolution { |
@@ -263,7 +260,28 @@ impl ReferenceResolution { | |||
263 | } | 260 | } |
264 | 261 | ||
265 | fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { | 262 | fn add_resolution(&mut self, file_id: FileId, symbol: FileSymbol) { |
266 | self.resolves_to.push((file_id, symbol)) | 263 | self.resolves_to.push(NavigationTarget { file_id, symbol }) |
264 | } | ||
265 | } | ||
266 | |||
267 | /// `AnalysisHost` stores the current state of the world. | ||
268 | #[derive(Debug, Default)] | ||
269 | pub struct AnalysisHost { | ||
270 | db: db::RootDatabase, | ||
271 | } | ||
272 | |||
273 | impl AnalysisHost { | ||
274 | /// Returns a snapshot of the current state, which you can query for | ||
275 | /// semantic information. | ||
276 | pub fn analysis(&self) -> Analysis { | ||
277 | Analysis { | ||
278 | db: self.db.snapshot(), | ||
279 | } | ||
280 | } | ||
281 | /// Applies changes to the current state of the world. If there are | ||
282 | /// outstanding snapshots, they will be canceled. | ||
283 | pub fn apply_change(&mut self, change: AnalysisChange) { | ||
284 | self.db.apply_change(change) | ||
267 | } | 285 | } |
268 | } | 286 | } |
269 | 287 | ||
@@ -273,112 +291,146 @@ impl ReferenceResolution { | |||
273 | /// `Analysis` are canceled (most method return `Err(Canceled)`). | 291 | /// `Analysis` are canceled (most method return `Err(Canceled)`). |
274 | #[derive(Debug)] | 292 | #[derive(Debug)] |
275 | pub struct Analysis { | 293 | pub struct Analysis { |
276 | pub(crate) imp: AnalysisImpl, | 294 | db: salsa::Snapshot<db::RootDatabase>, |
277 | } | 295 | } |
278 | 296 | ||
279 | impl Analysis { | 297 | impl Analysis { |
298 | /// Gets the text of the source file. | ||
280 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { | 299 | pub fn file_text(&self, file_id: FileId) -> Arc<String> { |
281 | self.imp.file_text(file_id) | 300 | self.db.file_text(file_id) |
282 | } | 301 | } |
302 | /// Gets the syntax tree of the file. | ||
283 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { | 303 | pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { |
284 | self.imp.file_syntax(file_id).clone() | 304 | self.db.source_file(file_id).clone() |
285 | } | 305 | } |
306 | /// Gets the file's `LineIndex`: data structure to convert between absolute | ||
307 | /// offsets and line/column representation. | ||
286 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { | 308 | pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { |
287 | self.imp.file_line_index(file_id) | 309 | self.db.file_lines(file_id) |
288 | } | 310 | } |
311 | /// Selects the next syntactic nodes encopasing the range. | ||
289 | pub fn extend_selection(&self, frange: FileRange) -> TextRange { | 312 | pub fn extend_selection(&self, frange: FileRange) -> TextRange { |
290 | extend_selection::extend_selection(&self.imp.db, frange) | 313 | extend_selection::extend_selection(&self.db, frange) |
291 | } | 314 | } |
315 | /// Returns position of the mathcing brace (all types of braces are | ||
316 | /// supported). | ||
292 | pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { | 317 | pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { |
293 | ra_editor::matching_brace(file, offset) | 318 | ra_editor::matching_brace(file, offset) |
294 | } | 319 | } |
320 | /// Returns a syntax tree represented as `String`, for debug purposes. | ||
321 | // FIXME: use a better name here. | ||
295 | pub fn syntax_tree(&self, file_id: FileId) -> String { | 322 | pub fn syntax_tree(&self, file_id: FileId) -> String { |
296 | let file = self.imp.file_syntax(file_id); | 323 | let file = self.db.source_file(file_id); |
297 | ra_editor::syntax_tree(&file) | 324 | ra_editor::syntax_tree(&file) |
298 | } | 325 | } |
326 | /// Returns an edit to remove all newlines in the range, cleaning up minor | ||
327 | /// stuff like trailing commas. | ||
299 | pub fn join_lines(&self, frange: FileRange) -> SourceChange { | 328 | pub fn join_lines(&self, frange: FileRange) -> SourceChange { |
300 | let file = self.imp.file_syntax(frange.file_id); | 329 | let file = self.db.source_file(frange.file_id); |
301 | SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range)) | 330 | SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range)) |
302 | } | 331 | } |
332 | /// Returns an edit which should be applied when opening a new line, fixing | ||
333 | /// up minor stuff like continuing the comment. | ||
303 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { | 334 | pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { |
304 | let file = self.imp.file_syntax(position.file_id); | 335 | let file = self.db.source_file(position.file_id); |
305 | let edit = ra_editor::on_enter(&file, position.offset)?; | 336 | let edit = ra_editor::on_enter(&file, position.offset)?; |
306 | let res = SourceChange::from_local_edit(position.file_id, edit); | 337 | Some(SourceChange::from_local_edit(position.file_id, edit)) |
307 | Some(res) | ||
308 | } | 338 | } |
339 | /// Returns an edit which should be applied after `=` was typed. Primaraly, | ||
340 | /// this works when adding `let =`. | ||
341 | // FIXME: use a snippet completion instead of this hack here. | ||
309 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { | 342 | pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> { |
310 | let file = self.imp.file_syntax(position.file_id); | 343 | let file = self.db.source_file(position.file_id); |
311 | Some(SourceChange::from_local_edit( | 344 | let edit = ra_editor::on_eq_typed(&file, position.offset)?; |
312 | position.file_id, | 345 | Some(SourceChange::from_local_edit(position.file_id, edit)) |
313 | ra_editor::on_eq_typed(&file, position.offset)?, | ||
314 | )) | ||
315 | } | 346 | } |
347 | /// Returns a tree representation of symbols in the file. Useful to draw a | ||
348 | /// file outline. | ||
316 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { | 349 | pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { |
317 | let file = self.imp.file_syntax(file_id); | 350 | let file = self.db.source_file(file_id); |
318 | ra_editor::file_structure(&file) | 351 | ra_editor::file_structure(&file) |
319 | } | 352 | } |
353 | /// Returns the set of folding ranges. | ||
320 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { | 354 | pub fn folding_ranges(&self, file_id: FileId) -> Vec<Fold> { |
321 | let file = self.imp.file_syntax(file_id); | 355 | let file = self.db.source_file(file_id); |
322 | ra_editor::folding_ranges(&file) | 356 | ra_editor::folding_ranges(&file) |
323 | } | 357 | } |
324 | pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 358 | /// Fuzzy searches for a symbol. |
325 | self.imp.world_symbols(query) | 359 | pub fn symbol_search(&self, query: Query) -> Cancelable<Vec<NavigationTarget>> { |
360 | let res = symbol_index::world_symbols(&*self.db, query)? | ||
361 | .into_iter() | ||
362 | .map(|(file_id, symbol)| NavigationTarget { file_id, symbol }) | ||
363 | .collect(); | ||
364 | Ok(res) | ||
326 | } | 365 | } |
366 | /// Resolves reference to definition, but does not gurantee correctness. | ||
327 | pub fn approximately_resolve_symbol( | 367 | pub fn approximately_resolve_symbol( |
328 | &self, | 368 | &self, |
329 | position: FilePosition, | 369 | position: FilePosition, |
330 | ) -> Cancelable<Option<ReferenceResolution>> { | 370 | ) -> Cancelable<Option<ReferenceResolution>> { |
331 | self.imp.approximately_resolve_symbol(position) | 371 | self.db.approximately_resolve_symbol(position) |
332 | } | 372 | } |
373 | /// Finds all usages of the reference at point. | ||
333 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { | 374 | pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { |
334 | self.imp.find_all_refs(position) | 375 | self.db.find_all_refs(position) |
335 | } | ||
336 | pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { | ||
337 | self.imp.doc_text_for(file_id, symbol) | ||
338 | } | 376 | } |
339 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> { | 377 | /// Returns documentation string for a given target. |
340 | self.imp.parent_module(position) | 378 | pub fn doc_text_for(&self, nav: NavigationTarget) -> Cancelable<Option<String>> { |
379 | self.db.doc_text_for(nav) | ||
341 | } | 380 | } |
342 | pub fn module_path(&self, position: FilePosition) -> Cancelable<Option<String>> { | 381 | /// Returns a `mod name;` declaration whihc created the current module. |
343 | self.imp.module_path(position) | 382 | pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<NavigationTarget>> { |
383 | self.db.parent_module(position) | ||
344 | } | 384 | } |
385 | /// Returns crates this file belongs too. | ||
345 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { | 386 | pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { |
346 | self.imp.crate_for(file_id) | 387 | self.db.crate_for(file_id) |
347 | } | 388 | } |
389 | /// Returns the root file of the given crate. | ||
348 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { | 390 | pub fn crate_root(&self, crate_id: CrateId) -> Cancelable<FileId> { |
349 | Ok(self.imp.crate_root(crate_id)) | 391 | Ok(self.db.crate_root(crate_id)) |
350 | } | 392 | } |
393 | /// Returns the set of possible targets to run for the current file. | ||
351 | pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { | 394 | pub fn runnables(&self, file_id: FileId) -> Cancelable<Vec<Runnable>> { |
352 | let file = self.imp.file_syntax(file_id); | 395 | runnables::runnables(&*self.db, file_id) |
353 | Ok(runnables::runnables(self, &file, file_id)) | ||
354 | } | 396 | } |
397 | /// Computes syntax highlighting for the given file. | ||
355 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { | 398 | pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { |
356 | syntax_highlighting::highlight(&*self.imp.db, file_id) | 399 | syntax_highlighting::highlight(&*self.db, file_id) |
357 | } | 400 | } |
401 | /// Computes completions at the given position. | ||
358 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { | 402 | pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { |
359 | self.imp.completions(position) | 403 | let completions = completion::completions(&self.db, position)?; |
404 | Ok(completions.map(|it| it.into())) | ||
360 | } | 405 | } |
406 | /// Computes assists (aks code actons aka intentions) for the given | ||
407 | /// position. | ||
361 | pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { | 408 | pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> { |
362 | Ok(self.imp.assists(frange)) | 409 | Ok(self.db.assists(frange)) |
363 | } | 410 | } |
411 | /// Computes the set of diagnostics for the given file. | ||
364 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { | 412 | pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { |
365 | self.imp.diagnostics(file_id) | 413 | self.db.diagnostics(file_id) |
366 | } | 414 | } |
415 | /// Computes parameter information for the given call expression. | ||
367 | pub fn resolve_callable( | 416 | pub fn resolve_callable( |
368 | &self, | 417 | &self, |
369 | position: FilePosition, | 418 | position: FilePosition, |
370 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { | 419 | ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { |
371 | self.imp.resolve_callable(position) | 420 | self.db.resolve_callable(position) |
372 | } | 421 | } |
422 | /// Computes the type of the expression at the given position. | ||
373 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { | 423 | pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> { |
374 | self.imp.type_of(frange) | 424 | self.db.type_of(frange) |
375 | } | 425 | } |
426 | /// Returns the edit required to rename reference at the position to the new | ||
427 | /// name. | ||
376 | pub fn rename( | 428 | pub fn rename( |
377 | &self, | 429 | &self, |
378 | position: FilePosition, | 430 | position: FilePosition, |
379 | new_name: &str, | 431 | new_name: &str, |
380 | ) -> Cancelable<Vec<SourceFileEdit>> { | 432 | ) -> Cancelable<Vec<SourceFileEdit>> { |
381 | self.imp.rename(position, new_name) | 433 | self.db.rename(position, new_name) |
382 | } | 434 | } |
383 | } | 435 | } |
384 | 436 | ||
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs deleted file mode 100644 index b9feb7fad..000000000 --- a/crates/ra_analysis/src/macros.rs +++ /dev/null | |||
@@ -1,75 +0,0 @@ | |||
1 | /// Begining of macro expansion. | ||
2 | /// | ||
3 | /// This code should be moved out of ra_analysis into hir (?) ideally. | ||
4 | use ra_syntax::{ast, AstNode, SourceFileNode, TextRange}; | ||
5 | |||
6 | use crate::{db::RootDatabase, FileId}; | ||
7 | |||
8 | pub(crate) fn expand( | ||
9 | _db: &RootDatabase, | ||
10 | _file_id: FileId, | ||
11 | macro_call: ast::MacroCall, | ||
12 | ) -> Option<MacroExpansion> { | ||
13 | let path = macro_call.path()?; | ||
14 | if path.qualifier().is_some() { | ||
15 | return None; | ||
16 | } | ||
17 | let name_ref = path.segment()?.name_ref()?; | ||
18 | if name_ref.text() != "ctry" { | ||
19 | return None; | ||
20 | } | ||
21 | |||
22 | let arg = macro_call.token_tree()?; | ||
23 | let text = format!( | ||
24 | r" | ||
25 | fn dummy() {{ | ||
26 | match {} {{ | ||
27 | None => return Ok(None), | ||
28 | Some(it) => it, | ||
29 | }} | ||
30 | }}", | ||
31 | arg.syntax().text() | ||
32 | ); | ||
33 | let file = SourceFileNode::parse(&text); | ||
34 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
35 | let match_arg = match_expr.expr()?; | ||
36 | let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())]; | ||
37 | let res = MacroExpansion { | ||
38 | source_file: file, | ||
39 | ranges_map, | ||
40 | }; | ||
41 | Some(res) | ||
42 | } | ||
43 | |||
44 | pub(crate) struct MacroExpansion { | ||
45 | pub(crate) source_file: SourceFileNode, | ||
46 | pub(crate) ranges_map: Vec<(TextRange, TextRange)>, | ||
47 | } | ||
48 | |||
49 | impl MacroExpansion { | ||
50 | pub(crate) fn source_file(&self) -> &SourceFileNode { | ||
51 | &self.source_file | ||
52 | } | ||
53 | pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
54 | for (s_range, t_range) in self.ranges_map.iter() { | ||
55 | if tgt_range.is_subrange(&t_range) { | ||
56 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
57 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
58 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
59 | return Some(src_range); | ||
60 | } | ||
61 | } | ||
62 | None | ||
63 | } | ||
64 | pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
65 | for (s_range, t_range) in self.ranges_map.iter() { | ||
66 | if src_range.is_subrange(&s_range) { | ||
67 | let src_at_zero_range = src_range - src_range.start(); | ||
68 | let src_range_offset = src_range.start() - s_range.start(); | ||
69 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
70 | return Some(src_range); | ||
71 | } | ||
72 | } | ||
73 | None | ||
74 | } | ||
75 | } | ||
diff --git a/crates/ra_analysis/src/runnables.rs b/crates/ra_analysis/src/runnables.rs index 61ca0930a..474267605 100644 --- a/crates/ra_analysis/src/runnables.rs +++ b/crates/ra_analysis/src/runnables.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use itertools::Itertools; | ||
1 | use ra_syntax::{ | 2 | use ra_syntax::{ |
2 | ast::{self, AstNode, NameOwner, ModuleItemOwner}, | 3 | ast::{self, AstNode, NameOwner, ModuleItemOwner}, |
3 | SourceFileNode, TextRange, SyntaxNodeRef, | 4 | TextRange, SyntaxNodeRef, |
4 | TextUnit, | ||
5 | }; | ||
6 | use crate::{ | ||
7 | Analysis, FileId, FilePosition | ||
8 | }; | 5 | }; |
6 | use ra_db::{Cancelable, SyntaxDatabase}; | ||
7 | |||
8 | use crate::{db::RootDatabase, FileId}; | ||
9 | 9 | ||
10 | #[derive(Debug)] | 10 | #[derive(Debug)] |
11 | pub struct Runnable { | 11 | pub struct Runnable { |
@@ -20,53 +20,67 @@ pub enum RunnableKind { | |||
20 | Bin, | 20 | Bin, |
21 | } | 21 | } |
22 | 22 | ||
23 | pub fn runnables( | 23 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Runnable>> { |
24 | analysis: &Analysis, | 24 | let source_file = db.source_file(file_id); |
25 | file_node: &SourceFileNode, | 25 | let res = source_file |
26 | file_id: FileId, | ||
27 | ) -> Vec<Runnable> { | ||
28 | file_node | ||
29 | .syntax() | 26 | .syntax() |
30 | .descendants() | 27 | .descendants() |
31 | .filter_map(|i| runnable(analysis, i, file_id)) | 28 | .filter_map(|i| runnable(db, file_id, i)) |
32 | .collect() | 29 | .collect(); |
30 | Ok(res) | ||
33 | } | 31 | } |
34 | 32 | ||
35 | fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> { | 33 | fn runnable(db: &RootDatabase, file_id: FileId, item: SyntaxNodeRef) -> Option<Runnable> { |
36 | if let Some(f) = ast::FnDef::cast(item) { | 34 | if let Some(fn_def) = ast::FnDef::cast(item) { |
37 | let name = f.name()?.text(); | 35 | runnable_fn(fn_def) |
38 | let kind = if name == "main" { | ||
39 | RunnableKind::Bin | ||
40 | } else if f.has_atom_attr("test") { | ||
41 | RunnableKind::Test { | ||
42 | name: name.to_string(), | ||
43 | } | ||
44 | } else { | ||
45 | return None; | ||
46 | }; | ||
47 | Some(Runnable { | ||
48 | range: f.syntax().range(), | ||
49 | kind, | ||
50 | }) | ||
51 | } else if let Some(m) = ast::Module::cast(item) { | 36 | } else if let Some(m) = ast::Module::cast(item) { |
52 | if m.item_list()? | 37 | runnable_mod(db, file_id, m) |
53 | .items() | ||
54 | .map(ast::ModuleItem::syntax) | ||
55 | .filter_map(ast::FnDef::cast) | ||
56 | .any(|f| f.has_atom_attr("test")) | ||
57 | { | ||
58 | let postition = FilePosition { | ||
59 | file_id, | ||
60 | offset: m.syntax().range().start() + TextUnit::from_usize(1), | ||
61 | }; | ||
62 | analysis.module_path(postition).ok()?.map(|path| Runnable { | ||
63 | range: m.syntax().range(), | ||
64 | kind: RunnableKind::TestMod { path }, | ||
65 | }) | ||
66 | } else { | ||
67 | None | ||
68 | } | ||
69 | } else { | 38 | } else { |
70 | None | 39 | None |
71 | } | 40 | } |
72 | } | 41 | } |
42 | |||
43 | fn runnable_fn(fn_def: ast::FnDef) -> Option<Runnable> { | ||
44 | let name = fn_def.name()?.text(); | ||
45 | let kind = if name == "main" { | ||
46 | RunnableKind::Bin | ||
47 | } else if fn_def.has_atom_attr("test") { | ||
48 | RunnableKind::Test { | ||
49 | name: name.to_string(), | ||
50 | } | ||
51 | } else { | ||
52 | return None; | ||
53 | }; | ||
54 | Some(Runnable { | ||
55 | range: fn_def.syntax().range(), | ||
56 | kind, | ||
57 | }) | ||
58 | } | ||
59 | |||
60 | fn runnable_mod(db: &RootDatabase, file_id: FileId, module: ast::Module) -> Option<Runnable> { | ||
61 | let has_test_function = module | ||
62 | .item_list()? | ||
63 | .items() | ||
64 | .filter_map(|it| match it { | ||
65 | ast::ModuleItem::FnDef(it) => Some(it), | ||
66 | _ => None, | ||
67 | }) | ||
68 | .any(|f| f.has_atom_attr("test")); | ||
69 | if !has_test_function { | ||
70 | return None; | ||
71 | } | ||
72 | let range = module.syntax().range(); | ||
73 | let module = | ||
74 | hir::source_binder::module_from_child_node(db, file_id, module.syntax()).ok()??; | ||
75 | let path = module | ||
76 | .path_to_root() | ||
77 | .into_iter() | ||
78 | .rev() | ||
79 | .into_iter() | ||
80 | .filter_map(|it| it.name().map(Clone::clone)) | ||
81 | .join("::"); | ||
82 | Some(Runnable { | ||
83 | range, | ||
84 | kind: RunnableKind::TestMod { path }, | ||
85 | }) | ||
86 | } | ||
diff --git a/crates/ra_analysis/src/symbol_index.rs b/crates/ra_analysis/src/symbol_index.rs index e5bdf0aa1..ddcf3d052 100644 --- a/crates/ra_analysis/src/symbol_index.rs +++ b/crates/ra_analysis/src/symbol_index.rs | |||
@@ -4,17 +4,19 @@ use std::{ | |||
4 | }; | 4 | }; |
5 | 5 | ||
6 | use fst::{self, Streamer}; | 6 | use fst::{self, Streamer}; |
7 | use ra_editor::{self, FileSymbol}; | ||
8 | use ra_syntax::{ | 7 | use ra_syntax::{ |
9 | SourceFileNode, | 8 | AstNode, SyntaxNodeRef, SourceFileNode, SmolStr, TextRange, |
9 | algo::visit::{visitor, Visitor}, | ||
10 | SyntaxKind::{self, *}, | 10 | SyntaxKind::{self, *}, |
11 | ast::{self, NameOwner, DocCommentsOwner}, | ||
11 | }; | 12 | }; |
12 | use ra_db::{SyntaxDatabase, SourceRootId}; | 13 | use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase}; |
14 | use salsa::ParallelDatabase; | ||
13 | use rayon::prelude::*; | 15 | use rayon::prelude::*; |
14 | 16 | ||
15 | use crate::{ | 17 | use crate::{ |
16 | Cancelable, | 18 | Cancelable, FileId, Query, |
17 | FileId, Query, | 19 | db::RootDatabase, |
18 | }; | 20 | }; |
19 | 21 | ||
20 | salsa::query_group! { | 22 | salsa::query_group! { |
@@ -35,6 +37,41 @@ fn file_symbols(db: &impl SyntaxDatabase, file_id: FileId) -> Cancelable<Arc<Sym | |||
35 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) | 37 | Ok(Arc::new(SymbolIndex::for_file(file_id, syntax))) |
36 | } | 38 | } |
37 | 39 | ||
40 | pub(crate) fn world_symbols( | ||
41 | db: &RootDatabase, | ||
42 | query: Query, | ||
43 | ) -> Cancelable<Vec<(FileId, FileSymbol)>> { | ||
44 | /// Need to wrap Snapshot to provide `Clone` impl for `map_with` | ||
45 | struct Snap(salsa::Snapshot<RootDatabase>); | ||
46 | impl Clone for Snap { | ||
47 | fn clone(&self) -> Snap { | ||
48 | Snap(self.0.snapshot()) | ||
49 | } | ||
50 | } | ||
51 | |||
52 | let buf: Vec<Arc<SymbolIndex>> = if query.libs { | ||
53 | let snap = Snap(db.snapshot()); | ||
54 | db.library_roots() | ||
55 | .par_iter() | ||
56 | .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id)) | ||
57 | .collect() | ||
58 | } else { | ||
59 | let mut files = Vec::new(); | ||
60 | for &root in db.local_roots().iter() { | ||
61 | let sr = db.source_root(root); | ||
62 | files.extend(sr.files.values().map(|&it| it)) | ||
63 | } | ||
64 | |||
65 | let snap = Snap(db.snapshot()); | ||
66 | files | ||
67 | .par_iter() | ||
68 | .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) | ||
69 | .filter_map(|it| it.ok()) | ||
70 | .collect() | ||
71 | }; | ||
72 | Ok(query.search(&buf)) | ||
73 | } | ||
74 | |||
38 | #[derive(Default, Debug)] | 75 | #[derive(Default, Debug)] |
39 | pub(crate) struct SymbolIndex { | 76 | pub(crate) struct SymbolIndex { |
40 | symbols: Vec<(FileId, FileSymbol)>, | 77 | symbols: Vec<(FileId, FileSymbol)>, |
@@ -65,8 +102,9 @@ impl SymbolIndex { | |||
65 | ) -> SymbolIndex { | 102 | ) -> SymbolIndex { |
66 | let mut symbols = files | 103 | let mut symbols = files |
67 | .flat_map(|(file_id, file)| { | 104 | .flat_map(|(file_id, file)| { |
68 | ra_editor::file_symbols(&file) | 105 | file.syntax() |
69 | .into_iter() | 106 | .descendants() |
107 | .filter_map(to_symbol) | ||
70 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) | 108 | .map(move |symbol| (symbol.name.as_str().to_lowercase(), (file_id, symbol))) |
71 | .collect::<Vec<_>>() | 109 | .collect::<Vec<_>>() |
72 | }) | 110 | }) |
@@ -121,3 +159,116 @@ fn is_type(kind: SyntaxKind) -> bool { | |||
121 | _ => false, | 159 | _ => false, |
122 | } | 160 | } |
123 | } | 161 | } |
162 | |||
163 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
164 | pub(crate) struct FileSymbol { | ||
165 | pub(crate) name: SmolStr, | ||
166 | pub(crate) node_range: TextRange, | ||
167 | pub(crate) kind: SyntaxKind, | ||
168 | } | ||
169 | |||
170 | impl FileSymbol { | ||
171 | pub(crate) fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
172 | file.syntax() | ||
173 | .descendants() | ||
174 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
175 | .filter_map(|node: SyntaxNodeRef| { | ||
176 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
177 | let comments = node.doc_comment_text(); | ||
178 | if comments.is_empty() { | ||
179 | None | ||
180 | } else { | ||
181 | Some(comments) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | visitor() | ||
186 | .visit(doc_comments::<ast::FnDef>) | ||
187 | .visit(doc_comments::<ast::StructDef>) | ||
188 | .visit(doc_comments::<ast::EnumDef>) | ||
189 | .visit(doc_comments::<ast::TraitDef>) | ||
190 | .visit(doc_comments::<ast::Module>) | ||
191 | .visit(doc_comments::<ast::TypeDef>) | ||
192 | .visit(doc_comments::<ast::ConstDef>) | ||
193 | .visit(doc_comments::<ast::StaticDef>) | ||
194 | .accept(node)? | ||
195 | }) | ||
196 | .nth(0) | ||
197 | } | ||
198 | /// Get a description of this node. | ||
199 | /// | ||
200 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
201 | pub(crate) fn description(&self, file: &SourceFileNode) -> Option<String> { | ||
202 | // TODO: After type inference is done, add type information to improve the output | ||
203 | file.syntax() | ||
204 | .descendants() | ||
205 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
206 | .filter_map(|node: SyntaxNodeRef| { | ||
207 | // TODO: Refactor to be have less repetition | ||
208 | visitor() | ||
209 | .visit(|node: ast::FnDef| { | ||
210 | let mut string = "fn ".to_string(); | ||
211 | node.name()?.syntax().text().push_to(&mut string); | ||
212 | Some(string) | ||
213 | }) | ||
214 | .visit(|node: ast::StructDef| { | ||
215 | let mut string = "struct ".to_string(); | ||
216 | node.name()?.syntax().text().push_to(&mut string); | ||
217 | Some(string) | ||
218 | }) | ||
219 | .visit(|node: ast::EnumDef| { | ||
220 | let mut string = "enum ".to_string(); | ||
221 | node.name()?.syntax().text().push_to(&mut string); | ||
222 | Some(string) | ||
223 | }) | ||
224 | .visit(|node: ast::TraitDef| { | ||
225 | let mut string = "trait ".to_string(); | ||
226 | node.name()?.syntax().text().push_to(&mut string); | ||
227 | Some(string) | ||
228 | }) | ||
229 | .visit(|node: ast::Module| { | ||
230 | let mut string = "mod ".to_string(); | ||
231 | node.name()?.syntax().text().push_to(&mut string); | ||
232 | Some(string) | ||
233 | }) | ||
234 | .visit(|node: ast::TypeDef| { | ||
235 | let mut string = "type ".to_string(); | ||
236 | node.name()?.syntax().text().push_to(&mut string); | ||
237 | Some(string) | ||
238 | }) | ||
239 | .visit(|node: ast::ConstDef| { | ||
240 | let mut string = "const ".to_string(); | ||
241 | node.name()?.syntax().text().push_to(&mut string); | ||
242 | Some(string) | ||
243 | }) | ||
244 | .visit(|node: ast::StaticDef| { | ||
245 | let mut string = "static ".to_string(); | ||
246 | node.name()?.syntax().text().push_to(&mut string); | ||
247 | Some(string) | ||
248 | }) | ||
249 | .accept(node)? | ||
250 | }) | ||
251 | .nth(0) | ||
252 | } | ||
253 | } | ||
254 | |||
255 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
256 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> { | ||
257 | let name = node.name()?; | ||
258 | Some(FileSymbol { | ||
259 | name: name.text(), | ||
260 | node_range: node.syntax().range(), | ||
261 | kind: node.syntax().kind(), | ||
262 | }) | ||
263 | } | ||
264 | visitor() | ||
265 | .visit(decl::<ast::FnDef>) | ||
266 | .visit(decl::<ast::StructDef>) | ||
267 | .visit(decl::<ast::EnumDef>) | ||
268 | .visit(decl::<ast::TraitDef>) | ||
269 | .visit(decl::<ast::Module>) | ||
270 | .visit(decl::<ast::TypeDef>) | ||
271 | .visit(decl::<ast::ConstDef>) | ||
272 | .visit(decl::<ast::StaticDef>) | ||
273 | .accept(node)? | ||
274 | } | ||
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs index 7e9139a74..ccea4aee3 100644 --- a/crates/ra_analysis/src/syntax_highlighting.rs +++ b/crates/ra_analysis/src/syntax_highlighting.rs | |||
@@ -15,13 +15,13 @@ pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<Hi | |||
15 | .descendants() | 15 | .descendants() |
16 | .filter_map(ast::MacroCall::cast) | 16 | .filter_map(ast::MacroCall::cast) |
17 | { | 17 | { |
18 | if let Some(exp) = crate::macros::expand(db, file_id, macro_call) { | 18 | if let Some((off, exp)) = hir::MacroDef::ast_expand(macro_call) { |
19 | let mapped_ranges = ra_editor::highlight(exp.source_file().syntax()) | 19 | let mapped_ranges = ra_editor::highlight(exp.syntax().borrowed()) |
20 | .into_iter() | 20 | .into_iter() |
21 | .filter_map(|r| { | 21 | .filter_map(|r| { |
22 | let mapped_range = exp.map_range_back(r.range)?; | 22 | let mapped_range = exp.map_range_back(r.range)?; |
23 | let res = HighlightedRange { | 23 | let res = HighlightedRange { |
24 | range: mapped_range, | 24 | range: mapped_range + off, |
25 | tag: r.tag, | 25 | tag: r.tag, |
26 | }; | 26 | }; |
27 | Some(res) | 27 | Some(res) |
@@ -44,7 +44,7 @@ mod tests { | |||
44 | fn main() { | 44 | fn main() { |
45 | ctry!({ let x = 92; x}); | 45 | ctry!({ let x = 92; x}); |
46 | } | 46 | } |
47 | ", | 47 | ", |
48 | ); | 48 | ); |
49 | let highlights = analysis.highlight(file_id).unwrap(); | 49 | let highlights = analysis.highlight(file_id).unwrap(); |
50 | assert_eq_dbg( | 50 | assert_eq_dbg( |
@@ -60,4 +60,26 @@ mod tests { | |||
60 | &highlights, | 60 | &highlights, |
61 | ) | 61 | ) |
62 | } | 62 | } |
63 | |||
64 | // FIXME: this test is not really necessary: artifact of the inital hacky | ||
65 | // macros implementation. | ||
66 | #[test] | ||
67 | fn highlight_query_group_macro() { | ||
68 | let (analysis, file_id) = single_file( | ||
69 | " | ||
70 | salsa::query_group! { | ||
71 | pub trait HirDatabase: SyntaxDatabase {} | ||
72 | } | ||
73 | ", | ||
74 | ); | ||
75 | let highlights = analysis.highlight(file_id).unwrap(); | ||
76 | assert_eq_dbg( | ||
77 | r#"[HighlightedRange { range: [20; 32), tag: "macro" }, | ||
78 | HighlightedRange { range: [13; 18), tag: "text" }, | ||
79 | HighlightedRange { range: [51; 54), tag: "keyword" }, | ||
80 | HighlightedRange { range: [55; 60), tag: "keyword" }, | ||
81 | HighlightedRange { range: [61; 72), tag: "function" }]"#, | ||
82 | &highlights, | ||
83 | ) | ||
84 | } | ||
63 | } | 85 | } |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index b61ead752..3045c2e78 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -25,7 +25,7 @@ fn approximate_resolve_works_in_items() { | |||
25 | assert_eq_dbg( | 25 | assert_eq_dbg( |
26 | r#"ReferenceResolution { | 26 | r#"ReferenceResolution { |
27 | reference_range: [23; 26), | 27 | reference_range: [23; 26), |
28 | resolves_to: [(FileId(1), FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF })] | 28 | resolves_to: [NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "Foo", node_range: [0; 11), kind: STRUCT_DEF } }] |
29 | }"#, | 29 | }"#, |
30 | &symbols, | 30 | &symbols, |
31 | ); | 31 | ); |
@@ -46,7 +46,7 @@ fn test_resolve_module() { | |||
46 | assert_eq_dbg( | 46 | assert_eq_dbg( |
47 | r#"ReferenceResolution { | 47 | r#"ReferenceResolution { |
48 | reference_range: [4; 7), | 48 | reference_range: [4; 7), |
49 | resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] | 49 | resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }] |
50 | }"#, | 50 | }"#, |
51 | &symbols, | 51 | &symbols, |
52 | ); | 52 | ); |
@@ -64,7 +64,7 @@ fn test_resolve_module() { | |||
64 | assert_eq_dbg( | 64 | assert_eq_dbg( |
65 | r#"ReferenceResolution { | 65 | r#"ReferenceResolution { |
66 | reference_range: [4; 7), | 66 | reference_range: [4; 7), |
67 | resolves_to: [(FileId(2), FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE })] | 67 | resolves_to: [NavigationTarget { file_id: FileId(2), symbol: FileSymbol { name: "foo", node_range: [0; 0), kind: MODULE } }] |
68 | }"#, | 68 | }"#, |
69 | &symbols, | 69 | &symbols, |
70 | ); | 70 | ); |
@@ -107,7 +107,7 @@ fn test_resolve_parent_module() { | |||
107 | ); | 107 | ); |
108 | let symbols = analysis.parent_module(pos).unwrap(); | 108 | let symbols = analysis.parent_module(pos).unwrap(); |
109 | assert_eq_dbg( | 109 | assert_eq_dbg( |
110 | r#"[(FileId(1), FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE })]"#, | 110 | r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "foo", node_range: [4; 7), kind: MODULE } }]"#, |
111 | &symbols, | 111 | &symbols, |
112 | ); | 112 | ); |
113 | } | 113 | } |
@@ -126,62 +126,12 @@ fn test_resolve_parent_module_for_inline() { | |||
126 | ); | 126 | ); |
127 | let symbols = analysis.parent_module(pos).unwrap(); | 127 | let symbols = analysis.parent_module(pos).unwrap(); |
128 | assert_eq_dbg( | 128 | assert_eq_dbg( |
129 | r#"[(FileId(1), FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE })]"#, | 129 | r#"[NavigationTarget { file_id: FileId(1), symbol: FileSymbol { name: "bar", node_range: [18; 21), kind: MODULE } }]"#, |
130 | &symbols, | 130 | &symbols, |
131 | ); | 131 | ); |
132 | } | 132 | } |
133 | 133 | ||
134 | #[test] | 134 | #[test] |
135 | fn test_path_one_layer() { | ||
136 | let (analysis, pos) = analysis_and_position( | ||
137 | " | ||
138 | //- /lib.rs | ||
139 | mod foo; | ||
140 | //- /foo/mod.rs | ||
141 | mod bla; | ||
142 | //- /foo/bla.rs | ||
143 | <|> //empty | ||
144 | ", | ||
145 | ); | ||
146 | let symbols = analysis.module_path(pos).unwrap().unwrap(); | ||
147 | assert_eq!("foo::bla", &symbols); | ||
148 | } | ||
149 | |||
150 | #[test] | ||
151 | fn test_path_two_layer() { | ||
152 | let (analysis, pos) = analysis_and_position( | ||
153 | " | ||
154 | //- /lib.rs | ||
155 | mod foo; | ||
156 | //- /foo/mod.rs | ||
157 | mod bla; | ||
158 | //- /foo/bla/mod.rs | ||
159 | mod more; | ||
160 | //- /foo/bla/more.rs | ||
161 | <|> //empty | ||
162 | ", | ||
163 | ); | ||
164 | let symbols = analysis.module_path(pos).unwrap().unwrap(); | ||
165 | assert_eq!("foo::bla::more", &symbols); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn test_path_in_file_mod() { | ||
170 | let (analysis, pos) = analysis_and_position( | ||
171 | " | ||
172 | //- /lib.rs | ||
173 | mod foo; | ||
174 | //- /foo.rs | ||
175 | mod bar { | ||
176 | <|> //empty | ||
177 | } | ||
178 | ", | ||
179 | ); | ||
180 | let symbols = analysis.module_path(pos).unwrap().unwrap(); | ||
181 | assert_eq!("foo::bar", &symbols); | ||
182 | } | ||
183 | |||
184 | #[test] | ||
185 | fn test_resolve_crate_root() { | 135 | fn test_resolve_crate_root() { |
186 | let mock = MockAnalysis::with_files( | 136 | let mock = MockAnalysis::with_files( |
187 | " | 137 | " |
diff --git a/crates/ra_editor/src/extend_selection.rs b/crates/ra_editor/src/extend_selection.rs index bf0727dde..7a423852b 100644 --- a/crates/ra_editor/src/extend_selection.rs +++ b/crates/ra_editor/src/extend_selection.rs | |||
@@ -6,6 +6,7 @@ use ra_syntax::{ | |||
6 | }; | 6 | }; |
7 | 7 | ||
8 | pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { | 8 | pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { |
9 | let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING]; | ||
9 | if range.is_empty() { | 10 | if range.is_empty() { |
10 | let offset = range.start(); | 11 | let offset = range.start(); |
11 | let mut leaves = find_leaf_at_offset(root, offset); | 12 | let mut leaves = find_leaf_at_offset(root, offset); |
@@ -15,8 +16,8 @@ pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRan | |||
15 | let leaf_range = match leaves { | 16 | let leaf_range = match leaves { |
16 | LeafAtOffset::None => return None, | 17 | LeafAtOffset::None => return None, |
17 | LeafAtOffset::Single(l) => { | 18 | LeafAtOffset::Single(l) => { |
18 | if l.kind() == COMMENT { | 19 | if string_kinds.contains(&l.kind()) { |
19 | extend_single_word_in_comment(l, offset).unwrap_or_else(|| l.range()) | 20 | extend_single_word_in_comment_or_string(l, offset).unwrap_or_else(|| l.range()) |
20 | } else { | 21 | } else { |
21 | l.range() | 22 | l.range() |
22 | } | 23 | } |
@@ -26,7 +27,7 @@ pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRan | |||
26 | return Some(leaf_range); | 27 | return Some(leaf_range); |
27 | }; | 28 | }; |
28 | let node = find_covering_node(root, range); | 29 | let node = find_covering_node(root, range); |
29 | if node.kind() == COMMENT && range == node.range() { | 30 | if string_kinds.contains(&node.kind()) && range == node.range() { |
30 | if let Some(range) = extend_comments(node) { | 31 | if let Some(range) = extend_comments(node) { |
31 | return Some(range); | 32 | return Some(range); |
32 | } | 33 | } |
@@ -38,7 +39,10 @@ pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRan | |||
38 | } | 39 | } |
39 | } | 40 | } |
40 | 41 | ||
41 | fn extend_single_word_in_comment(leaf: SyntaxNodeRef, offset: TextUnit) -> Option<TextRange> { | 42 | fn extend_single_word_in_comment_or_string( |
43 | leaf: SyntaxNodeRef, | ||
44 | offset: TextUnit, | ||
45 | ) -> Option<TextRange> { | ||
42 | let text: &str = leaf.leaf_text()?; | 46 | let text: &str = leaf.leaf_text()?; |
43 | let cursor_position: u32 = (offset - leaf.range().start()).into(); | 47 | let cursor_position: u32 = (offset - leaf.range().start()).into(); |
44 | 48 | ||
@@ -262,4 +266,16 @@ impl S { | |||
262 | &["hello", "// hello world"], | 266 | &["hello", "// hello world"], |
263 | ); | 267 | ); |
264 | } | 268 | } |
269 | |||
270 | #[test] | ||
271 | fn test_extend_selection_string() { | ||
272 | do_check( | ||
273 | r#" | ||
274 | fn bar(){} | ||
275 | |||
276 | " fn f<|>oo() {" | ||
277 | "#, | ||
278 | &["foo", "\" fn foo() {\""], | ||
279 | ); | ||
280 | } | ||
265 | } | 281 | } |
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs index b03f9ea54..bfc745e58 100644 --- a/crates/ra_editor/src/lib.rs +++ b/crates/ra_editor/src/lib.rs | |||
@@ -3,7 +3,7 @@ mod extend_selection; | |||
3 | mod folding_ranges; | 3 | mod folding_ranges; |
4 | mod line_index; | 4 | mod line_index; |
5 | mod line_index_utils; | 5 | mod line_index_utils; |
6 | mod symbols; | 6 | mod structure; |
7 | #[cfg(test)] | 7 | #[cfg(test)] |
8 | mod test_utils; | 8 | mod test_utils; |
9 | mod typing; | 9 | mod typing; |
@@ -15,7 +15,7 @@ pub use self::{ | |||
15 | folding_ranges::{folding_ranges, Fold, FoldKind}, | 15 | folding_ranges::{folding_ranges, Fold, FoldKind}, |
16 | line_index::{LineCol, LineIndex}, | 16 | line_index::{LineCol, LineIndex}, |
17 | line_index_utils::translate_offset_with_edit, | 17 | line_index_utils::translate_offset_with_edit, |
18 | symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, | 18 | structure::{file_structure, StructureNode}, |
19 | typing::{join_lines, on_enter, on_eq_typed}, | 19 | typing::{join_lines, on_enter, on_eq_typed}, |
20 | diagnostics::diagnostics | 20 | diagnostics::diagnostics |
21 | }; | 21 | }; |
diff --git a/crates/ra_editor/src/structure.rs b/crates/ra_editor/src/structure.rs new file mode 100644 index 000000000..2292b1ddf --- /dev/null +++ b/crates/ra_editor/src/structure.rs | |||
@@ -0,0 +1,129 @@ | |||
1 | use crate::TextRange; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, NameOwner}, | ||
6 | AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone)] | ||
10 | pub struct StructureNode { | ||
11 | pub parent: Option<usize>, | ||
12 | pub label: String, | ||
13 | pub navigation_range: TextRange, | ||
14 | pub node_range: TextRange, | ||
15 | pub kind: SyntaxKind, | ||
16 | } | ||
17 | |||
18 | pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> { | ||
19 | let mut res = Vec::new(); | ||
20 | let mut stack = Vec::new(); | ||
21 | |||
22 | for event in file.syntax().preorder() { | ||
23 | match event { | ||
24 | WalkEvent::Enter(node) => { | ||
25 | if let Some(mut symbol) = structure_node(node) { | ||
26 | symbol.parent = stack.last().map(|&n| n); | ||
27 | stack.push(res.len()); | ||
28 | res.push(symbol); | ||
29 | } | ||
30 | } | ||
31 | WalkEvent::Leave(node) => { | ||
32 | if structure_node(node).is_some() { | ||
33 | stack.pop().unwrap(); | ||
34 | } | ||
35 | } | ||
36 | } | ||
37 | } | ||
38 | res | ||
39 | } | ||
40 | |||
41 | fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> { | ||
42 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> { | ||
43 | let name = node.name()?; | ||
44 | Some(StructureNode { | ||
45 | parent: None, | ||
46 | label: name.text().to_string(), | ||
47 | navigation_range: name.syntax().range(), | ||
48 | node_range: node.syntax().range(), | ||
49 | kind: node.syntax().kind(), | ||
50 | }) | ||
51 | } | ||
52 | |||
53 | visitor() | ||
54 | .visit(decl::<ast::FnDef>) | ||
55 | .visit(decl::<ast::StructDef>) | ||
56 | .visit(decl::<ast::NamedFieldDef>) | ||
57 | .visit(decl::<ast::EnumDef>) | ||
58 | .visit(decl::<ast::TraitDef>) | ||
59 | .visit(decl::<ast::Module>) | ||
60 | .visit(decl::<ast::TypeDef>) | ||
61 | .visit(decl::<ast::ConstDef>) | ||
62 | .visit(decl::<ast::StaticDef>) | ||
63 | .visit(|im: ast::ImplItem| { | ||
64 | let target_type = im.target_type()?; | ||
65 | let target_trait = im.target_trait(); | ||
66 | let label = match target_trait { | ||
67 | None => format!("impl {}", target_type.syntax().text()), | ||
68 | Some(t) => format!( | ||
69 | "impl {} for {}", | ||
70 | t.syntax().text(), | ||
71 | target_type.syntax().text(), | ||
72 | ), | ||
73 | }; | ||
74 | |||
75 | let node = StructureNode { | ||
76 | parent: None, | ||
77 | label, | ||
78 | navigation_range: target_type.syntax().range(), | ||
79 | node_range: im.syntax().range(), | ||
80 | kind: im.syntax().kind(), | ||
81 | }; | ||
82 | Some(node) | ||
83 | }) | ||
84 | .accept(node)? | ||
85 | } | ||
86 | |||
87 | #[cfg(test)] | ||
88 | mod tests { | ||
89 | use super::*; | ||
90 | use test_utils::assert_eq_dbg; | ||
91 | |||
92 | #[test] | ||
93 | fn test_file_structure() { | ||
94 | let file = SourceFileNode::parse( | ||
95 | r#" | ||
96 | struct Foo { | ||
97 | x: i32 | ||
98 | } | ||
99 | |||
100 | mod m { | ||
101 | fn bar() {} | ||
102 | } | ||
103 | |||
104 | enum E { X, Y(i32) } | ||
105 | type T = (); | ||
106 | static S: i32 = 92; | ||
107 | const C: i32 = 92; | ||
108 | |||
109 | impl E {} | ||
110 | |||
111 | impl fmt::Debug for E {} | ||
112 | "#, | ||
113 | ); | ||
114 | let structure = file_structure(&file); | ||
115 | assert_eq_dbg( | ||
116 | r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, | ||
117 | StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF }, | ||
118 | StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE }, | ||
119 | StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF }, | ||
120 | StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF }, | ||
121 | StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF }, | ||
122 | StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF }, | ||
123 | StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF }, | ||
124 | StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM }, | ||
125 | StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#, | ||
126 | &structure, | ||
127 | ) | ||
128 | } | ||
129 | } | ||
diff --git a/crates/ra_editor/src/symbols.rs b/crates/ra_editor/src/symbols.rs deleted file mode 100644 index 9e25decfb..000000000 --- a/crates/ra_editor/src/symbols.rs +++ /dev/null | |||
@@ -1,246 +0,0 @@ | |||
1 | use crate::TextRange; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | algo::visit::{visitor, Visitor}, | ||
5 | ast::{self, DocCommentsOwner, NameOwner}, | ||
6 | AstNode, SourceFileNode, SmolStr, SyntaxKind, SyntaxNodeRef, WalkEvent, | ||
7 | }; | ||
8 | |||
9 | #[derive(Debug, Clone)] | ||
10 | pub struct StructureNode { | ||
11 | pub parent: Option<usize>, | ||
12 | pub label: String, | ||
13 | pub navigation_range: TextRange, | ||
14 | pub node_range: TextRange, | ||
15 | pub kind: SyntaxKind, | ||
16 | } | ||
17 | |||
18 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
19 | pub struct FileSymbol { | ||
20 | pub name: SmolStr, | ||
21 | pub node_range: TextRange, | ||
22 | pub kind: SyntaxKind, | ||
23 | } | ||
24 | |||
25 | impl FileSymbol { | ||
26 | pub fn docs(&self, file: &SourceFileNode) -> Option<String> { | ||
27 | file.syntax() | ||
28 | .descendants() | ||
29 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
30 | .filter_map(|node: SyntaxNodeRef| { | ||
31 | fn doc_comments<'a, N: DocCommentsOwner<'a>>(node: N) -> Option<String> { | ||
32 | let comments = node.doc_comment_text(); | ||
33 | if comments.is_empty() { | ||
34 | None | ||
35 | } else { | ||
36 | Some(comments) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | visitor() | ||
41 | .visit(doc_comments::<ast::FnDef>) | ||
42 | .visit(doc_comments::<ast::StructDef>) | ||
43 | .visit(doc_comments::<ast::EnumDef>) | ||
44 | .visit(doc_comments::<ast::TraitDef>) | ||
45 | .visit(doc_comments::<ast::Module>) | ||
46 | .visit(doc_comments::<ast::TypeDef>) | ||
47 | .visit(doc_comments::<ast::ConstDef>) | ||
48 | .visit(doc_comments::<ast::StaticDef>) | ||
49 | .accept(node)? | ||
50 | }) | ||
51 | .nth(0) | ||
52 | } | ||
53 | /// Get a description of this node. | ||
54 | /// | ||
55 | /// e.g. `struct Name`, `enum Name`, `fn Name` | ||
56 | pub fn description(&self, file: &SourceFileNode) -> Option<String> { | ||
57 | // TODO: After type inference is done, add type information to improve the output | ||
58 | file.syntax() | ||
59 | .descendants() | ||
60 | .filter(|node| node.kind() == self.kind && node.range() == self.node_range) | ||
61 | .filter_map(|node: SyntaxNodeRef| { | ||
62 | // TODO: Refactor to be have less repetition | ||
63 | visitor() | ||
64 | .visit(|node: ast::FnDef| { | ||
65 | let mut string = "fn ".to_string(); | ||
66 | node.name()?.syntax().text().push_to(&mut string); | ||
67 | Some(string) | ||
68 | }) | ||
69 | .visit(|node: ast::StructDef| { | ||
70 | let mut string = "struct ".to_string(); | ||
71 | node.name()?.syntax().text().push_to(&mut string); | ||
72 | Some(string) | ||
73 | }) | ||
74 | .visit(|node: ast::EnumDef| { | ||
75 | let mut string = "enum ".to_string(); | ||
76 | node.name()?.syntax().text().push_to(&mut string); | ||
77 | Some(string) | ||
78 | }) | ||
79 | .visit(|node: ast::TraitDef| { | ||
80 | let mut string = "trait ".to_string(); | ||
81 | node.name()?.syntax().text().push_to(&mut string); | ||
82 | Some(string) | ||
83 | }) | ||
84 | .visit(|node: ast::Module| { | ||
85 | let mut string = "mod ".to_string(); | ||
86 | node.name()?.syntax().text().push_to(&mut string); | ||
87 | Some(string) | ||
88 | }) | ||
89 | .visit(|node: ast::TypeDef| { | ||
90 | let mut string = "type ".to_string(); | ||
91 | node.name()?.syntax().text().push_to(&mut string); | ||
92 | Some(string) | ||
93 | }) | ||
94 | .visit(|node: ast::ConstDef| { | ||
95 | let mut string = "const ".to_string(); | ||
96 | node.name()?.syntax().text().push_to(&mut string); | ||
97 | Some(string) | ||
98 | }) | ||
99 | .visit(|node: ast::StaticDef| { | ||
100 | let mut string = "static ".to_string(); | ||
101 | node.name()?.syntax().text().push_to(&mut string); | ||
102 | Some(string) | ||
103 | }) | ||
104 | .accept(node)? | ||
105 | }) | ||
106 | .nth(0) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> { | ||
111 | file.syntax().descendants().filter_map(to_symbol).collect() | ||
112 | } | ||
113 | |||
114 | fn to_symbol(node: SyntaxNodeRef) -> Option<FileSymbol> { | ||
115 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<FileSymbol> { | ||
116 | let name = node.name()?; | ||
117 | Some(FileSymbol { | ||
118 | name: name.text(), | ||
119 | node_range: node.syntax().range(), | ||
120 | kind: node.syntax().kind(), | ||
121 | }) | ||
122 | } | ||
123 | visitor() | ||
124 | .visit(decl::<ast::FnDef>) | ||
125 | .visit(decl::<ast::StructDef>) | ||
126 | .visit(decl::<ast::EnumDef>) | ||
127 | .visit(decl::<ast::TraitDef>) | ||
128 | .visit(decl::<ast::Module>) | ||
129 | .visit(decl::<ast::TypeDef>) | ||
130 | .visit(decl::<ast::ConstDef>) | ||
131 | .visit(decl::<ast::StaticDef>) | ||
132 | .accept(node)? | ||
133 | } | ||
134 | |||
135 | pub fn file_structure(file: &SourceFileNode) -> Vec<StructureNode> { | ||
136 | let mut res = Vec::new(); | ||
137 | let mut stack = Vec::new(); | ||
138 | |||
139 | for event in file.syntax().preorder() { | ||
140 | match event { | ||
141 | WalkEvent::Enter(node) => { | ||
142 | if let Some(mut symbol) = structure_node(node) { | ||
143 | symbol.parent = stack.last().map(|&n| n); | ||
144 | stack.push(res.len()); | ||
145 | res.push(symbol); | ||
146 | } | ||
147 | } | ||
148 | WalkEvent::Leave(node) => { | ||
149 | if structure_node(node).is_some() { | ||
150 | stack.pop().unwrap(); | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | res | ||
156 | } | ||
157 | |||
158 | fn structure_node(node: SyntaxNodeRef) -> Option<StructureNode> { | ||
159 | fn decl<'a, N: NameOwner<'a>>(node: N) -> Option<StructureNode> { | ||
160 | let name = node.name()?; | ||
161 | Some(StructureNode { | ||
162 | parent: None, | ||
163 | label: name.text().to_string(), | ||
164 | navigation_range: name.syntax().range(), | ||
165 | node_range: node.syntax().range(), | ||
166 | kind: node.syntax().kind(), | ||
167 | }) | ||
168 | } | ||
169 | |||
170 | visitor() | ||
171 | .visit(decl::<ast::FnDef>) | ||
172 | .visit(decl::<ast::StructDef>) | ||
173 | .visit(decl::<ast::NamedFieldDef>) | ||
174 | .visit(decl::<ast::EnumDef>) | ||
175 | .visit(decl::<ast::TraitDef>) | ||
176 | .visit(decl::<ast::Module>) | ||
177 | .visit(decl::<ast::TypeDef>) | ||
178 | .visit(decl::<ast::ConstDef>) | ||
179 | .visit(decl::<ast::StaticDef>) | ||
180 | .visit(|im: ast::ImplItem| { | ||
181 | let target_type = im.target_type()?; | ||
182 | let target_trait = im.target_trait(); | ||
183 | let label = match target_trait { | ||
184 | None => format!("impl {}", target_type.syntax().text()), | ||
185 | Some(t) => format!( | ||
186 | "impl {} for {}", | ||
187 | t.syntax().text(), | ||
188 | target_type.syntax().text(), | ||
189 | ), | ||
190 | }; | ||
191 | |||
192 | let node = StructureNode { | ||
193 | parent: None, | ||
194 | label, | ||
195 | navigation_range: target_type.syntax().range(), | ||
196 | node_range: im.syntax().range(), | ||
197 | kind: im.syntax().kind(), | ||
198 | }; | ||
199 | Some(node) | ||
200 | }) | ||
201 | .accept(node)? | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::*; | ||
207 | use test_utils::assert_eq_dbg; | ||
208 | |||
209 | #[test] | ||
210 | fn test_file_structure() { | ||
211 | let file = SourceFileNode::parse( | ||
212 | r#" | ||
213 | struct Foo { | ||
214 | x: i32 | ||
215 | } | ||
216 | |||
217 | mod m { | ||
218 | fn bar() {} | ||
219 | } | ||
220 | |||
221 | enum E { X, Y(i32) } | ||
222 | type T = (); | ||
223 | static S: i32 = 92; | ||
224 | const C: i32 = 92; | ||
225 | |||
226 | impl E {} | ||
227 | |||
228 | impl fmt::Debug for E {} | ||
229 | "#, | ||
230 | ); | ||
231 | let symbols = file_structure(&file); | ||
232 | assert_eq_dbg( | ||
233 | r#"[StructureNode { parent: None, label: "Foo", navigation_range: [8; 11), node_range: [1; 26), kind: STRUCT_DEF }, | ||
234 | StructureNode { parent: Some(0), label: "x", navigation_range: [18; 19), node_range: [18; 24), kind: NAMED_FIELD_DEF }, | ||
235 | StructureNode { parent: None, label: "m", navigation_range: [32; 33), node_range: [28; 53), kind: MODULE }, | ||
236 | StructureNode { parent: Some(2), label: "bar", navigation_range: [43; 46), node_range: [40; 51), kind: FN_DEF }, | ||
237 | StructureNode { parent: None, label: "E", navigation_range: [60; 61), node_range: [55; 75), kind: ENUM_DEF }, | ||
238 | StructureNode { parent: None, label: "T", navigation_range: [81; 82), node_range: [76; 88), kind: TYPE_DEF }, | ||
239 | StructureNode { parent: None, label: "S", navigation_range: [96; 97), node_range: [89; 108), kind: STATIC_DEF }, | ||
240 | StructureNode { parent: None, label: "C", navigation_range: [115; 116), node_range: [109; 127), kind: CONST_DEF }, | ||
241 | StructureNode { parent: None, label: "impl E", navigation_range: [134; 135), node_range: [129; 138), kind: IMPL_ITEM }, | ||
242 | StructureNode { parent: None, label: "impl fmt::Debug for E", navigation_range: [160; 161), node_range: [140; 164), kind: IMPL_ITEM }]"#, | ||
243 | &symbols, | ||
244 | ) | ||
245 | } | ||
246 | } | ||
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index e839a5a90..c6463235c 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_db::Cancelable; | ||
3 | use ra_syntax::ast::{self, NameOwner, StructFlavor}; | 4 | use ra_syntax::ast::{self, NameOwner, StructFlavor}; |
4 | 5 | ||
5 | use crate::{ | 6 | use crate::{ |
6 | DefId, Cancelable, Name, AsName, | 7 | DefId, Name, AsName, |
7 | db::HirDatabase, | 8 | db::HirDatabase, |
8 | type_ref::TypeRef, | 9 | type_ref::TypeRef, |
9 | }; | 10 | }; |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 5a8ca3b47..73a4cdc5c 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -1,13 +1,14 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::SyntaxNode; | 3 | use ra_syntax::{SyntaxNode, SourceFileNode}; |
4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; | 4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable}; |
5 | 5 | ||
6 | use crate::{ | 6 | use crate::{ |
7 | DefLoc, DefId, Name, | 7 | DefLoc, DefId, MacroCallLoc, MacroCallId, Name, HirFileId, |
8 | SourceFileItems, SourceItemId, | 8 | SourceFileItems, SourceItemId, |
9 | query_definitions, | 9 | query_definitions, |
10 | FnScopes, | 10 | FnScopes, |
11 | macros::MacroExpansion, | ||
11 | module::{ModuleId, ModuleTree, ModuleSource, | 12 | module::{ModuleId, ModuleTree, ModuleSource, |
12 | nameres::{ItemMap, InputModuleItems}}, | 13 | nameres::{ItemMap, InputModuleItems}}, |
13 | ty::{InferenceResult, Ty}, | 14 | ty::{InferenceResult, Ty}, |
@@ -18,7 +19,17 @@ salsa::query_group! { | |||
18 | 19 | ||
19 | pub trait HirDatabase: SyntaxDatabase | 20 | pub trait HirDatabase: SyntaxDatabase |
20 | + AsRef<LocationIntener<DefLoc, DefId>> | 21 | + AsRef<LocationIntener<DefLoc, DefId>> |
22 | + AsRef<LocationIntener<MacroCallLoc, MacroCallId>> | ||
21 | { | 23 | { |
24 | fn hir_source_file(file_id: HirFileId) -> SourceFileNode { | ||
25 | type HirSourceFileQuery; | ||
26 | use fn HirFileId::hir_source_file; | ||
27 | } | ||
28 | fn expand_macro_invocation(invoc: MacroCallId) -> Option<Arc<MacroExpansion>> { | ||
29 | type ExpandMacroCallQuery; | ||
30 | use fn crate::macros::expand_macro_invocation; | ||
31 | } | ||
32 | |||
22 | fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { | 33 | fn fn_scopes(def_id: DefId) -> Arc<FnScopes> { |
23 | type FnScopesQuery; | 34 | type FnScopesQuery; |
24 | use fn query_definitions::fn_scopes; | 35 | use fn query_definitions::fn_scopes; |
@@ -49,7 +60,7 @@ pub trait HirDatabase: SyntaxDatabase | |||
49 | use fn crate::ty::type_for_field; | 60 | use fn crate::ty::type_for_field; |
50 | } | 61 | } |
51 | 62 | ||
52 | fn file_items(file_id: FileId) -> Arc<SourceFileItems> { | 63 | fn file_items(file_id: HirFileId) -> Arc<SourceFileItems> { |
53 | type SourceFileItemsQuery; | 64 | type SourceFileItemsQuery; |
54 | use fn query_definitions::file_items; | 65 | use fn query_definitions::file_items; |
55 | } | 66 | } |
diff --git a/crates/ra_hir/src/ids.rs b/crates/ra_hir/src/ids.rs new file mode 100644 index 000000000..a09dee8b1 --- /dev/null +++ b/crates/ra_hir/src/ids.rs | |||
@@ -0,0 +1,280 @@ | |||
1 | use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId}; | ||
2 | use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast}; | ||
3 | |||
4 | use crate::{ | ||
5 | HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, | ||
6 | arena::{Arena, Id}, | ||
7 | }; | ||
8 | |||
9 | /// hir makes a heavy use of ids: integer (u32) handlers to various things. You | ||
10 | /// can think of id as a pointer (but without a lifetime) or a file descriptor | ||
11 | /// (but for hir objects). | ||
12 | /// | ||
13 | /// This module defines a bunch of ids we are using. The most important ones are | ||
14 | /// probably `HirFileId` and `DefId`. | ||
15 | |||
16 | /// Input to the analyzer is a set of file, where each file is indetified by | ||
17 | /// `FileId` and contains source code. However, another source of source code in | ||
18 | /// Rust are macros: each macro can be thought of as producing a "temporary | ||
19 | /// file". To assign id to such file, we use the id of a macro call that | ||
20 | /// produced the file. So, a `HirFileId` is either a `FileId` (source code | ||
21 | /// written by user), or a `MacroCallId` (source code produced by macro). | ||
22 | /// | ||
23 | /// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin | ||
24 | /// the call plus the offset of the macro call in the file. Note that this is a | ||
25 | /// recursive definition! Nethetheless, size_of of `HirFileId` is finite | ||
26 | /// (because everything bottoms out at the real `FileId`) and small | ||
27 | /// (`MacroCallId` uses location interner). | ||
28 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
29 | pub struct HirFileId(HirFileIdRepr); | ||
30 | |||
31 | impl HirFileId { | ||
32 | /// For macro-expansion files, returns the file original source file the | ||
33 | /// expansionoriginated from. | ||
34 | pub(crate) fn original_file(self, db: &impl HirDatabase) -> FileId { | ||
35 | match self.0 { | ||
36 | HirFileIdRepr::File(file_id) => file_id, | ||
37 | HirFileIdRepr::Macro(macro_call_id) => { | ||
38 | let loc = macro_call_id.loc(db); | ||
39 | loc.source_item_id.file_id.original_file(db) | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | pub(crate) fn as_original_file(self) -> FileId { | ||
45 | match self.0 { | ||
46 | HirFileIdRepr::File(file_id) => file_id, | ||
47 | HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self), | ||
48 | } | ||
49 | } | ||
50 | |||
51 | pub(crate) fn hir_source_file(db: &impl HirDatabase, file_id: HirFileId) -> SourceFileNode { | ||
52 | match file_id.0 { | ||
53 | HirFileIdRepr::File(file_id) => db.source_file(file_id), | ||
54 | HirFileIdRepr::Macro(m) => { | ||
55 | if let Some(exp) = db.expand_macro_invocation(m) { | ||
56 | return exp.file(); | ||
57 | } | ||
58 | // returning an empty string looks fishy... | ||
59 | SourceFileNode::parse("") | ||
60 | } | ||
61 | } | ||
62 | } | ||
63 | } | ||
64 | |||
65 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
66 | enum HirFileIdRepr { | ||
67 | File(FileId), | ||
68 | Macro(MacroCallId), | ||
69 | } | ||
70 | |||
71 | impl From<FileId> for HirFileId { | ||
72 | fn from(file_id: FileId) -> HirFileId { | ||
73 | HirFileId(HirFileIdRepr::File(file_id)) | ||
74 | } | ||
75 | } | ||
76 | |||
77 | impl From<MacroCallId> for HirFileId { | ||
78 | fn from(macro_call_id: MacroCallId) -> HirFileId { | ||
79 | HirFileId(HirFileIdRepr::Macro(macro_call_id)) | ||
80 | } | ||
81 | } | ||
82 | |||
83 | /// `MacroCallId` identifies a particular macro invocation, like | ||
84 | /// `println!("Hello, {}", world)`. | ||
85 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
86 | pub struct MacroCallId(u32); | ||
87 | ra_db::impl_numeric_id!(MacroCallId); | ||
88 | |||
89 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
90 | pub struct MacroCallLoc { | ||
91 | pub(crate) source_root_id: SourceRootId, | ||
92 | pub(crate) module_id: ModuleId, | ||
93 | pub(crate) source_item_id: SourceItemId, | ||
94 | } | ||
95 | |||
96 | impl MacroCallId { | ||
97 | pub(crate) fn loc( | ||
98 | self, | ||
99 | db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>, | ||
100 | ) -> MacroCallLoc { | ||
101 | db.as_ref().id2loc(self) | ||
102 | } | ||
103 | } | ||
104 | |||
105 | impl MacroCallLoc { | ||
106 | #[allow(unused)] | ||
107 | pub(crate) fn id( | ||
108 | &self, | ||
109 | db: &impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>>, | ||
110 | ) -> MacroCallId { | ||
111 | db.as_ref().loc2id(&self) | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) | ||
116 | /// in a specific module. | ||
117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
118 | pub struct DefId(u32); | ||
119 | ra_db::impl_numeric_id!(DefId); | ||
120 | |||
121 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
122 | pub struct DefLoc { | ||
123 | pub(crate) kind: DefKind, | ||
124 | pub(crate) source_root_id: SourceRootId, | ||
125 | pub(crate) module_id: ModuleId, | ||
126 | pub(crate) source_item_id: SourceItemId, | ||
127 | } | ||
128 | |||
129 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
130 | pub(crate) enum DefKind { | ||
131 | Module, | ||
132 | Function, | ||
133 | Struct, | ||
134 | Enum, | ||
135 | Item, | ||
136 | |||
137 | StructCtor, | ||
138 | } | ||
139 | |||
140 | impl DefId { | ||
141 | pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { | ||
142 | db.as_ref().id2loc(self) | ||
143 | } | ||
144 | |||
145 | pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> { | ||
146 | let loc = self.loc(db); | ||
147 | let res = match loc.kind { | ||
148 | DefKind::Module => { | ||
149 | let module = Module::new(db, loc.source_root_id, loc.module_id)?; | ||
150 | Def::Module(module) | ||
151 | } | ||
152 | DefKind::Function => { | ||
153 | let function = Function::new(self); | ||
154 | Def::Function(function) | ||
155 | } | ||
156 | DefKind::Struct => { | ||
157 | let struct_def = Struct::new(self); | ||
158 | Def::Struct(struct_def) | ||
159 | } | ||
160 | DefKind::Enum => { | ||
161 | let enum_def = Enum::new(self); | ||
162 | Def::Enum(enum_def) | ||
163 | } | ||
164 | DefKind::StructCtor => Def::Item, | ||
165 | DefKind::Item => Def::Item, | ||
166 | }; | ||
167 | Ok(res) | ||
168 | } | ||
169 | |||
170 | /// For a module, returns that module; for any other def, returns the containing module. | ||
171 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
172 | let loc = self.loc(db); | ||
173 | Module::new(db, loc.source_root_id, loc.module_id) | ||
174 | } | ||
175 | } | ||
176 | |||
177 | impl DefLoc { | ||
178 | pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId { | ||
179 | db.as_ref().loc2id(&self) | ||
180 | } | ||
181 | } | ||
182 | |||
183 | impl DefKind { | ||
184 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { | ||
185 | match kind { | ||
186 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), | ||
187 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), | ||
188 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
189 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
190 | // These define items, but don't have their own DefKinds yet: | ||
191 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), | ||
192 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), | ||
193 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), | ||
194 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), | ||
195 | _ => PerNs::none(), | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
200 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
201 | /// it's OK to use it as a salsa key/value. | ||
202 | pub(crate) type SourceFileItemId = Id<SyntaxNode>; | ||
203 | |||
204 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
205 | pub struct SourceItemId { | ||
206 | pub(crate) file_id: HirFileId, | ||
207 | /// None for the whole file. | ||
208 | pub(crate) item_id: Option<SourceFileItemId>, | ||
209 | } | ||
210 | |||
211 | /// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. | ||
212 | #[derive(Debug, PartialEq, Eq)] | ||
213 | pub struct SourceFileItems { | ||
214 | file_id: HirFileId, | ||
215 | arena: Arena<SyntaxNode>, | ||
216 | } | ||
217 | |||
218 | impl SourceFileItems { | ||
219 | pub(crate) fn new(file_id: HirFileId, source_file: SourceFile) -> SourceFileItems { | ||
220 | let mut res = SourceFileItems { | ||
221 | file_id, | ||
222 | arena: Arena::default(), | ||
223 | }; | ||
224 | res.init(source_file); | ||
225 | res | ||
226 | } | ||
227 | |||
228 | fn init(&mut self, source_file: SourceFile) { | ||
229 | source_file.syntax().descendants().for_each(|it| { | ||
230 | if let Some(module_item) = ast::ModuleItem::cast(it) { | ||
231 | self.alloc(module_item.syntax().owned()); | ||
232 | } else if let Some(macro_call) = ast::MacroCall::cast(it) { | ||
233 | self.alloc(macro_call.syntax().owned()); | ||
234 | } | ||
235 | }); | ||
236 | } | ||
237 | |||
238 | fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { | ||
239 | self.arena.alloc(item) | ||
240 | } | ||
241 | pub(crate) fn id_of(&self, file_id: HirFileId, item: SyntaxNodeRef) -> SourceFileItemId { | ||
242 | assert_eq!( | ||
243 | self.file_id, file_id, | ||
244 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
245 | self.file_id, file_id | ||
246 | ); | ||
247 | self.id_of_unchecked(item) | ||
248 | } | ||
249 | pub(crate) fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId { | ||
250 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) { | ||
251 | return id; | ||
252 | } | ||
253 | // This should not happen. Let's try to give a sensible diagnostics. | ||
254 | if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { | ||
255 | // FIXME(#288): whyyy are we getting here? | ||
256 | log::error!( | ||
257 | "unequal syntax nodes with the same range:\n{:?}\n{:?}", | ||
258 | item, | ||
259 | i | ||
260 | ); | ||
261 | return id; | ||
262 | } | ||
263 | panic!( | ||
264 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
265 | item, | ||
266 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
267 | ); | ||
268 | } | ||
269 | pub fn id_of_source_file(&self) -> SourceFileItemId { | ||
270 | let (id, _syntax) = self.arena.iter().next().unwrap(); | ||
271 | id | ||
272 | } | ||
273 | } | ||
274 | |||
275 | impl std::ops::Index<SourceFileItemId> for SourceFileItems { | ||
276 | type Output = SyntaxNode; | ||
277 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { | ||
278 | &self.arena[idx] | ||
279 | } | ||
280 | } | ||
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs index 89b1e639e..a0821d15d 100644 --- a/crates/ra_hir/src/krate.rs +++ b/crates/ra_hir/src/krate.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | pub use ra_db::CrateId; | 1 | pub use ra_db::{CrateId, Cancelable}; |
2 | 2 | ||
3 | use crate::{HirDatabase, Module, Cancelable, Name, AsName}; | 3 | use crate::{HirDatabase, Module, Name, AsName, HirFileId}; |
4 | 4 | ||
5 | /// hir::Crate describes a single crate. It's the main inteface with which | 5 | /// hir::Crate describes a single crate. It's the main inteface with which |
6 | /// crate's dependencies interact. Mostly, it should be just a proxy for the | 6 | /// crate's dependencies interact. Mostly, it should be just a proxy for the |
@@ -35,6 +35,7 @@ impl Crate { | |||
35 | let crate_graph = db.crate_graph(); | 35 | let crate_graph = db.crate_graph(); |
36 | let file_id = crate_graph.crate_root(self.crate_id); | 36 | let file_id = crate_graph.crate_root(self.crate_id); |
37 | let source_root_id = db.file_source_root(file_id); | 37 | let source_root_id = db.file_source_root(file_id); |
38 | let file_id = HirFileId::from(file_id); | ||
38 | let module_tree = db.module_tree(source_root_id)?; | 39 | let module_tree = db.module_tree(source_root_id)?; |
39 | // FIXME: teach module tree about crate roots instead of guessing | 40 | // FIXME: teach module tree about crate roots instead of guessing |
40 | let (module_id, _) = ctry!(module_tree | 41 | let (module_id, _) = ctry!(module_tree |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 5bbb09c01..8ee52a466 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -22,7 +22,10 @@ mod path; | |||
22 | mod arena; | 22 | mod arena; |
23 | pub mod source_binder; | 23 | pub mod source_binder; |
24 | 24 | ||
25 | mod ids; | ||
26 | mod macros; | ||
25 | mod name; | 27 | mod name; |
28 | // can't use `crate` or `r#crate` here :( | ||
26 | mod krate; | 29 | mod krate; |
27 | mod module; | 30 | mod module; |
28 | mod function; | 31 | mod function; |
@@ -30,21 +33,18 @@ mod adt; | |||
30 | mod type_ref; | 33 | mod type_ref; |
31 | mod ty; | 34 | mod ty; |
32 | 35 | ||
33 | use std::ops::Index; | ||
34 | |||
35 | use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind}; | ||
36 | use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; | ||
37 | |||
38 | use crate::{ | 36 | use crate::{ |
39 | db::HirDatabase, | 37 | db::HirDatabase, |
40 | arena::{Arena, Id}, | ||
41 | name::{AsName, KnownName}, | 38 | name::{AsName, KnownName}, |
39 | ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems}, | ||
42 | }; | 40 | }; |
43 | 41 | ||
44 | pub use self::{ | 42 | pub use self::{ |
45 | path::{Path, PathKind}, | 43 | path::{Path, PathKind}, |
46 | name::Name, | 44 | name::Name, |
47 | krate::Crate, | 45 | krate::Crate, |
46 | ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc}, | ||
47 | macros::{MacroDef, MacroInput, MacroExpansion}, | ||
48 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, | 48 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, |
49 | function::{Function, FnScopes}, | 49 | function::{Function, FnScopes}, |
50 | adt::{Struct, Enum}, | 50 | adt::{Struct, Enum}, |
@@ -53,60 +53,6 @@ pub use self::{ | |||
53 | 53 | ||
54 | pub use self::function::FnSignatureInfo; | 54 | pub use self::function::FnSignatureInfo; |
55 | 55 | ||
56 | /// Def's are a core concept of hir. A `Def` is an Item (function, module, etc) | ||
57 | /// in a specific module. | ||
58 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
59 | pub struct DefId(u32); | ||
60 | ra_db::impl_numeric_id!(DefId); | ||
61 | |||
62 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
63 | pub(crate) enum DefKind { | ||
64 | Module, | ||
65 | Function, | ||
66 | Struct, | ||
67 | Enum, | ||
68 | Item, | ||
69 | |||
70 | StructCtor, | ||
71 | } | ||
72 | |||
73 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||
74 | pub struct DefLoc { | ||
75 | pub(crate) kind: DefKind, | ||
76 | source_root_id: SourceRootId, | ||
77 | module_id: ModuleId, | ||
78 | source_item_id: SourceItemId, | ||
79 | } | ||
80 | |||
81 | impl DefKind { | ||
82 | pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> PerNs<DefKind> { | ||
83 | match kind { | ||
84 | SyntaxKind::FN_DEF => PerNs::values(DefKind::Function), | ||
85 | SyntaxKind::MODULE => PerNs::types(DefKind::Module), | ||
86 | SyntaxKind::STRUCT_DEF => PerNs::both(DefKind::Struct, DefKind::StructCtor), | ||
87 | SyntaxKind::ENUM_DEF => PerNs::types(DefKind::Enum), | ||
88 | // These define items, but don't have their own DefKinds yet: | ||
89 | SyntaxKind::TRAIT_DEF => PerNs::types(DefKind::Item), | ||
90 | SyntaxKind::TYPE_DEF => PerNs::types(DefKind::Item), | ||
91 | SyntaxKind::CONST_DEF => PerNs::values(DefKind::Item), | ||
92 | SyntaxKind::STATIC_DEF => PerNs::values(DefKind::Item), | ||
93 | _ => PerNs::none(), | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | |||
98 | impl DefId { | ||
99 | pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc { | ||
100 | db.as_ref().id2loc(self) | ||
101 | } | ||
102 | } | ||
103 | |||
104 | impl DefLoc { | ||
105 | pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId { | ||
106 | db.as_ref().loc2id(&self) | ||
107 | } | ||
108 | } | ||
109 | |||
110 | pub enum Def { | 56 | pub enum Def { |
111 | Module(Module), | 57 | Module(Module), |
112 | Function(Function), | 58 | Function(Function), |
@@ -114,106 +60,3 @@ pub enum Def { | |||
114 | Enum(Enum), | 60 | Enum(Enum), |
115 | Item, | 61 | Item, |
116 | } | 62 | } |
117 | |||
118 | impl DefId { | ||
119 | pub fn resolve(self, db: &impl HirDatabase) -> Cancelable<Def> { | ||
120 | let loc = self.loc(db); | ||
121 | let res = match loc.kind { | ||
122 | DefKind::Module => { | ||
123 | let module = Module::new(db, loc.source_root_id, loc.module_id)?; | ||
124 | Def::Module(module) | ||
125 | } | ||
126 | DefKind::Function => { | ||
127 | let function = Function::new(self); | ||
128 | Def::Function(function) | ||
129 | } | ||
130 | DefKind::Struct => { | ||
131 | let struct_def = Struct::new(self); | ||
132 | Def::Struct(struct_def) | ||
133 | } | ||
134 | DefKind::Enum => { | ||
135 | let enum_def = Enum::new(self); | ||
136 | Def::Enum(enum_def) | ||
137 | } | ||
138 | DefKind::StructCtor => Def::Item, | ||
139 | DefKind::Item => Def::Item, | ||
140 | }; | ||
141 | Ok(res) | ||
142 | } | ||
143 | |||
144 | /// For a module, returns that module; for any other def, returns the containing module. | ||
145 | pub fn module(self, db: &impl HirDatabase) -> Cancelable<Module> { | ||
146 | let loc = self.loc(db); | ||
147 | Module::new(db, loc.source_root_id, loc.module_id) | ||
148 | } | ||
149 | } | ||
150 | |||
151 | /// Identifier of item within a specific file. This is stable over reparses, so | ||
152 | /// it's OK to use it as a salsa key/value. | ||
153 | pub(crate) type SourceFileItemId = Id<SyntaxNode>; | ||
154 | |||
155 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
156 | pub struct SourceItemId { | ||
157 | file_id: FileId, | ||
158 | /// None for the whole file. | ||
159 | item_id: Option<SourceFileItemId>, | ||
160 | } | ||
161 | |||
162 | /// Maps item's `SyntaxNode`s to `SourceFileItemId` and back. | ||
163 | #[derive(Debug, PartialEq, Eq)] | ||
164 | pub struct SourceFileItems { | ||
165 | file_id: FileId, | ||
166 | arena: Arena<SyntaxNode>, | ||
167 | } | ||
168 | |||
169 | impl SourceFileItems { | ||
170 | fn new(file_id: FileId) -> SourceFileItems { | ||
171 | SourceFileItems { | ||
172 | file_id, | ||
173 | arena: Arena::default(), | ||
174 | } | ||
175 | } | ||
176 | |||
177 | fn alloc(&mut self, item: SyntaxNode) -> SourceFileItemId { | ||
178 | self.arena.alloc(item) | ||
179 | } | ||
180 | pub fn id_of(&self, file_id: FileId, item: SyntaxNodeRef) -> SourceFileItemId { | ||
181 | assert_eq!( | ||
182 | self.file_id, file_id, | ||
183 | "SourceFileItems: wrong file, expected {:?}, got {:?}", | ||
184 | self.file_id, file_id | ||
185 | ); | ||
186 | self.id_of_unchecked(item) | ||
187 | } | ||
188 | fn id_of_unchecked(&self, item: SyntaxNodeRef) -> SourceFileItemId { | ||
189 | if let Some((id, _)) = self.arena.iter().find(|(_id, i)| i.borrowed() == item) { | ||
190 | return id; | ||
191 | } | ||
192 | // This should not happen. Let's try to give a sensible diagnostics. | ||
193 | if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) { | ||
194 | // FIXME(#288): whyyy are we getting here? | ||
195 | log::error!( | ||
196 | "unequal syntax nodes with the same range:\n{:?}\n{:?}", | ||
197 | item, | ||
198 | i | ||
199 | ); | ||
200 | return id; | ||
201 | } | ||
202 | panic!( | ||
203 | "Can't find {:?} in SourceFileItems:\n{:?}", | ||
204 | item, | ||
205 | self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(), | ||
206 | ); | ||
207 | } | ||
208 | pub fn id_of_source_file(&self) -> SourceFileItemId { | ||
209 | let (id, _syntax) = self.arena.iter().next().unwrap(); | ||
210 | id | ||
211 | } | ||
212 | } | ||
213 | |||
214 | impl Index<SourceFileItemId> for SourceFileItems { | ||
215 | type Output = SyntaxNode; | ||
216 | fn index(&self, idx: SourceFileItemId) -> &SyntaxNode { | ||
217 | &self.arena[idx] | ||
218 | } | ||
219 | } | ||
diff --git a/crates/ra_hir/src/macros.rs b/crates/ra_hir/src/macros.rs new file mode 100644 index 000000000..b7b75e702 --- /dev/null +++ b/crates/ra_hir/src/macros.rs | |||
@@ -0,0 +1,181 @@ | |||
1 | /// Machinery for macro expansion. | ||
2 | /// | ||
3 | /// One of the more complicated things about macros is managing the source code | ||
4 | /// that is produced after expansion. See `HirFileId` and `MacroCallId` for how | ||
5 | /// do we do that. | ||
6 | /// | ||
7 | /// When file-management question is resolved, all that is left is a token tree | ||
8 | /// to token tree transformation plus hygent. We don't have either of thouse | ||
9 | /// yet, so all macros are string based at the moment! | ||
10 | use std::sync::Arc; | ||
11 | |||
12 | use ra_db::LocalSyntaxPtr; | ||
13 | use ra_syntax::{ | ||
14 | TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode, | ||
15 | ast::{self, NameOwner}, | ||
16 | }; | ||
17 | |||
18 | use crate::{HirDatabase, MacroCallId}; | ||
19 | |||
20 | // Hard-coded defs for now :-( | ||
21 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
22 | pub enum MacroDef { | ||
23 | CTry, | ||
24 | QueryGroup, | ||
25 | } | ||
26 | |||
27 | impl MacroDef { | ||
28 | /// Expands macro call, returning the expansion and offset to be used to | ||
29 | /// convert ranges between expansion and original source. | ||
30 | pub fn ast_expand(macro_call: ast::MacroCall) -> Option<(TextUnit, MacroExpansion)> { | ||
31 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
32 | let exp = def.expand(input)?; | ||
33 | let off = macro_call.token_tree()?.syntax().range().start(); | ||
34 | Some((off, exp)) | ||
35 | } | ||
36 | |||
37 | fn from_call(macro_call: ast::MacroCall) -> Option<(MacroDef, MacroInput)> { | ||
38 | let def = { | ||
39 | let path = macro_call.path()?; | ||
40 | let name_ref = path.segment()?.name_ref()?; | ||
41 | if name_ref.text() == "ctry" { | ||
42 | MacroDef::CTry | ||
43 | } else if name_ref.text() == "query_group" { | ||
44 | MacroDef::QueryGroup | ||
45 | } else { | ||
46 | return None; | ||
47 | } | ||
48 | }; | ||
49 | |||
50 | let input = { | ||
51 | let arg = macro_call.token_tree()?.syntax(); | ||
52 | MacroInput { | ||
53 | text: arg.text().to_string(), | ||
54 | } | ||
55 | }; | ||
56 | Some((def, input)) | ||
57 | } | ||
58 | |||
59 | fn expand(self, input: MacroInput) -> Option<MacroExpansion> { | ||
60 | match self { | ||
61 | MacroDef::CTry => self.expand_ctry(input), | ||
62 | MacroDef::QueryGroup => self.expand_query_group(input), | ||
63 | } | ||
64 | } | ||
65 | fn expand_ctry(self, input: MacroInput) -> Option<MacroExpansion> { | ||
66 | let text = format!( | ||
67 | r" | ||
68 | fn dummy() {{ | ||
69 | match {} {{ | ||
70 | None => return Ok(None), | ||
71 | Some(it) => it, | ||
72 | }} | ||
73 | }}", | ||
74 | input.text | ||
75 | ); | ||
76 | let file = SourceFileNode::parse(&text); | ||
77 | let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?; | ||
78 | let match_arg = match_expr.expr()?; | ||
79 | let ptr = LocalSyntaxPtr::new(match_arg.syntax()); | ||
80 | let src_range = TextRange::offset_len(0.into(), TextUnit::of_str(&input.text)); | ||
81 | let ranges_map = vec![(src_range, match_arg.syntax().range())]; | ||
82 | let res = MacroExpansion { | ||
83 | text, | ||
84 | ranges_map, | ||
85 | ptr, | ||
86 | }; | ||
87 | Some(res) | ||
88 | } | ||
89 | fn expand_query_group(self, input: MacroInput) -> Option<MacroExpansion> { | ||
90 | let anchor = "trait "; | ||
91 | let pos = input.text.find(anchor)? + anchor.len(); | ||
92 | let trait_name = input.text[pos..] | ||
93 | .chars() | ||
94 | .take_while(|c| c.is_alphabetic()) | ||
95 | .collect::<String>(); | ||
96 | if trait_name.is_empty() { | ||
97 | return None; | ||
98 | } | ||
99 | let src_range = TextRange::offset_len((pos as u32).into(), TextUnit::of_str(&trait_name)); | ||
100 | let text = format!(r"trait {} {{ }}", trait_name); | ||
101 | let file = SourceFileNode::parse(&text); | ||
102 | let trait_def = file.syntax().descendants().find_map(ast::TraitDef::cast)?; | ||
103 | let name = trait_def.name()?; | ||
104 | let ptr = LocalSyntaxPtr::new(trait_def.syntax()); | ||
105 | let ranges_map = vec![(src_range, name.syntax().range())]; | ||
106 | let res = MacroExpansion { | ||
107 | text, | ||
108 | ranges_map, | ||
109 | ptr, | ||
110 | }; | ||
111 | Some(res) | ||
112 | } | ||
113 | } | ||
114 | |||
115 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
116 | pub struct MacroInput { | ||
117 | // Should be token trees | ||
118 | pub text: String, | ||
119 | } | ||
120 | |||
121 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
122 | pub struct MacroExpansion { | ||
123 | /// The result of macro expansion. Should be token tree as well. | ||
124 | text: String, | ||
125 | /// Correspondence between ranges in the original source code and ranges in | ||
126 | /// the macro. | ||
127 | ranges_map: Vec<(TextRange, TextRange)>, | ||
128 | /// Implementation detail: internally, a macro is expanded to the whole file, | ||
129 | /// even if it is an expression. This `ptr` selects the actual expansion from | ||
130 | /// the expanded file. | ||
131 | ptr: LocalSyntaxPtr, | ||
132 | } | ||
133 | |||
134 | impl MacroExpansion { | ||
135 | // FIXME: does not really make sense, macro expansion is not neccessary a | ||
136 | // whole file. See `MacroExpansion::ptr` as well. | ||
137 | pub(crate) fn file(&self) -> SourceFileNode { | ||
138 | SourceFileNode::parse(&self.text) | ||
139 | } | ||
140 | |||
141 | pub fn syntax(&self) -> SyntaxNode { | ||
142 | self.ptr.resolve(&self.file()) | ||
143 | } | ||
144 | /// Maps range in the source code to the range in the expanded code. | ||
145 | pub fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> { | ||
146 | for (s_range, t_range) in self.ranges_map.iter() { | ||
147 | if src_range.is_subrange(&s_range) { | ||
148 | let src_at_zero_range = src_range - src_range.start(); | ||
149 | let src_range_offset = src_range.start() - s_range.start(); | ||
150 | let src_range = src_at_zero_range + src_range_offset + t_range.start(); | ||
151 | return Some(src_range); | ||
152 | } | ||
153 | } | ||
154 | None | ||
155 | } | ||
156 | /// Maps range in the expanded code to the range in the source code. | ||
157 | pub fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> { | ||
158 | for (s_range, t_range) in self.ranges_map.iter() { | ||
159 | if tgt_range.is_subrange(&t_range) { | ||
160 | let tgt_at_zero_range = tgt_range - tgt_range.start(); | ||
161 | let tgt_range_offset = tgt_range.start() - t_range.start(); | ||
162 | let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start(); | ||
163 | return Some(src_range); | ||
164 | } | ||
165 | } | ||
166 | None | ||
167 | } | ||
168 | } | ||
169 | |||
170 | pub(crate) fn expand_macro_invocation( | ||
171 | db: &impl HirDatabase, | ||
172 | invoc: MacroCallId, | ||
173 | ) -> Option<Arc<MacroExpansion>> { | ||
174 | let loc = invoc.loc(db); | ||
175 | let syntax = db.file_item(loc.source_item_id); | ||
176 | let syntax = syntax.borrowed(); | ||
177 | let macro_call = ast::MacroCall::cast(syntax).unwrap(); | ||
178 | |||
179 | let (def, input) = MacroDef::from_call(macro_call)?; | ||
180 | def.expand(input).map(Arc::new) | ||
181 | } | ||
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index a2507c9b5..89b18194a 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs | |||
@@ -6,7 +6,7 @@ use ra_db::{LocationIntener, BaseDatabase, FilePosition, FileId, CrateGraph, Sou | |||
6 | use relative_path::RelativePathBuf; | 6 | use relative_path::RelativePathBuf; |
7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; | 7 | use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; |
8 | 8 | ||
9 | use crate::{db, DefId, DefLoc}; | 9 | use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc}; |
10 | 10 | ||
11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 11 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
12 | 12 | ||
@@ -95,6 +95,7 @@ impl MockDatabase { | |||
95 | #[derive(Debug, Default)] | 95 | #[derive(Debug, Default)] |
96 | struct IdMaps { | 96 | struct IdMaps { |
97 | defs: LocationIntener<DefLoc, DefId>, | 97 | defs: LocationIntener<DefLoc, DefId>, |
98 | macros: LocationIntener<MacroCallLoc, MacroCallId>, | ||
98 | } | 99 | } |
99 | 100 | ||
100 | impl salsa::Database for MockDatabase { | 101 | impl salsa::Database for MockDatabase { |
@@ -144,6 +145,11 @@ impl AsRef<LocationIntener<DefLoc, DefId>> for MockDatabase { | |||
144 | &self.id_maps.defs | 145 | &self.id_maps.defs |
145 | } | 146 | } |
146 | } | 147 | } |
148 | impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase { | ||
149 | fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> { | ||
150 | &self.id_maps.macros | ||
151 | } | ||
152 | } | ||
147 | 153 | ||
148 | impl MockDatabase { | 154 | impl MockDatabase { |
149 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { | 155 | pub(crate) fn log(&self, f: impl FnOnce()) -> Vec<salsa::Event<MockDatabase>> { |
@@ -183,6 +189,8 @@ salsa::database_storage! { | |||
183 | fn file_lines() for ra_db::FileLinesQuery; | 189 | fn file_lines() for ra_db::FileLinesQuery; |
184 | } | 190 | } |
185 | impl db::HirDatabase { | 191 | impl db::HirDatabase { |
192 | fn hir_source_file() for db::HirSourceFileQuery; | ||
193 | fn expand_macro_invocation() for db::ExpandMacroCallQuery; | ||
186 | fn module_tree() for db::ModuleTreeQuery; | 194 | fn module_tree() for db::ModuleTreeQuery; |
187 | fn fn_scopes() for db::FnScopesQuery; | 195 | fn fn_scopes() for db::FnScopesQuery; |
188 | fn file_items() for db::SourceFileItemsQuery; | 196 | fn file_items() for db::SourceFileItemsQuery; |
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 87e30191f..a53b69d20 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs | |||
@@ -15,6 +15,7 @@ use relative_path::RelativePathBuf; | |||
15 | use crate::{ | 15 | use crate::{ |
16 | Def, DefKind, DefLoc, DefId, | 16 | Def, DefKind, DefLoc, DefId, |
17 | Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, | 17 | Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, |
18 | HirFileId, | ||
18 | arena::{Arena, Id}, | 19 | arena::{Arena, Id}, |
19 | }; | 20 | }; |
20 | 21 | ||
@@ -48,13 +49,17 @@ impl Module { | |||
48 | /// Returns `None` for the root module | 49 | /// Returns `None` for the root module |
49 | pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { | 50 | pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { |
50 | let link = self.module_id.parent_link(&self.tree)?; | 51 | let link = self.module_id.parent_link(&self.tree)?; |
51 | let file_id = link.owner(&self.tree).source(&self.tree).file_id(); | 52 | let file_id = link |
53 | .owner(&self.tree) | ||
54 | .source(&self.tree) | ||
55 | .file_id() | ||
56 | .as_original_file(); | ||
52 | let src = link.bind_source(&self.tree, db); | 57 | let src = link.bind_source(&self.tree, db); |
53 | Some((file_id, src)) | 58 | Some((file_id, src)) |
54 | } | 59 | } |
55 | 60 | ||
56 | pub fn source(&self) -> ModuleSource { | 61 | pub fn file_id(&self) -> FileId { |
57 | self.module_id.source(&self.tree) | 62 | self.source().file_id().as_original_file() |
58 | } | 63 | } |
59 | 64 | ||
60 | /// Parent module. Returns `None` if this is a root module. | 65 | /// Parent module. Returns `None` if this is a root module. |
@@ -69,13 +74,13 @@ impl Module { | |||
69 | /// Returns the crate this module is part of. | 74 | /// Returns the crate this module is part of. |
70 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { | 75 | pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> { |
71 | let root_id = self.module_id.crate_root(&self.tree); | 76 | let root_id = self.module_id.crate_root(&self.tree); |
72 | let file_id = root_id.source(&self.tree).file_id(); | 77 | let file_id = root_id.source(&self.tree).file_id().as_original_file(); |
73 | let crate_graph = db.crate_graph(); | 78 | let crate_graph = db.crate_graph(); |
74 | let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; | 79 | let crate_id = crate_graph.crate_id_for_crate_root(file_id)?; |
75 | Some(Crate::new(crate_id)) | 80 | Some(Crate::new(crate_id)) |
76 | } | 81 | } |
77 | 82 | ||
78 | /// Returns the all modulkes on the way to the root. | 83 | /// Returns the all modules on the way to the root. |
79 | pub fn path_to_root(&self) -> Vec<Module> { | 84 | pub fn path_to_root(&self) -> Vec<Module> { |
80 | generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>() | 85 | generate(Some(self.clone()), move |it| it.parent()).collect::<Vec<Module>>() |
81 | } | 86 | } |
@@ -162,6 +167,10 @@ impl Module { | |||
162 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { | 167 | pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { |
163 | self.module_id.problems(&self.tree, db) | 168 | self.module_id.problems(&self.tree, db) |
164 | } | 169 | } |
170 | |||
171 | pub(crate) fn source(&self) -> ModuleSource { | ||
172 | self.module_id.source(&self.tree) | ||
173 | } | ||
165 | } | 174 | } |
166 | 175 | ||
167 | /// Physically, rust source is organized as a set of files, but logically it is | 176 | /// Physically, rust source is organized as a set of files, but logically it is |
@@ -291,18 +300,18 @@ pub struct ModuleData { | |||
291 | 300 | ||
292 | impl ModuleSource { | 301 | impl ModuleSource { |
293 | // precondition: item_id **must** point to module | 302 | // precondition: item_id **must** point to module |
294 | fn new(file_id: FileId, item_id: Option<SourceFileItemId>) -> ModuleSource { | 303 | fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource { |
295 | let source_item_id = SourceItemId { file_id, item_id }; | 304 | let source_item_id = SourceItemId { file_id, item_id }; |
296 | ModuleSource(source_item_id) | 305 | ModuleSource(source_item_id) |
297 | } | 306 | } |
298 | 307 | ||
299 | pub(crate) fn new_file(file_id: FileId) -> ModuleSource { | 308 | pub(crate) fn new_file(file_id: HirFileId) -> ModuleSource { |
300 | ModuleSource::new(file_id, None) | 309 | ModuleSource::new(file_id, None) |
301 | } | 310 | } |
302 | 311 | ||
303 | pub(crate) fn new_inline( | 312 | pub(crate) fn new_inline( |
304 | db: &impl HirDatabase, | 313 | db: &impl HirDatabase, |
305 | file_id: FileId, | 314 | file_id: HirFileId, |
306 | m: ast::Module, | 315 | m: ast::Module, |
307 | ) -> ModuleSource { | 316 | ) -> ModuleSource { |
308 | assert!(!m.has_semi()); | 317 | assert!(!m.has_semi()); |
@@ -311,7 +320,7 @@ impl ModuleSource { | |||
311 | ModuleSource::new(file_id, Some(item_id)) | 320 | ModuleSource::new(file_id, Some(item_id)) |
312 | } | 321 | } |
313 | 322 | ||
314 | pub fn file_id(self) -> FileId { | 323 | pub(crate) fn file_id(self) -> HirFileId { |
315 | self.0.file_id | 324 | self.0.file_id |
316 | } | 325 | } |
317 | 326 | ||
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs index eded85a63..3849026db 100644 --- a/crates/ra_hir/src/module/imp.rs +++ b/crates/ra_hir/src/module/imp.rs | |||
@@ -64,7 +64,7 @@ fn create_module_tree<'a>( | |||
64 | 64 | ||
65 | let source_root = db.source_root(source_root); | 65 | let source_root = db.source_root(source_root); |
66 | for &file_id in source_root.files.values() { | 66 | for &file_id in source_root.files.values() { |
67 | let source = ModuleSource::new_file(file_id); | 67 | let source = ModuleSource::new_file(file_id.into()); |
68 | if visited.contains(&source) { | 68 | if visited.contains(&source) { |
69 | continue; // TODO: use explicit crate_roots here | 69 | continue; // TODO: use explicit crate_roots here |
70 | } | 70 | } |
@@ -123,7 +123,7 @@ fn build_subtree( | |||
123 | visited, | 123 | visited, |
124 | roots, | 124 | roots, |
125 | Some(link), | 125 | Some(link), |
126 | ModuleSource::new_file(file_id), | 126 | ModuleSource::new_file(file_id.into()), |
127 | ), | 127 | ), |
128 | }) | 128 | }) |
129 | .collect::<Cancelable<Vec<_>>>()?; | 129 | .collect::<Cancelable<Vec<_>>>()?; |
@@ -155,7 +155,7 @@ fn resolve_submodule( | |||
155 | name: &Name, | 155 | name: &Name, |
156 | ) -> (Vec<FileId>, Option<Problem>) { | 156 | ) -> (Vec<FileId>, Option<Problem>) { |
157 | // FIXME: handle submodules of inline modules properly | 157 | // FIXME: handle submodules of inline modules properly |
158 | let file_id = source.file_id(); | 158 | let file_id = source.file_id().original_file(db); |
159 | let source_root_id = db.file_source_root(file_id); | 159 | let source_root_id = db.file_source_root(file_id); |
160 | let path = db.file_relative_path(file_id); | 160 | let path = db.file_relative_path(file_id); |
161 | let root = RelativePathBuf::default(); | 161 | let root = RelativePathBuf::default(); |
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs index 68eb02a98..40aa33ffa 100644 --- a/crates/ra_hir/src/module/nameres.rs +++ b/crates/ra_hir/src/module/nameres.rs | |||
@@ -22,10 +22,10 @@ use ra_syntax::{ | |||
22 | SyntaxKind::{self, *}, | 22 | SyntaxKind::{self, *}, |
23 | ast::{self, AstNode} | 23 | ast::{self, AstNode} |
24 | }; | 24 | }; |
25 | use ra_db::SourceRootId; | 25 | use ra_db::{SourceRootId, Cancelable, FileId}; |
26 | 26 | ||
27 | use crate::{ | 27 | use crate::{ |
28 | Cancelable, FileId, | 28 | HirFileId, |
29 | DefId, DefLoc, DefKind, | 29 | DefId, DefLoc, DefKind, |
30 | SourceItemId, SourceFileItemId, SourceFileItems, | 30 | SourceItemId, SourceFileItemId, SourceFileItems, |
31 | Path, PathKind, | 31 | Path, PathKind, |
@@ -70,7 +70,7 @@ pub struct InputModuleItems { | |||
70 | 70 | ||
71 | #[derive(Debug, PartialEq, Eq)] | 71 | #[derive(Debug, PartialEq, Eq)] |
72 | struct ModuleItem { | 72 | struct ModuleItem { |
73 | id: SourceFileItemId, | 73 | id: SourceItemId, |
74 | name: Name, | 74 | name: Name, |
75 | kind: SyntaxKind, | 75 | kind: SyntaxKind, |
76 | vis: Vis, | 76 | vis: Vis, |
@@ -95,9 +95,11 @@ pub struct NamedImport { | |||
95 | } | 95 | } |
96 | 96 | ||
97 | impl NamedImport { | 97 | impl NamedImport { |
98 | // FIXME: this is only here for one use-case in completion. Seems like a | ||
99 | // pretty gross special case. | ||
98 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { | 100 | pub fn range(&self, db: &impl HirDatabase, file_id: FileId) -> TextRange { |
99 | let source_item_id = SourceItemId { | 101 | let source_item_id = SourceItemId { |
100 | file_id, | 102 | file_id: file_id.into(), |
101 | item_id: Some(self.file_item_id), | 103 | item_id: Some(self.file_item_id), |
102 | }; | 104 | }; |
103 | let syntax = db.file_item(source_item_id); | 105 | let syntax = db.file_item(source_item_id); |
@@ -209,24 +211,28 @@ impl<T> PerNs<T> { | |||
209 | } | 211 | } |
210 | 212 | ||
211 | impl InputModuleItems { | 213 | impl InputModuleItems { |
212 | pub(crate) fn new<'a>( | 214 | pub(crate) fn add_item( |
215 | &mut self, | ||
216 | file_id: HirFileId, | ||
213 | file_items: &SourceFileItems, | 217 | file_items: &SourceFileItems, |
214 | items: impl Iterator<Item = ast::ModuleItem<'a>>, | 218 | item: ast::ModuleItem, |
215 | ) -> InputModuleItems { | 219 | ) -> Option<()> { |
216 | let mut res = InputModuleItems::default(); | ||
217 | for item in items { | ||
218 | res.add_item(file_items, item); | ||
219 | } | ||
220 | res | ||
221 | } | ||
222 | |||
223 | fn add_item(&mut self, file_items: &SourceFileItems, item: ast::ModuleItem) -> Option<()> { | ||
224 | match item { | 220 | match item { |
225 | ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 221 | ast::ModuleItem::StructDef(it) => { |
226 | ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 222 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
227 | ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 223 | } |
228 | ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 224 | ast::ModuleItem::EnumDef(it) => { |
229 | ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 225 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
226 | } | ||
227 | ast::ModuleItem::FnDef(it) => { | ||
228 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
229 | } | ||
230 | ast::ModuleItem::TraitDef(it) => { | ||
231 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
232 | } | ||
233 | ast::ModuleItem::TypeDef(it) => { | ||
234 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
235 | } | ||
230 | ast::ModuleItem::ImplItem(_) => { | 236 | ast::ModuleItem::ImplItem(_) => { |
231 | // impls don't define items | 237 | // impls don't define items |
232 | } | 238 | } |
@@ -234,9 +240,15 @@ impl InputModuleItems { | |||
234 | ast::ModuleItem::ExternCrateItem(_) => { | 240 | ast::ModuleItem::ExternCrateItem(_) => { |
235 | // TODO | 241 | // TODO |
236 | } | 242 | } |
237 | ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 243 | ast::ModuleItem::ConstDef(it) => { |
238 | ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(file_items, it)?), | 244 | self.items.push(ModuleItem::new(file_id, file_items, it)?) |
239 | ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(file_items, it)?), | 245 | } |
246 | ast::ModuleItem::StaticDef(it) => { | ||
247 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
248 | } | ||
249 | ast::ModuleItem::Module(it) => { | ||
250 | self.items.push(ModuleItem::new(file_id, file_items, it)?) | ||
251 | } | ||
240 | } | 252 | } |
241 | Some(()) | 253 | Some(()) |
242 | } | 254 | } |
@@ -258,11 +270,16 @@ impl InputModuleItems { | |||
258 | } | 270 | } |
259 | 271 | ||
260 | impl ModuleItem { | 272 | impl ModuleItem { |
261 | fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { | 273 | fn new<'a>( |
274 | file_id: HirFileId, | ||
275 | file_items: &SourceFileItems, | ||
276 | item: impl ast::NameOwner<'a>, | ||
277 | ) -> Option<ModuleItem> { | ||
262 | let name = item.name()?.as_name(); | 278 | let name = item.name()?.as_name(); |
263 | let kind = item.syntax().kind(); | 279 | let kind = item.syntax().kind(); |
264 | let vis = Vis::Other; | 280 | let vis = Vis::Other; |
265 | let id = file_items.id_of_unchecked(item.syntax()); | 281 | let item_id = Some(file_items.id_of_unchecked(item.syntax())); |
282 | let id = SourceItemId { file_id, item_id }; | ||
266 | let res = ModuleItem { | 283 | let res = ModuleItem { |
267 | id, | 284 | id, |
268 | name, | 285 | name, |
@@ -302,7 +319,7 @@ where | |||
302 | 319 | ||
303 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { | 320 | pub(crate) fn resolve(mut self) -> Cancelable<ItemMap> { |
304 | for (&module_id, items) in self.input.iter() { | 321 | for (&module_id, items) in self.input.iter() { |
305 | self.populate_module(module_id, items)?; | 322 | self.populate_module(module_id, Arc::clone(items))?; |
306 | } | 323 | } |
307 | 324 | ||
308 | for &module_id in self.input.keys() { | 325 | for &module_id in self.input.keys() { |
@@ -312,9 +329,11 @@ where | |||
312 | Ok(self.result) | 329 | Ok(self.result) |
313 | } | 330 | } |
314 | 331 | ||
315 | fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) -> Cancelable<()> { | 332 | fn populate_module( |
316 | let file_id = module_id.source(&self.module_tree).file_id(); | 333 | &mut self, |
317 | 334 | module_id: ModuleId, | |
335 | input: Arc<InputModuleItems>, | ||
336 | ) -> Cancelable<()> { | ||
318 | let mut module_items = ModuleScope::default(); | 337 | let mut module_items = ModuleScope::default(); |
319 | 338 | ||
320 | // Populate extern crates prelude | 339 | // Populate extern crates prelude |
@@ -322,7 +341,8 @@ where | |||
322 | let root_id = module_id.crate_root(&self.module_tree); | 341 | let root_id = module_id.crate_root(&self.module_tree); |
323 | let file_id = root_id.source(&self.module_tree).file_id(); | 342 | let file_id = root_id.source(&self.module_tree).file_id(); |
324 | let crate_graph = self.db.crate_graph(); | 343 | let crate_graph = self.db.crate_graph(); |
325 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id) { | 344 | if let Some(crate_id) = crate_graph.crate_id_for_crate_root(file_id.as_original_file()) |
345 | { | ||
326 | let krate = Crate::new(crate_id); | 346 | let krate = Crate::new(crate_id); |
327 | for dep in krate.dependencies(self.db) { | 347 | for dep in krate.dependencies(self.db) { |
328 | if let Some(module) = dep.krate.root_module(self.db)? { | 348 | if let Some(module) = dep.krate.root_module(self.db)? { |
@@ -362,10 +382,7 @@ where | |||
362 | kind: k, | 382 | kind: k, |
363 | source_root_id: self.source_root, | 383 | source_root_id: self.source_root, |
364 | module_id, | 384 | module_id, |
365 | source_item_id: SourceItemId { | 385 | source_item_id: item.id, |
366 | file_id, | ||
367 | item_id: Some(item.id), | ||
368 | }, | ||
369 | }; | 386 | }; |
370 | def_loc.id(self.db) | 387 | def_loc.id(self.db) |
371 | }); | 388 | }); |
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs index ca20f064f..a6a0bea31 100644 --- a/crates/ra_hir/src/module/nameres/tests.rs +++ b/crates/ra_hir/src/module/nameres/tests.rs | |||
@@ -78,6 +78,35 @@ fn item_map_smoke_test() { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | #[test] | 80 | #[test] |
81 | fn item_map_contains_items_from_expansions() { | ||
82 | let (item_map, module_id) = item_map( | ||
83 | " | ||
84 | //- /lib.rs | ||
85 | mod foo; | ||
86 | |||
87 | use crate::foo::bar::Baz; | ||
88 | <|> | ||
89 | |||
90 | //- /foo/mod.rs | ||
91 | pub mod bar; | ||
92 | |||
93 | //- /foo/bar.rs | ||
94 | salsa::query_group! { | ||
95 | trait Baz {} | ||
96 | } | ||
97 | ", | ||
98 | ); | ||
99 | check_module_item_map( | ||
100 | &item_map, | ||
101 | module_id, | ||
102 | " | ||
103 | Baz: t | ||
104 | foo: t | ||
105 | ", | ||
106 | ); | ||
107 | } | ||
108 | |||
109 | #[test] | ||
81 | fn item_map_using_self() { | 110 | fn item_map_using_self() { |
82 | let (item_map, module_id) = item_map( | 111 | let (item_map, module_id) = item_map( |
83 | " | 112 | " |
@@ -144,6 +173,59 @@ fn typing_inside_a_function_should_not_invalidate_item_map() { | |||
144 | let (mut db, pos) = MockDatabase::with_position( | 173 | let (mut db, pos) = MockDatabase::with_position( |
145 | " | 174 | " |
146 | //- /lib.rs | 175 | //- /lib.rs |
176 | mod foo; | ||
177 | |||
178 | use crate::foo::bar::Baz; | ||
179 | |||
180 | //- /foo/mod.rs | ||
181 | pub mod bar; | ||
182 | |||
183 | //- /foo/bar.rs | ||
184 | <|> | ||
185 | salsa::query_group! { | ||
186 | trait Baz { | ||
187 | fn foo() -> i32 { 1 + 1 } | ||
188 | } | ||
189 | } | ||
190 | ", | ||
191 | ); | ||
192 | let source_root = db.file_source_root(pos.file_id); | ||
193 | { | ||
194 | let events = db.log_executed(|| { | ||
195 | db.item_map(source_root).unwrap(); | ||
196 | }); | ||
197 | assert!(format!("{:?}", events).contains("item_map")) | ||
198 | } | ||
199 | |||
200 | let new_text = " | ||
201 | salsa::query_group! { | ||
202 | trait Baz { | ||
203 | fn foo() -> i32 { 92 } | ||
204 | } | ||
205 | } | ||
206 | " | ||
207 | .to_string(); | ||
208 | |||
209 | db.query_mut(ra_db::FileTextQuery) | ||
210 | .set(pos.file_id, Arc::new(new_text)); | ||
211 | |||
212 | { | ||
213 | let events = db.log_executed(|| { | ||
214 | db.item_map(source_root).unwrap(); | ||
215 | }); | ||
216 | assert!( | ||
217 | !format!("{:?}", events).contains("item_map"), | ||
218 | "{:#?}", | ||
219 | events | ||
220 | ) | ||
221 | } | ||
222 | } | ||
223 | |||
224 | #[test] | ||
225 | fn typing_inside_a_function_inside_a_macro_should_not_invalidate_item_map() { | ||
226 | let (mut db, pos) = MockDatabase::with_position( | ||
227 | " | ||
228 | //- /lib.rs | ||
147 | mod foo;<|> | 229 | mod foo;<|> |
148 | 230 | ||
149 | use crate::foo::bar::Baz; | 231 | use crate::foo::bar::Baz; |
@@ -183,7 +265,7 @@ fn typing_inside_a_function_should_not_invalidate_item_map() { | |||
183 | db.item_map(source_root).unwrap(); | 265 | db.item_map(source_root).unwrap(); |
184 | }); | 266 | }); |
185 | assert!( | 267 | assert!( |
186 | !format!("{:?}", events).contains("_item_map"), | 268 | !format!("{:?}", events).contains("item_map"), |
187 | "{:#?}", | 269 | "{:#?}", |
188 | events | 270 | events |
189 | ) | 271 | ) |
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index 721bd4195..a5d99beda 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs | |||
@@ -8,10 +8,11 @@ use ra_syntax::{ | |||
8 | AstNode, SyntaxNode, | 8 | AstNode, SyntaxNode, |
9 | ast::{self, NameOwner, ModuleItemOwner} | 9 | ast::{self, NameOwner, ModuleItemOwner} |
10 | }; | 10 | }; |
11 | use ra_db::{SourceRootId, FileId, Cancelable,}; | 11 | use ra_db::{SourceRootId, Cancelable,}; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, | 14 | SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId, |
15 | MacroCallLoc, | ||
15 | db::HirDatabase, | 16 | db::HirDatabase, |
16 | function::FnScopes, | 17 | function::FnScopes, |
17 | module::{ | 18 | module::{ |
@@ -47,25 +48,17 @@ pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc< | |||
47 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) | 48 | Ok(Arc::new(EnumData::new(enum_def.borrowed()))) |
48 | } | 49 | } |
49 | 50 | ||
50 | pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { | 51 | pub(super) fn file_items(db: &impl HirDatabase, file_id: HirFileId) -> Arc<SourceFileItems> { |
51 | let mut res = SourceFileItems::new(file_id); | 52 | let source_file = db.hir_source_file(file_id); |
52 | let source_file = db.source_file(file_id); | ||
53 | let source_file = source_file.borrowed(); | 53 | let source_file = source_file.borrowed(); |
54 | source_file | 54 | let res = SourceFileItems::new(file_id, source_file); |
55 | .syntax() | ||
56 | .descendants() | ||
57 | .filter_map(ast::ModuleItem::cast) | ||
58 | .map(|it| it.syntax().owned()) | ||
59 | .for_each(|it| { | ||
60 | res.alloc(it); | ||
61 | }); | ||
62 | Arc::new(res) | 55 | Arc::new(res) |
63 | } | 56 | } |
64 | 57 | ||
65 | pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { | 58 | pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { |
66 | match source_item_id.item_id { | 59 | match source_item_id.item_id { |
67 | Some(id) => db.file_items(source_item_id.file_id)[id].clone(), | 60 | Some(id) => db.file_items(source_item_id.file_id)[id].clone(), |
68 | None => db.source_file(source_item_id.file_id).syntax().owned(), | 61 | None => db.hir_source_file(source_item_id.file_id).syntax().owned(), |
69 | } | 62 | } |
70 | } | 63 | } |
71 | 64 | ||
@@ -87,7 +80,7 @@ pub(crate) fn submodules( | |||
87 | 80 | ||
88 | fn collect_submodules<'a>( | 81 | fn collect_submodules<'a>( |
89 | db: &impl HirDatabase, | 82 | db: &impl HirDatabase, |
90 | file_id: FileId, | 83 | file_id: HirFileId, |
91 | root: impl ast::ModuleItemOwner<'a>, | 84 | root: impl ast::ModuleItemOwner<'a>, |
92 | ) -> Vec<Submodule> { | 85 | ) -> Vec<Submodule> { |
93 | modules(root) | 86 | modules(root) |
@@ -119,24 +112,48 @@ pub(crate) fn modules<'a>( | |||
119 | 112 | ||
120 | pub(super) fn input_module_items( | 113 | pub(super) fn input_module_items( |
121 | db: &impl HirDatabase, | 114 | db: &impl HirDatabase, |
122 | source_root: SourceRootId, | 115 | source_root_id: SourceRootId, |
123 | module_id: ModuleId, | 116 | module_id: ModuleId, |
124 | ) -> Cancelable<Arc<InputModuleItems>> { | 117 | ) -> Cancelable<Arc<InputModuleItems>> { |
125 | let module_tree = db.module_tree(source_root)?; | 118 | let module_tree = db.module_tree(source_root_id)?; |
126 | let source = module_id.source(&module_tree); | 119 | let source = module_id.source(&module_tree); |
127 | let file_items = db.file_items(source.file_id()); | 120 | let file_id = source.file_id(); |
128 | let res = match source.resolve(db) { | 121 | let file_items = db.file_items(file_id); |
129 | ModuleSourceNode::SourceFile(it) => { | 122 | let fill = |acc: &mut InputModuleItems, items: &mut Iterator<Item = ast::ItemOrMacro>| { |
130 | let items = it.borrowed().items(); | 123 | for item in items { |
131 | InputModuleItems::new(&file_items, items) | 124 | match item { |
125 | ast::ItemOrMacro::Item(it) => { | ||
126 | acc.add_item(file_id, &file_items, it); | ||
127 | } | ||
128 | ast::ItemOrMacro::Macro(macro_call) => { | ||
129 | let item_id = file_items.id_of_unchecked(macro_call.syntax()); | ||
130 | let loc = MacroCallLoc { | ||
131 | source_root_id, | ||
132 | module_id, | ||
133 | source_item_id: SourceItemId { | ||
134 | file_id, | ||
135 | item_id: Some(item_id), | ||
136 | }, | ||
137 | }; | ||
138 | let id = loc.id(db); | ||
139 | let file_id = HirFileId::from(id); | ||
140 | let file_items = db.file_items(file_id); | ||
141 | //FIXME: expand recursively | ||
142 | for item in db.hir_source_file(file_id).borrowed().items() { | ||
143 | acc.add_item(file_id, &file_items, item); | ||
144 | } | ||
145 | } | ||
146 | } | ||
132 | } | 147 | } |
148 | }; | ||
149 | |||
150 | let mut res = InputModuleItems::default(); | ||
151 | match source.resolve(db) { | ||
152 | ModuleSourceNode::SourceFile(it) => fill(&mut res, &mut it.borrowed().items_with_macros()), | ||
133 | ModuleSourceNode::Module(it) => { | 153 | ModuleSourceNode::Module(it) => { |
134 | let items = it | 154 | if let Some(item_list) = it.borrowed().item_list() { |
135 | .borrowed() | 155 | fill(&mut res, &mut item_list.items_with_macros()) |
136 | .item_list() | 156 | } |
137 | .into_iter() | ||
138 | .flat_map(|it| it.items()); | ||
139 | InputModuleItems::new(&file_items, items) | ||
140 | } | 157 | } |
141 | }; | 158 | }; |
142 | Ok(Arc::new(res)) | 159 | Ok(Arc::new(res)) |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a0d1daf71..24490d119 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -20,7 +20,7 @@ use crate::{ | |||
20 | 20 | ||
21 | /// Locates the module by `FileId`. Picks topmost module in the file. | 21 | /// Locates the module by `FileId`. Picks topmost module in the file. |
22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { | 22 | pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { |
23 | let module_source = ModuleSource::new_file(file_id); | 23 | let module_source = ModuleSource::new_file(file_id.into()); |
24 | module_from_source(db, module_source) | 24 | module_from_source(db, module_source) |
25 | } | 25 | } |
26 | 26 | ||
@@ -50,8 +50,8 @@ pub fn module_from_position( | |||
50 | ) -> Cancelable<Option<Module>> { | 50 | ) -> Cancelable<Option<Module>> { |
51 | let file = db.source_file(position.file_id); | 51 | let file = db.source_file(position.file_id); |
52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { | 52 | let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset) { |
53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id, m), | 53 | Some(m) if !m.has_semi() => ModuleSource::new_inline(db, position.file_id.into(), m), |
54 | _ => ModuleSource::new_file(position.file_id), | 54 | _ => ModuleSource::new_file(position.file_id.into()), |
55 | }; | 55 | }; |
56 | module_from_source(db, module_source) | 56 | module_from_source(db, module_source) |
57 | } | 57 | } |
@@ -67,9 +67,9 @@ pub fn module_from_child_node( | |||
67 | .filter_map(ast::Module::cast) | 67 | .filter_map(ast::Module::cast) |
68 | .find(|it| !it.has_semi()) | 68 | .find(|it| !it.has_semi()) |
69 | { | 69 | { |
70 | ModuleSource::new_inline(db, file_id, m) | 70 | ModuleSource::new_inline(db, file_id.into(), m) |
71 | } else { | 71 | } else { |
72 | ModuleSource::new_file(file_id) | 72 | ModuleSource::new_file(file_id.into()) |
73 | }; | 73 | }; |
74 | module_from_source(db, module_source) | 74 | module_from_source(db, module_source) |
75 | } | 75 | } |
@@ -78,7 +78,7 @@ fn module_from_source( | |||
78 | db: &impl HirDatabase, | 78 | db: &impl HirDatabase, |
79 | module_source: ModuleSource, | 79 | module_source: ModuleSource, |
80 | ) -> Cancelable<Option<Module>> { | 80 | ) -> Cancelable<Option<Module>> { |
81 | let source_root_id = db.file_source_root(module_source.file_id()); | 81 | let source_root_id = db.file_source_root(module_source.file_id().as_original_file()); |
82 | let module_tree = db.module_tree(source_root_id)?; | 82 | let module_tree = db.module_tree(source_root_id)?; |
83 | let m = module_tree | 83 | let m = module_tree |
84 | .modules_with_sources() | 84 | .modules_with_sources() |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 618486250..1107ffc8b 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -2,7 +2,7 @@ use languageserver_types::{ | |||
2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, | 2 | self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, |
3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, | 3 | TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, |
4 | }; | 4 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText}; | 5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText, NavigationTarget}; |
6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; | 6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; |
7 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 7 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; |
@@ -322,6 +322,15 @@ impl TryConvWith for FileSystemEdit { | |||
322 | } | 322 | } |
323 | } | 323 | } |
324 | 324 | ||
325 | impl TryConvWith for &NavigationTarget { | ||
326 | type Ctx = ServerWorld; | ||
327 | type Output = Location; | ||
328 | fn try_conv_with(self, world: &ServerWorld) -> Result<Location> { | ||
329 | let line_index = world.analysis().file_line_index(self.file_id()); | ||
330 | to_location(self.file_id(), self.range(), &world, &line_index) | ||
331 | } | ||
332 | } | ||
333 | |||
325 | pub fn to_location( | 334 | pub fn to_location( |
326 | file_id: FileId, | 335 | file_id: FileId, |
327 | range: TextRange, | 336 | range: TextRange, |
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 11825d74e..26b6c7d8a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs | |||
@@ -188,12 +188,11 @@ pub fn handle_workspace_symbol( | |||
188 | 188 | ||
189 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { | 189 | fn exec_query(world: &ServerWorld, query: Query) -> Result<Vec<SymbolInformation>> { |
190 | let mut res = Vec::new(); | 190 | let mut res = Vec::new(); |
191 | for (file_id, symbol) in world.analysis().symbol_search(query)? { | 191 | for nav in world.analysis().symbol_search(query)? { |
192 | let line_index = world.analysis().file_line_index(file_id); | ||
193 | let info = SymbolInformation { | 192 | let info = SymbolInformation { |
194 | name: symbol.name.to_string(), | 193 | name: nav.name().into(), |
195 | kind: symbol.kind.conv(), | 194 | kind: nav.kind().conv(), |
196 | location: to_location(file_id, symbol.node_range, world, &line_index)?, | 195 | location: nav.try_conv_with(world)?, |
197 | container_name: None, | 196 | container_name: None, |
198 | deprecated: None, | 197 | deprecated: None, |
199 | }; | 198 | }; |
@@ -212,12 +211,11 @@ pub fn handle_goto_definition( | |||
212 | None => return Ok(None), | 211 | None => return Ok(None), |
213 | Some(it) => it, | 212 | Some(it) => it, |
214 | }; | 213 | }; |
215 | let mut res = Vec::new(); | 214 | let res = rr |
216 | for (file_id, symbol) in rr.resolves_to { | 215 | .resolves_to |
217 | let line_index = world.analysis().file_line_index(file_id); | 216 | .into_iter() |
218 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 217 | .map(|nav| nav.try_conv_with(&world)) |
219 | res.push(location) | 218 | .collect::<Result<Vec<_>>>()?; |
220 | } | ||
221 | Ok(Some(req::GotoDefinitionResponse::Array(res))) | 219 | Ok(Some(req::GotoDefinitionResponse::Array(res))) |
222 | } | 220 | } |
223 | 221 | ||
@@ -226,13 +224,12 @@ pub fn handle_parent_module( | |||
226 | params: req::TextDocumentPositionParams, | 224 | params: req::TextDocumentPositionParams, |
227 | ) -> Result<Vec<Location>> { | 225 | ) -> Result<Vec<Location>> { |
228 | let position = params.try_conv_with(&world)?; | 226 | let position = params.try_conv_with(&world)?; |
229 | let mut res = Vec::new(); | 227 | world |
230 | for (file_id, symbol) in world.analysis().parent_module(position)? { | 228 | .analysis() |
231 | let line_index = world.analysis().file_line_index(file_id); | 229 | .parent_module(position)? |
232 | let location = to_location(file_id, symbol.node_range, &world, &line_index)?; | 230 | .into_iter() |
233 | res.push(location); | 231 | .map(|nav| nav.try_conv_with(&world)) |
234 | } | 232 | .collect::<Result<Vec<_>>>() |
235 | Ok(res) | ||
236 | } | 233 | } |
237 | 234 | ||
238 | pub fn handle_runnables( | 235 | pub fn handle_runnables( |
@@ -517,8 +514,8 @@ pub fn handle_hover( | |||
517 | Some(it) => it, | 514 | Some(it) => it, |
518 | }; | 515 | }; |
519 | let mut result = Vec::new(); | 516 | let mut result = Vec::new(); |
520 | for (file_id, symbol) in rr.resolves_to { | 517 | for nav in rr.resolves_to { |
521 | if let Some(docs) = world.analysis().doc_text_for(file_id, symbol)? { | 518 | if let Some(docs) = world.analysis().doc_text_for(nav)? { |
522 | result.push(docs); | 519 | result.push(docs); |
523 | } | 520 | } |
524 | } | 521 | } |
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 8fb6b6408..3e948800e 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -48,10 +48,40 @@ pub trait FnDefOwner<'a>: AstNode<'a> { | |||
48 | } | 48 | } |
49 | } | 49 | } |
50 | 50 | ||
51 | // ModuleItem | ||
52 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||
53 | pub enum ItemOrMacro<'a> { | ||
54 | Item(ModuleItem<'a>), | ||
55 | Macro(MacroCall<'a>), | ||
56 | } | ||
57 | |||
58 | impl<'a> AstNode<'a> for ItemOrMacro<'a> { | ||
59 | fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> { | ||
60 | let res = if let Some(item) = ModuleItem::cast(syntax) { | ||
61 | ItemOrMacro::Item(item) | ||
62 | } else if let Some(macro_call) = MacroCall::cast(syntax) { | ||
63 | ItemOrMacro::Macro(macro_call) | ||
64 | } else { | ||
65 | return None; | ||
66 | }; | ||
67 | Some(res) | ||
68 | } | ||
69 | fn syntax(self) -> SyntaxNodeRef<'a> { | ||
70 | match self { | ||
71 | ItemOrMacro::Item(it) => it.syntax(), | ||
72 | ItemOrMacro::Macro(it) => it.syntax(), | ||
73 | } | ||
74 | } | ||
75 | } | ||
76 | |||
51 | pub trait ModuleItemOwner<'a>: AstNode<'a> { | 77 | pub trait ModuleItemOwner<'a>: AstNode<'a> { |
52 | fn items(self) -> AstChildren<'a, ModuleItem<'a>> { | 78 | fn items(self) -> AstChildren<'a, ModuleItem<'a>> { |
53 | children(self) | 79 | children(self) |
54 | } | 80 | } |
81 | |||
82 | fn items_with_macros(self) -> AstChildren<'a, ItemOrMacro<'a>> { | ||
83 | children(self) | ||
84 | } | ||
55 | } | 85 | } |
56 | 86 | ||
57 | pub trait TypeParamsOwner<'a>: AstNode<'a> { | 87 | pub trait TypeParamsOwner<'a>: AstNode<'a> { |
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs index 34a3aabef..6753c513f 100644 --- a/crates/ra_syntax/src/lib.rs +++ b/crates/ra_syntax/src/lib.rs | |||
@@ -51,7 +51,7 @@ use ra_text_edit::AtomTextEdit; | |||
51 | use crate::yellow::GreenNode; | 51 | use crate::yellow::GreenNode; |
52 | 52 | ||
53 | /// `SourceFileNode` represents a parse tree for a single Rust file. | 53 | /// `SourceFileNode` represents a parse tree for a single Rust file. |
54 | pub use crate::ast::SourceFileNode; | 54 | pub use crate::ast::{SourceFile, SourceFileNode}; |
55 | 55 | ||
56 | impl SourceFileNode { | 56 | impl SourceFileNode { |
57 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode { | 57 | fn new(green: GreenNode, errors: Vec<SyntaxError>) -> SourceFileNode { |
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index 757eac95b..5bbc3e993 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -11,7 +11,7 @@ | |||
11 | //! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131) | 11 | //! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131) |
12 | //! | 12 | //! |
13 | //! VFS is based on a concept of roots: a set of directories on the file system | 13 | //! VFS is based on a concept of roots: a set of directories on the file system |
14 | //! whihc are watched for changes. Typically, there will be a root for each | 14 | //! which are watched for changes. Typically, there will be a root for each |
15 | //! Cargo package. | 15 | //! Cargo package. |
16 | mod arena; | 16 | mod arena; |
17 | mod io; | 17 | mod io; |