aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_analysis/Cargo.toml2
-rw-r--r--crates/ra_analysis/src/completion.rs2
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs2
-rw-r--r--crates/ra_analysis/src/db.rs16
-rw-r--r--crates/ra_analysis/src/extend_selection.rs10
-rw-r--r--crates/ra_analysis/src/imp.rs255
-rw-r--r--crates/ra_analysis/src/lib.rs194
-rw-r--r--crates/ra_analysis/src/macros.rs75
-rw-r--r--crates/ra_analysis/src/runnables.rs106
-rw-r--r--crates/ra_analysis/src/symbol_index.rs165
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs30
-rw-r--r--crates/ra_analysis/tests/tests.rs60
-rw-r--r--crates/ra_editor/src/extend_selection.rs24
-rw-r--r--crates/ra_editor/src/lib.rs4
-rw-r--r--crates/ra_editor/src/structure.rs129
-rw-r--r--crates/ra_editor/src/symbols.rs246
-rw-r--r--crates/ra_hir/src/adt.rs3
-rw-r--r--crates/ra_hir/src/db.rs19
-rw-r--r--crates/ra_hir/src/ids.rs280
-rw-r--r--crates/ra_hir/src/krate.rs5
-rw-r--r--crates/ra_hir/src/lib.rs169
-rw-r--r--crates/ra_hir/src/macros.rs181
-rw-r--r--crates/ra_hir/src/mock.rs10
-rw-r--r--crates/ra_hir/src/module.rs27
-rw-r--r--crates/ra_hir/src/module/imp.rs6
-rw-r--r--crates/ra_hir/src/module/nameres.rs85
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs84
-rw-r--r--crates/ra_hir/src/query_definitions.rs73
-rw-r--r--crates/ra_hir/src/source_binder.rs12
-rw-r--r--crates/ra_lsp_server/src/conv.rs11
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs37
-rw-r--r--crates/ra_syntax/src/ast.rs30
-rw-r--r--crates/ra_syntax/src/lib.rs2
-rw-r--r--crates/ra_vfs/src/lib.rs2
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"
667version = "0.1.0" 667version = "0.1.0"
668dependencies = [ 668dependencies = [
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"
5authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8itertools = "0.8.0"
8log = "0.4.5" 9log = "0.4.5"
9relative-path = "0.4.0" 10relative-path = "0.4.0"
10rayon = "1.0.2" 11rayon = "1.0.2"
@@ -12,6 +13,7 @@ fst = "0.3.1"
12salsa = "0.9.0" 13salsa = "0.9.0"
13rustc-hash = "1.0" 14rustc-hash = "1.0"
14parking_lot = "0.7.0" 15parking_lot = "0.7.0"
16
15ra_syntax = { path = "../ra_syntax" } 17ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 18ra_editor = { path = "../ra_editor" }
17ra_text_edit = { path = "../ra_text_edit" } 19ra_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 @@
1use std::{fmt, sync::Arc}; 1use std::{fmt, sync::Arc};
2use salsa::{self, Database}; 2use salsa::{self, Database};
3use ra_db::{LocationIntener, BaseDatabase}; 3use ra_db::{LocationIntener, BaseDatabase};
4use hir::{self, DefId, DefLoc};
5 4
6use crate::{ 5use crate::{
7 symbol_index, 6 symbol_index,
@@ -15,7 +14,8 @@ pub(crate) struct RootDatabase {
15 14
16#[derive(Default)] 15#[derive(Default)]
17struct IdMaps { 16struct IdMaps {
18 defs: LocationIntener<DefLoc, DefId>, 17 defs: LocationIntener<hir::DefLoc, hir::DefId>,
18 macros: LocationIntener<hir::MacroCallLoc, hir::MacroCallId>,
19} 19}
20 20
21impl fmt::Debug for IdMaps { 21impl fmt::Debug for IdMaps {
@@ -59,12 +59,18 @@ impl salsa::ParallelDatabase for RootDatabase {
59 59
60impl BaseDatabase for RootDatabase {} 60impl BaseDatabase for RootDatabase {}
61 61
62impl AsRef<LocationIntener<DefLoc, DefId>> for RootDatabase { 62impl 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
68impl 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
68salsa::database_storage! { 74salsa::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
20fn extend_selection_in_macro( 20fn 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 @@
1use std::{ 1use std::sync::Arc;
2 fmt,
3 sync::Arc,
4};
5 2
6use rayon::prelude::*; 3use salsa::Database;
7use salsa::{Database, ParallelDatabase};
8 4
9use hir::{ 5use hir::{
10 self, FnSignatureInfo, Problem, source_binder, 6 self, FnSignatureInfo, Problem, source_binder,
11}; 7};
12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 8use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
13use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; 9use ra_editor::{self, find_node_at_offset, LocalEdit, Severity};
14use ra_syntax::{ 10use 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
22use crate::{ 18use 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)] 26impl db::RootDatabase {
32pub(crate) struct AnalysisHostImpl { 27 pub(crate) fn apply_change(&mut self, change: AnalysisChange) {
33 db: db::RootDatabase,
34}
35
36impl 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
131pub(crate) struct AnalysisImpl { 102impl db::RootDatabase {
132 pub(crate) db: salsa::Snapshot<db::RootDatabase>,
133}
134
135impl 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
142impl 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
20mod extend_selection; 20mod extend_selection;
21mod syntax_highlighting; 21mod syntax_highlighting;
22mod macros;
23 22
24use std::{fmt, sync::Arc}; 23use std::{fmt, sync::Arc};
25 24
26use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
27use ra_syntax::{SourceFileNode, TextRange, TextUnit}; 26use ra_syntax::{SourceFileNode, TextRange, TextUnit, SmolStr, SyntaxKind};
28use ra_text_edit::TextEdit; 27use ra_text_edit::TextEdit;
29use rayon::prelude::*; 28use rayon::prelude::*;
30use relative_path::RelativePathBuf; 29use relative_path::RelativePathBuf;
30use salsa::ParallelDatabase;
31 31
32use crate::{ 32use crate::symbol_index::{SymbolIndex, FileSymbol};
33 imp::{AnalysisHostImpl, AnalysisImpl},
34 symbol_index::SymbolIndex,
35};
36 33
37pub use crate::{ 34pub use crate::{
38 completion::{CompletionItem, CompletionItemKind, InsertText}, 35 completion::{CompletionItem, CompletionItemKind, InsertText},
39 runnables::{Runnable, RunnableKind} 36 runnables::{Runnable, RunnableKind},
40}; 37};
41pub use ra_editor::{ 38pub use ra_editor::{
42 FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity 39 Fold, FoldKind, HighlightedRange, LineIndex, StructureNode, Severity
43}; 40};
44pub use hir::FnSignatureInfo; 41pub use hir::FnSignatureInfo;
45 42
46pub use ra_db::{ 43pub 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)]
156pub struct AnalysisHost {
157 imp: AnalysisHostImpl,
158}
159
160impl 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)]
176pub struct SourceChange { 152pub 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)]
223pub struct NavigationTarget {
224 file_id: FileId,
225 symbol: FileSymbol,
226}
227
228impl 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)]
248pub struct ReferenceResolution { 245pub 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
257impl ReferenceResolution { 254impl 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)]
269pub struct AnalysisHost {
270 db: db::RootDatabase,
271}
272
273impl 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)]
275pub struct Analysis { 293pub struct Analysis {
276 pub(crate) imp: AnalysisImpl, 294 db: salsa::Snapshot<db::RootDatabase>,
277} 295}
278 296
279impl Analysis { 297impl 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.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(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
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl 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 @@
1use itertools::Itertools;
1use ra_syntax::{ 2use ra_syntax::{
2 ast::{self, AstNode, NameOwner, ModuleItemOwner}, 3 ast::{self, AstNode, NameOwner, ModuleItemOwner},
3 SourceFileNode, TextRange, SyntaxNodeRef, 4 TextRange, SyntaxNodeRef,
4 TextUnit,
5};
6use crate::{
7 Analysis, FileId, FilePosition
8}; 5};
6use ra_db::{Cancelable, SyntaxDatabase};
7
8use crate::{db::RootDatabase, FileId};
9 9
10#[derive(Debug)] 10#[derive(Debug)]
11pub struct Runnable { 11pub struct Runnable {
@@ -20,53 +20,67 @@ pub enum RunnableKind {
20 Bin, 20 Bin,
21} 21}
22 22
23pub fn runnables( 23pub(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
35fn runnable<'a>(analysis: &Analysis, item: SyntaxNodeRef<'a>, file_id: FileId) -> Option<Runnable> { 33fn 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
43fn 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
60fn 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
6use fst::{self, Streamer}; 6use fst::{self, Streamer};
7use ra_editor::{self, FileSymbol};
8use ra_syntax::{ 7use 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};
12use ra_db::{SyntaxDatabase, SourceRootId}; 13use ra_db::{SyntaxDatabase, SourceRootId, FilesDatabase};
14use salsa::ParallelDatabase;
13use rayon::prelude::*; 15use rayon::prelude::*;
14 16
15use crate::{ 17use crate::{
16 Cancelable, 18 Cancelable, FileId, Query,
17 FileId, Query, 19 db::RootDatabase,
18}; 20};
19 21
20salsa::query_group! { 22salsa::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
40pub(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)]
39pub(crate) struct SymbolIndex { 76pub(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)]
164pub(crate) struct FileSymbol {
165 pub(crate) name: SmolStr,
166 pub(crate) node_range: TextRange,
167 pub(crate) kind: SyntaxKind,
168}
169
170impl 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
255fn 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]
135fn 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]
151fn 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]
169fn 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]
185fn test_resolve_crate_root() { 135fn 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
8pub fn extend_selection(root: SyntaxNodeRef, range: TextRange) -> Option<TextRange> { 8pub 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
41fn extend_single_word_in_comment(leaf: SyntaxNodeRef, offset: TextUnit) -> Option<TextRange> { 42fn 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#"
274fn 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;
3mod folding_ranges; 3mod folding_ranges;
4mod line_index; 4mod line_index;
5mod line_index_utils; 5mod line_index_utils;
6mod symbols; 6mod structure;
7#[cfg(test)] 7#[cfg(test)]
8mod test_utils; 8mod test_utils;
9mod typing; 9mod 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 @@
1use crate::TextRange;
2
3use ra_syntax::{
4 algo::visit::{visitor, Visitor},
5 ast::{self, NameOwner},
6 AstNode, SourceFileNode, SyntaxKind, SyntaxNodeRef, WalkEvent,
7};
8
9#[derive(Debug, Clone)]
10pub 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
18pub 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
41fn 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)]
88mod 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#"
96struct Foo {
97 x: i32
98}
99
100mod m {
101 fn bar() {}
102}
103
104enum E { X, Y(i32) }
105type T = ();
106static S: i32 = 92;
107const C: i32 = 92;
108
109impl E {}
110
111impl 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 @@
1use crate::TextRange;
2
3use 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)]
10pub 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)]
19pub struct FileSymbol {
20 pub name: SmolStr,
21 pub node_range: TextRange,
22 pub kind: SyntaxKind,
23}
24
25impl 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
110pub fn file_symbols(file: &SourceFileNode) -> Vec<FileSymbol> {
111 file.syntax().descendants().filter_map(to_symbol).collect()
112}
113
114fn 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
135pub 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
158fn 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)]
205mod 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#"
213struct Foo {
214 x: i32
215}
216
217mod m {
218 fn bar() {}
219}
220
221enum E { X, Y(i32) }
222type T = ();
223static S: i32 = 92;
224const C: i32 = 92;
225
226impl E {}
227
228impl 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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_db::Cancelable;
3use ra_syntax::ast::{self, NameOwner, StructFlavor}; 4use ra_syntax::ast::{self, NameOwner, StructFlavor};
4 5
5use crate::{ 6use 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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::SyntaxNode; 3use ra_syntax::{SyntaxNode, SourceFileNode};
4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; 4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, Cancelable};
5 5
6use crate::{ 6use 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
19pub trait HirDatabase: SyntaxDatabase 20pub 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 @@
1use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
2use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
3
4use 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)]
29pub struct HirFileId(HirFileIdRepr);
30
31impl 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)]
66enum HirFileIdRepr {
67 File(FileId),
68 Macro(MacroCallId),
69}
70
71impl From<FileId> for HirFileId {
72 fn from(file_id: FileId) -> HirFileId {
73 HirFileId(HirFileIdRepr::File(file_id))
74 }
75}
76
77impl 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)]
86pub struct MacroCallId(u32);
87ra_db::impl_numeric_id!(MacroCallId);
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub struct MacroCallLoc {
91 pub(crate) source_root_id: SourceRootId,
92 pub(crate) module_id: ModuleId,
93 pub(crate) source_item_id: SourceItemId,
94}
95
96impl 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
105impl 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)]
118pub struct DefId(u32);
119ra_db::impl_numeric_id!(DefId);
120
121#[derive(Clone, Debug, PartialEq, Eq, Hash)]
122pub 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)]
130pub(crate) enum DefKind {
131 Module,
132 Function,
133 Struct,
134 Enum,
135 Item,
136
137 StructCtor,
138}
139
140impl 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
177impl DefLoc {
178 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
179 db.as_ref().loc2id(&self)
180 }
181}
182
183impl 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.
202pub(crate) type SourceFileItemId = Id<SyntaxNode>;
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
205pub 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)]
213pub struct SourceFileItems {
214 file_id: HirFileId,
215 arena: Arena<SyntaxNode>,
216}
217
218impl 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
275impl 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 @@
1pub use ra_db::CrateId; 1pub use ra_db::{CrateId, Cancelable};
2 2
3use crate::{HirDatabase, Module, Cancelable, Name, AsName}; 3use 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;
22mod arena; 22mod arena;
23pub mod source_binder; 23pub mod source_binder;
24 24
25mod ids;
26mod macros;
25mod name; 27mod name;
28// can't use `crate` or `r#crate` here :(
26mod krate; 29mod krate;
27mod module; 30mod module;
28mod function; 31mod function;
@@ -30,21 +33,18 @@ mod adt;
30mod type_ref; 33mod type_ref;
31mod ty; 34mod ty;
32 35
33use std::ops::Index;
34
35use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
36use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
37
38use crate::{ 36use 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
44pub use self::{ 42pub 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
54pub use self::function::FnSignatureInfo; 54pub 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)]
59pub struct DefId(u32);
60ra_db::impl_numeric_id!(DefId);
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
63pub(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)]
74pub struct DefLoc {
75 pub(crate) kind: DefKind,
76 source_root_id: SourceRootId,
77 module_id: ModuleId,
78 source_item_id: SourceItemId,
79}
80
81impl 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
98impl DefId {
99 pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
100 db.as_ref().id2loc(self)
101 }
102}
103
104impl DefLoc {
105 pub(crate) fn id(&self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefId {
106 db.as_ref().loc2id(&self)
107 }
108}
109
110pub enum Def { 56pub 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
118impl 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.
153pub(crate) type SourceFileItemId = Id<SyntaxNode>;
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
156pub 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)]
164pub struct SourceFileItems {
165 file_id: FileId,
166 arena: Arena<SyntaxNode>,
167}
168
169impl 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
214impl 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!
10use std::sync::Arc;
11
12use ra_db::LocalSyntaxPtr;
13use ra_syntax::{
14 TextRange, TextUnit, SourceFileNode, AstNode, SyntaxNode,
15 ast::{self, NameOwner},
16};
17
18use crate::{HirDatabase, MacroCallId};
19
20// Hard-coded defs for now :-(
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub enum MacroDef {
23 CTry,
24 QueryGroup,
25}
26
27impl 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)]
116pub struct MacroInput {
117 // Should be token trees
118 pub text: String,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub 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
134impl 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
170pub(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
6use relative_path::RelativePathBuf; 6use relative_path::RelativePathBuf;
7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset}; 7use test_utils::{parse_fixture, CURSOR_MARKER, extract_offset};
8 8
9use crate::{db, DefId, DefLoc}; 9use crate::{db, DefId, DefLoc, MacroCallId, MacroCallLoc};
10 10
11pub const WORKSPACE: SourceRootId = SourceRootId(0); 11pub const WORKSPACE: SourceRootId = SourceRootId(0);
12 12
@@ -95,6 +95,7 @@ impl MockDatabase {
95#[derive(Debug, Default)] 95#[derive(Debug, Default)]
96struct IdMaps { 96struct IdMaps {
97 defs: LocationIntener<DefLoc, DefId>, 97 defs: LocationIntener<DefLoc, DefId>,
98 macros: LocationIntener<MacroCallLoc, MacroCallId>,
98} 99}
99 100
100impl salsa::Database for MockDatabase { 101impl 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}
148impl AsRef<LocationIntener<MacroCallLoc, MacroCallId>> for MockDatabase {
149 fn as_ref(&self) -> &LocationIntener<MacroCallLoc, MacroCallId> {
150 &self.id_maps.macros
151 }
152}
147 153
148impl MockDatabase { 154impl 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;
15use crate::{ 15use 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
292impl ModuleSource { 301impl 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};
25use ra_db::SourceRootId; 25use ra_db::{SourceRootId, Cancelable, FileId};
26 26
27use crate::{ 27use 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)]
72struct ModuleItem { 72struct 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
97impl NamedImport { 97impl 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
211impl InputModuleItems { 213impl 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
260impl ModuleItem { 272impl 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]
81fn 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]
81fn item_map_using_self() { 110fn 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]
225fn 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};
11use ra_db::{SourceRootId, FileId, Cancelable,}; 11use ra_db::{SourceRootId, Cancelable,};
12 12
13use crate::{ 13use 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
50pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> { 51pub(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
65pub(super) fn file_item(db: &impl HirDatabase, source_item_id: SourceItemId) -> SyntaxNode { 58pub(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
120pub(super) fn input_module_items( 113pub(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.
22pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable<Option<Module>> { 22pub 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};
5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText}; 5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText, NavigationTarget};
6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; 6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit};
7use ra_text_edit::{AtomTextEdit, TextEdit}; 7use ra_text_edit::{AtomTextEdit, TextEdit};
8use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 8use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -322,6 +322,15 @@ impl TryConvWith for FileSystemEdit {
322 } 322 }
323} 323}
324 324
325impl 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
325pub fn to_location( 334pub 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
238pub fn handle_runnables( 235pub 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)]
53pub enum ItemOrMacro<'a> {
54 Item(ModuleItem<'a>),
55 Macro(MacroCall<'a>),
56}
57
58impl<'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
51pub trait ModuleItemOwner<'a>: AstNode<'a> { 77pub 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
57pub trait TypeParamsOwner<'a>: AstNode<'a> { 87pub 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;
51use crate::yellow::GreenNode; 51use 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.
54pub use crate::ast::SourceFileNode; 54pub use crate::ast::{SourceFile, SourceFileNode};
55 55
56impl SourceFileNode { 56impl 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.
16mod arena; 16mod arena;
17mod io; 17mod io;