aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_analysis/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_analysis/src')
-rw-r--r--crates/ra_analysis/src/completion.rs33
-rw-r--r--crates/ra_analysis/src/descriptors/mod.rs8
-rw-r--r--crates/ra_analysis/src/descriptors/module/imp.rs149
-rw-r--r--crates/ra_analysis/src/descriptors/module/mod.rs29
-rw-r--r--crates/ra_analysis/src/imp.rs93
-rw-r--r--crates/ra_analysis/src/lib.rs60
-rw-r--r--crates/ra_analysis/src/mock_analysis.rs9
-rw-r--r--crates/ra_analysis/src/syntax_ptr.rs4
8 files changed, 224 insertions, 161 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index 6667c06e7..7c3476e5c 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -11,10 +11,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
11use crate::{ 11use crate::{
12 db::{self, SyntaxDatabase}, 12 db::{self, SyntaxDatabase},
13 descriptors::function::FnScopes, 13 descriptors::function::FnScopes,
14 descriptors::module::{ModuleId, ModuleScope, ModuleTree}, 14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
15 descriptors::DescriptorDatabase, 15 descriptors::DescriptorDatabase,
16 input::FilesDatabase, 16 input::FilesDatabase,
17 Cancelable, FileId, 17 Cancelable, FilePosition,
18}; 18};
19 19
20#[derive(Debug)] 20#[derive(Debug)]
@@ -29,21 +29,21 @@ pub struct CompletionItem {
29 29
30pub(crate) fn resolve_based_completion( 30pub(crate) fn resolve_based_completion(
31 db: &db::RootDatabase, 31 db: &db::RootDatabase,
32 file_id: FileId, 32 position: FilePosition,
33 offset: TextUnit,
34) -> Cancelable<Option<Vec<CompletionItem>>> { 33) -> Cancelable<Option<Vec<CompletionItem>>> {
35 let source_root_id = db.file_source_root(file_id); 34 let source_root_id = db.file_source_root(position.file_id);
36 let file = db.file_syntax(file_id); 35 let file = db.file_syntax(position.file_id);
37 let module_tree = db.module_tree(source_root_id)?; 36 let module_tree = db.module_tree(source_root_id)?;
38 let module_id = match module_tree.any_module_for_file(file_id) { 37 let module_id = match module_tree.any_module_for_source(ModuleSource::File(position.file_id)) {
39 None => return Ok(None), 38 None => return Ok(None),
40 Some(it) => it, 39 Some(it) => it,
41 }; 40 };
42 let file = { 41 let file = {
43 let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); 42 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
44 file.reparse(&edit) 43 file.reparse(&edit)
45 }; 44 };
46 let target_module_id = match find_target_module(&module_tree, module_id, &file, offset) { 45 let target_module_id = match find_target_module(&module_tree, module_id, &file, position.offset)
46 {
47 None => return Ok(None), 47 None => return Ok(None),
48 Some(it) => it, 48 Some(it) => it,
49 }; 49 };
@@ -99,18 +99,17 @@ fn crate_path(name_ref: ast::NameRef) -> Option<Vec<ast::NameRef>> {
99 99
100pub(crate) fn scope_completion( 100pub(crate) fn scope_completion(
101 db: &db::RootDatabase, 101 db: &db::RootDatabase,
102 file_id: FileId, 102 position: FilePosition,
103 offset: TextUnit,
104) -> Option<Vec<CompletionItem>> { 103) -> Option<Vec<CompletionItem>> {
105 let original_file = db.file_syntax(file_id); 104 let original_file = db.file_syntax(position.file_id);
106 // Insert a fake ident to get a valid parse tree 105 // Insert a fake ident to get a valid parse tree
107 let file = { 106 let file = {
108 let edit = AtomEdit::insert(offset, "intellijRulezz".to_string()); 107 let edit = AtomEdit::insert(position.offset, "intellijRulezz".to_string());
109 original_file.reparse(&edit) 108 original_file.reparse(&edit)
110 }; 109 };
111 let mut has_completions = false; 110 let mut has_completions = false;
112 let mut res = Vec::new(); 111 let mut res = Vec::new();
113 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), offset) { 112 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
114 has_completions = true; 113 has_completions = true;
115 complete_name_ref(&file, name_ref, &mut res); 114 complete_name_ref(&file, name_ref, &mut res);
116 // special case, `trait T { fn foo(i_am_a_name_ref) {} }` 115 // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
@@ -129,7 +128,7 @@ pub(crate) fn scope_completion(
129 _ => (), 128 _ => (),
130 } 129 }
131 } 130 }
132 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), offset) { 131 if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) {
133 if is_node::<ast::Param>(name.syntax()) { 132 if is_node::<ast::Param>(name.syntax()) {
134 has_completions = true; 133 has_completions = true;
135 param_completions(name.syntax(), &mut res); 134 param_completions(name.syntax(), &mut res);
@@ -383,7 +382,7 @@ mod tests {
383 382
384 fn check_scope_completion(code: &str, expected_completions: &str) { 383 fn check_scope_completion(code: &str, expected_completions: &str) {
385 let (analysis, position) = single_file_with_position(code); 384 let (analysis, position) = single_file_with_position(code);
386 let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset) 385 let completions = scope_completion(&analysis.imp.db, position)
387 .unwrap() 386 .unwrap()
388 .into_iter() 387 .into_iter()
389 .filter(|c| c.snippet.is_none()) 388 .filter(|c| c.snippet.is_none())
@@ -393,7 +392,7 @@ mod tests {
393 392
394 fn check_snippet_completion(code: &str, expected_completions: &str) { 393 fn check_snippet_completion(code: &str, expected_completions: &str) {
395 let (analysis, position) = single_file_with_position(code); 394 let (analysis, position) = single_file_with_position(code);
396 let completions = scope_completion(&analysis.imp.db, position.file_id, position.offset) 395 let completions = scope_completion(&analysis.imp.db, position)
397 .unwrap() 396 .unwrap()
398 .into_iter() 397 .into_iter()
399 .filter(|c| c.snippet.is_some()) 398 .filter(|c| c.snippet.is_some())
diff --git a/crates/ra_analysis/src/descriptors/mod.rs b/crates/ra_analysis/src/descriptors/mod.rs
index eaeef54c1..56bde3849 100644
--- a/crates/ra_analysis/src/descriptors/mod.rs
+++ b/crates/ra_analysis/src/descriptors/mod.rs
@@ -5,16 +5,16 @@ use std::sync::Arc;
5 5
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self, AstNode, FnDefNode}, 7 ast::{self, AstNode, FnDefNode},
8 SmolStr, TextRange, 8 TextRange,
9}; 9};
10 10
11use crate::{ 11use crate::{
12 db::SyntaxDatabase, 12 db::SyntaxDatabase,
13 descriptors::function::{resolve_local_name, FnId, FnScopes}, 13 descriptors::function::{resolve_local_name, FnId, FnScopes},
14 descriptors::module::{ModuleId, ModuleScope, ModuleTree}, 14 descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource},
15 input::SourceRootId, 15 input::SourceRootId,
16 syntax_ptr::LocalSyntaxPtr, 16 syntax_ptr::LocalSyntaxPtr,
17 Cancelable, FileId, 17 Cancelable,
18}; 18};
19 19
20salsa::query_group! { 20salsa::query_group! {
@@ -23,7 +23,7 @@ salsa::query_group! {
23 type ModuleTreeQuery; 23 type ModuleTreeQuery;
24 use fn module::imp::module_tree; 24 use fn module::imp::module_tree;
25 } 25 }
26 fn submodules(file_id: FileId) -> Cancelable<Arc<Vec<SmolStr>>> { 26 fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
27 type SubmodulesQuery; 27 type SubmodulesQuery;
28 use fn module::imp::submodules; 28 use fn module::imp::submodules;
29 } 29 }
diff --git a/crates/ra_analysis/src/descriptors/module/imp.rs b/crates/ra_analysis/src/descriptors/module/imp.rs
index 3a010ecf5..b3b1f1f21 100644
--- a/crates/ra_analysis/src/descriptors/module/imp.rs
+++ b/crates/ra_analysis/src/descriptors/module/imp.rs
@@ -19,25 +19,66 @@ use super::{
19 ModuleTree, Problem, 19 ModuleTree, Problem,
20}; 20};
21 21
22#[derive(Clone, Hash, PartialEq, Eq, Debug)]
23pub(crate) enum Submodule {
24 Declaration(SmolStr),
25 Definition(SmolStr, ModuleSource),
26}
27
28impl Submodule {
29 fn name(&self) -> &SmolStr {
30 match self {
31 Submodule::Declaration(name) => name,
32 Submodule::Definition(name, _) => name,
33 }
34 }
35}
36
22pub(crate) fn submodules( 37pub(crate) fn submodules(
23 db: &impl DescriptorDatabase, 38 db: &impl DescriptorDatabase,
24 file_id: FileId, 39 source: ModuleSource,
25) -> Cancelable<Arc<Vec<SmolStr>>> { 40) -> Cancelable<Arc<Vec<Submodule>>> {
26 db::check_canceled(db)?; 41 db::check_canceled(db)?;
27 let file = db.file_syntax(file_id); 42 let file_id = source.file_id();
28 let root = file.ast(); 43 let submodules = match source.resolve(db) {
29 let submodules = modules(root).map(|(name, _)| name).collect(); 44 ModuleSourceNode::Root(it) => collect_submodules(file_id, it.ast()),
30 Ok(Arc::new(submodules)) 45 ModuleSourceNode::Inline(it) => it
46 .ast()
47 .item_list()
48 .map(|it| collect_submodules(file_id, it))
49 .unwrap_or_else(Vec::new),
50 };
51 return Ok(Arc::new(submodules));
52
53 fn collect_submodules<'a>(
54 file_id: FileId,
55 root: impl ast::ModuleItemOwner<'a>,
56 ) -> Vec<Submodule> {
57 modules(root)
58 .map(|(name, m)| {
59 if m.has_semi() {
60 Submodule::Declaration(name)
61 } else {
62 let src = ModuleSource::new_inline(file_id, m);
63 Submodule::Definition(name, src)
64 }
65 })
66 .collect()
67 }
31} 68}
32 69
33pub(crate) fn modules(root: ast::Root<'_>) -> impl Iterator<Item = (SmolStr, ast::Module<'_>)> { 70pub(crate) fn modules<'a>(
34 root.modules().filter_map(|module| { 71 root: impl ast::ModuleItemOwner<'a>,
35 let name = module.name()?.text(); 72) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> {
36 if !module.has_semi() { 73 root.items()
37 return None; 74 .filter_map(|item| match item {
38 } 75 ast::ModuleItem::Module(m) => Some(m),
39 Some((name, module)) 76 _ => None,
40 }) 77 })
78 .filter_map(|module| {
79 let name = module.name()?.text();
80 Some((name, module))
81 })
41} 82}
42 83
43pub(crate) fn module_scope( 84pub(crate) fn module_scope(
@@ -66,11 +107,6 @@ pub(crate) fn module_tree(
66 Ok(Arc::new(res)) 107 Ok(Arc::new(res))
67} 108}
68 109
69#[derive(Clone, Hash, PartialEq, Eq, Debug)]
70pub struct Submodule {
71 pub name: SmolStr,
72}
73
74fn create_module_tree<'a>( 110fn create_module_tree<'a>(
75 db: &impl DescriptorDatabase, 111 db: &impl DescriptorDatabase,
76 source_root: SourceRootId, 112 source_root: SourceRootId,
@@ -85,7 +121,8 @@ fn create_module_tree<'a>(
85 121
86 let source_root = db.source_root(source_root); 122 let source_root = db.source_root(source_root);
87 for &file_id in source_root.files.iter() { 123 for &file_id in source_root.files.iter() {
88 if visited.contains(&file_id) { 124 let source = ModuleSource::File(file_id);
125 if visited.contains(&source) {
89 continue; // TODO: use explicit crate_roots here 126 continue; // TODO: use explicit crate_roots here
90 } 127 }
91 assert!(!roots.contains_key(&file_id)); 128 assert!(!roots.contains_key(&file_id));
@@ -96,7 +133,7 @@ fn create_module_tree<'a>(
96 &mut visited, 133 &mut visited,
97 &mut roots, 134 &mut roots,
98 None, 135 None,
99 file_id, 136 source,
100 )?; 137 )?;
101 roots.insert(file_id, module_id); 138 roots.insert(file_id, module_id);
102 } 139 }
@@ -107,36 +144,63 @@ fn build_subtree(
107 db: &impl DescriptorDatabase, 144 db: &impl DescriptorDatabase,
108 source_root: &SourceRoot, 145 source_root: &SourceRoot,
109 tree: &mut ModuleTree, 146 tree: &mut ModuleTree,
110 visited: &mut FxHashSet<FileId>, 147 visited: &mut FxHashSet<ModuleSource>,
111 roots: &mut FxHashMap<FileId, ModuleId>, 148 roots: &mut FxHashMap<FileId, ModuleId>,
112 parent: Option<LinkId>, 149 parent: Option<LinkId>,
113 file_id: FileId, 150 source: ModuleSource,
114) -> Cancelable<ModuleId> { 151) -> Cancelable<ModuleId> {
115 visited.insert(file_id); 152 visited.insert(source);
116 let id = tree.push_mod(ModuleData { 153 let id = tree.push_mod(ModuleData {
117 source: ModuleSource::File(file_id), 154 source,
118 parent, 155 parent,
119 children: Vec::new(), 156 children: Vec::new(),
120 }); 157 });
121 for name in db.submodules(file_id)?.iter() { 158 for sub in db.submodules(source)?.iter() {
122 let (points_to, problem) = resolve_submodule(file_id, name, &source_root.file_resolver);
123 let link = tree.push_link(LinkData { 159 let link = tree.push_link(LinkData {
124 name: name.clone(), 160 name: sub.name().clone(),
125 owner: id, 161 owner: id,
126 points_to: Vec::new(), 162 points_to: Vec::new(),
127 problem: None, 163 problem: None,
128 }); 164 });
129 165
130 let points_to = points_to 166 let (points_to, problem) = match sub {
131 .into_iter() 167 Submodule::Declaration(name) => {
132 .map(|file_id| match roots.remove(&file_id) { 168 let (points_to, problem) =
133 Some(module_id) => { 169 resolve_submodule(source, &name, &source_root.file_resolver);
134 tree.module_mut(module_id).parent = Some(link); 170 let points_to = points_to
135 Ok(module_id) 171 .into_iter()
136 } 172 .map(|file_id| match roots.remove(&file_id) {
137 None => build_subtree(db, source_root, tree, visited, roots, Some(link), file_id), 173 Some(module_id) => {
138 }) 174 tree.module_mut(module_id).parent = Some(link);
139 .collect::<Cancelable<Vec<_>>>()?; 175 Ok(module_id)
176 }
177 None => build_subtree(
178 db,
179 source_root,
180 tree,
181 visited,
182 roots,
183 Some(link),
184 ModuleSource::File(file_id),
185 ),
186 })
187 .collect::<Cancelable<Vec<_>>>()?;
188 (points_to, problem)
189 }
190 Submodule::Definition(_name, submodule_source) => {
191 let points_to = build_subtree(
192 db,
193 source_root,
194 tree,
195 visited,
196 roots,
197 Some(link),
198 *submodule_source,
199 )?;
200 (vec![points_to], None)
201 }
202 };
203
140 tree.link_mut(link).points_to = points_to; 204 tree.link_mut(link).points_to = points_to;
141 tree.link_mut(link).problem = problem; 205 tree.link_mut(link).problem = problem;
142 } 206 }
@@ -144,10 +208,17 @@ fn build_subtree(
144} 208}
145 209
146fn resolve_submodule( 210fn resolve_submodule(
147 file_id: FileId, 211 source: ModuleSource,
148 name: &SmolStr, 212 name: &SmolStr,
149 file_resolver: &FileResolverImp, 213 file_resolver: &FileResolverImp,
150) -> (Vec<FileId>, Option<Problem>) { 214) -> (Vec<FileId>, Option<Problem>) {
215 let file_id = match source {
216 ModuleSource::File(it) => it,
217 ModuleSource::Inline(..) => {
218 // TODO
219 return (Vec::new(), None);
220 }
221 };
151 let mod_name = file_resolver.file_stem(file_id); 222 let mod_name = file_resolver.file_stem(file_id);
152 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main"; 223 let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
153 224
diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs
index e22489fc1..3d799ba05 100644
--- a/crates/ra_analysis/src/descriptors/module/mod.rs
+++ b/crates/ra_analysis/src/descriptors/module/mod.rs
@@ -25,17 +25,17 @@ pub(crate) struct ModuleTree {
25} 25}
26 26
27impl ModuleTree { 27impl ModuleTree {
28 pub(crate) fn modules_for_file(&self, file_id: FileId) -> Vec<ModuleId> { 28 pub(crate) fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
29 self.mods 29 self.mods
30 .iter() 30 .iter()
31 .enumerate() 31 .enumerate()
32 .filter(|(_idx, it)| it.source.is_file(file_id)) 32 .filter(|(_idx, it)| it.source == source)
33 .map(|(idx, _)| ModuleId(idx as u32)) 33 .map(|(idx, _)| ModuleId(idx as u32))
34 .collect() 34 .collect()
35 } 35 }
36 36
37 pub(crate) fn any_module_for_file(&self, file_id: FileId) -> Option<ModuleId> { 37 pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
38 self.modules_for_file(file_id).pop() 38 self.modules_for_source(source).pop()
39 } 39 }
40} 40}
41 41
@@ -142,9 +142,7 @@ impl LinkId {
142 .1; 142 .1;
143 ast.into() 143 ast.into()
144 } 144 }
145 ModuleSourceNode::Inline(..) => { 145 ModuleSourceNode::Inline(it) => it,
146 unimplemented!("https://github.com/rust-analyzer/rust-analyzer/issues/181")
147 }
148 } 146 }
149 } 147 }
150} 148}
@@ -157,6 +155,12 @@ struct ModuleData {
157} 155}
158 156
159impl ModuleSource { 157impl ModuleSource {
158 pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
159 assert!(!module.has_semi());
160 let ptr = SyntaxPtr::new(file_id, module.syntax());
161 ModuleSource::Inline(ptr)
162 }
163
160 pub(crate) fn as_file(self) -> Option<FileId> { 164 pub(crate) fn as_file(self) -> Option<FileId> {
161 match self { 165 match self {
162 ModuleSource::File(f) => Some(f), 166 ModuleSource::File(f) => Some(f),
@@ -164,6 +168,13 @@ impl ModuleSource {
164 } 168 }
165 } 169 }
166 170
171 pub(crate) fn file_id(self) -> FileId {
172 match self {
173 ModuleSource::File(f) => f,
174 ModuleSource::Inline(ptr) => ptr.file_id(),
175 }
176 }
177
167 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode { 178 fn resolve(self, db: &impl SyntaxDatabase) -> ModuleSourceNode {
168 match self { 179 match self {
169 ModuleSource::File(file_id) => { 180 ModuleSource::File(file_id) => {
@@ -178,10 +189,6 @@ impl ModuleSource {
178 } 189 }
179 } 190 }
180 } 191 }
181
182 fn is_file(self, file_id: FileId) -> bool {
183 self.as_file() == Some(file_id)
184 }
185} 192}
186 193
187#[derive(Hash, Debug, PartialEq, Eq)] 194#[derive(Hash, Debug, PartialEq, Eq)]
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 4f337d163..f2482559f 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -27,7 +27,7 @@ use crate::{
27 input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE}, 27 input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
28 symbol_index::SymbolIndex, 28 symbol_index::SymbolIndex,
29 AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver, 29 AnalysisChange, Cancelable, CrateGraph, CrateId, Diagnostic, FileId, FileResolver,
30 FileSystemEdit, Position, Query, SourceChange, SourceFileEdit, 30 FileSystemEdit, FilePosition, Query, SourceChange, SourceFileEdit,
31}; 31};
32 32
33#[derive(Clone, Debug)] 33#[derive(Clone, Debug)]
@@ -220,27 +220,29 @@ impl AnalysisImpl {
220 let source_root = self.db.file_source_root(file_id); 220 let source_root = self.db.file_source_root(file_id);
221 self.db.module_tree(source_root) 221 self.db.module_tree(source_root)
222 } 222 }
223 pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> { 223 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
224 let module_tree = self.module_tree(file_id)?; 224 let module_tree = self.module_tree(position.file_id)?;
225 let file = self.db.file_syntax(position.file_id);
226 let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
227 {
228 Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
229 _ => ModuleSource::File(position.file_id),
230 };
225 231
226 let res = module_tree 232 let res = module_tree
227 .modules_for_file(file_id) 233 .modules_for_source(module_source)
228 .into_iter() 234 .into_iter()
229 .filter_map(|module_id| { 235 .filter_map(|module_id| {
230 let link = module_id.parent_link(&module_tree)?; 236 let link = module_id.parent_link(&module_tree)?;
231 let file_id = match link.owner(&module_tree).source(&module_tree) { 237 let file_id = link.owner(&module_tree).source(&module_tree).file_id();
232 ModuleSource::File(file_id) => file_id,
233 ModuleSource::Inline(..) => {
234 //TODO: https://github.com/rust-analyzer/rust-analyzer/issues/181
235 return None;
236 }
237 };
238 let decl = link.bind_source(&module_tree, &*self.db); 238 let decl = link.bind_source(&module_tree, &*self.db);
239 let decl = decl.ast(); 239 let decl = decl.ast();
240 240
241 let decl_name = decl.name().unwrap();
242
241 let sym = FileSymbol { 243 let sym = FileSymbol {
242 name: decl.name().unwrap().text(), 244 name: decl_name.text(),
243 node_range: decl.syntax().range(), 245 node_range: decl_name.syntax().range(),
244 kind: MODULE, 246 kind: MODULE,
245 }; 247 };
246 Some((file_id, sym)) 248 Some((file_id, sym))
@@ -252,7 +254,7 @@ impl AnalysisImpl {
252 let module_tree = self.module_tree(file_id)?; 254 let module_tree = self.module_tree(file_id)?;
253 let crate_graph = self.db.crate_graph(); 255 let crate_graph = self.db.crate_graph();
254 let res = module_tree 256 let res = module_tree
255 .modules_for_file(file_id) 257 .modules_for_source(ModuleSource::File(file_id))
256 .into_iter() 258 .into_iter()
257 .map(|it| it.root(&module_tree)) 259 .map(|it| it.root(&module_tree))
258 .filter_map(|it| it.source(&module_tree).as_file()) 260 .filter_map(|it| it.source(&module_tree).as_file())
@@ -264,18 +266,14 @@ impl AnalysisImpl {
264 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 266 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
265 self.db.crate_graph().crate_roots[&crate_id] 267 self.db.crate_graph().crate_roots[&crate_id]
266 } 268 }
267 pub fn completions( 269 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
268 &self,
269 file_id: FileId,
270 offset: TextUnit,
271 ) -> Cancelable<Option<Vec<CompletionItem>>> {
272 let mut res = Vec::new(); 270 let mut res = Vec::new();
273 let mut has_completions = false; 271 let mut has_completions = false;
274 if let Some(scope_based) = scope_completion(&self.db, file_id, offset) { 272 if let Some(scope_based) = scope_completion(&self.db, position) {
275 res.extend(scope_based); 273 res.extend(scope_based);
276 has_completions = true; 274 has_completions = true;
277 } 275 }
278 if let Some(scope_based) = resolve_based_completion(&self.db, file_id, offset)? { 276 if let Some(scope_based) = resolve_based_completion(&self.db, position)? {
279 res.extend(scope_based); 277 res.extend(scope_based);
280 has_completions = true; 278 has_completions = true;
281 } 279 }
@@ -284,18 +282,19 @@ impl AnalysisImpl {
284 } 282 }
285 pub fn approximately_resolve_symbol( 283 pub fn approximately_resolve_symbol(
286 &self, 284 &self,
287 file_id: FileId, 285 position: FilePosition,
288 offset: TextUnit,
289 ) -> Cancelable<Vec<(FileId, FileSymbol)>> { 286 ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
290 let module_tree = self.module_tree(file_id)?; 287 let module_tree = self.module_tree(position.file_id)?;
291 let file = self.db.file_syntax(file_id); 288 let file = self.db.file_syntax(position.file_id);
292 let syntax = file.syntax(); 289 let syntax = file.syntax();
293 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, offset) { 290 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
294 // First try to resolve the symbol locally 291 // First try to resolve the symbol locally
295 return if let Some((name, range)) = resolve_local_name(&self.db, file_id, name_ref) { 292 return if let Some((name, range)) =
293 resolve_local_name(&self.db, position.file_id, name_ref)
294 {
296 let mut vec = vec![]; 295 let mut vec = vec![];
297 vec.push(( 296 vec.push((
298 file_id, 297 position.file_id,
299 FileSymbol { 298 FileSymbol {
300 name, 299 name,
301 node_range: range, 300 node_range: range,
@@ -308,10 +307,10 @@ impl AnalysisImpl {
308 self.index_resolve(name_ref) 307 self.index_resolve(name_ref)
309 }; 308 };
310 } 309 }
311 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, offset) { 310 if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
312 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 311 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
313 if module.has_semi() { 312 if module.has_semi() {
314 let file_ids = self.resolve_module(&*module_tree, file_id, module); 313 let file_ids = self.resolve_module(&*module_tree, position.file_id, module);
315 314
316 let res = file_ids 315 let res = file_ids
317 .into_iter() 316 .into_iter()
@@ -336,16 +335,17 @@ impl AnalysisImpl {
336 Ok(vec![]) 335 Ok(vec![])
337 } 336 }
338 337
339 pub fn find_all_refs(&self, file_id: FileId, offset: TextUnit) -> Vec<(FileId, TextRange)> { 338 pub fn find_all_refs(&self, position: FilePosition) -> Vec<(FileId, TextRange)> {
340 let file = self.db.file_syntax(file_id); 339 let file = self.db.file_syntax(position.file_id);
341 let syntax = file.syntax(); 340 let syntax = file.syntax();
342 341
343 // Find the binding associated with the offset 342 // Find the binding associated with the offset
344 let maybe_binding = find_node_at_offset::<ast::BindPat>(syntax, offset).or_else(|| { 343 let maybe_binding =
345 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, offset)?; 344 find_node_at_offset::<ast::BindPat>(syntax, position.offset).or_else(|| {
346 let resolved = resolve_local_name(&self.db, file_id, name_ref)?; 345 let name_ref = find_node_at_offset::<ast::NameRef>(syntax, position.offset)?;
347 find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end()) 346 let resolved = resolve_local_name(&self.db, position.file_id, name_ref)?;
348 }); 347 find_node_at_offset::<ast::BindPat>(syntax, resolved.1.end())
348 });
349 349
350 let binding = match maybe_binding { 350 let binding = match maybe_binding {
351 None => return Vec::new(), 351 None => return Vec::new(),
@@ -354,11 +354,11 @@ impl AnalysisImpl {
354 354
355 let decl = DeclarationDescriptor::new(binding); 355 let decl = DeclarationDescriptor::new(binding);
356 356
357 let mut ret = vec![(file_id, decl.range)]; 357 let mut ret = vec![(position.file_id, decl.range)];
358 ret.extend( 358 ret.extend(
359 decl.find_all_refs() 359 decl.find_all_refs()
360 .into_iter() 360 .into_iter()
361 .map(|ref_desc| (file_id, ref_desc.range)), 361 .map(|ref_desc| (position.file_id, ref_desc.range)),
362 ); 362 );
363 363
364 ret 364 ret
@@ -376,7 +376,7 @@ impl AnalysisImpl {
376 fix: None, 376 fix: None,
377 }) 377 })
378 .collect::<Vec<_>>(); 378 .collect::<Vec<_>>();
379 if let Some(m) = module_tree.any_module_for_file(file_id) { 379 if let Some(m) = module_tree.any_module_for_source(ModuleSource::File(file_id)) {
380 for (name_node, problem) in m.problems(&module_tree, &*self.db) { 380 for (name_node, problem) in m.problems(&module_tree, &*self.db) {
381 let diag = match problem { 381 let diag = match problem {
382 Problem::UnresolvedModule { candidate } => { 382 Problem::UnresolvedModule { candidate } => {
@@ -452,14 +452,13 @@ impl AnalysisImpl {
452 452
453 pub fn resolve_callable( 453 pub fn resolve_callable(
454 &self, 454 &self,
455 file_id: FileId, 455 position: FilePosition,
456 offset: TextUnit,
457 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { 456 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> {
458 let file = self.db.file_syntax(file_id); 457 let file = self.db.file_syntax(position.file_id);
459 let syntax = file.syntax(); 458 let syntax = file.syntax();
460 459
461 // Find the calling expression and it's NameRef 460 // Find the calling expression and it's NameRef
462 let calling_node = match FnCallNode::with_node(syntax, offset) { 461 let calling_node = match FnCallNode::with_node(syntax, position.offset) {
463 Some(node) => node, 462 Some(node) => node,
464 None => return Ok(None), 463 None => return Ok(None),
465 }; 464 };
@@ -494,7 +493,7 @@ impl AnalysisImpl {
494 if let Some(ref arg_list) = calling_node.arg_list() { 493 if let Some(ref arg_list) = calling_node.arg_list() {
495 let start = arg_list.syntax().range().start(); 494 let start = arg_list.syntax().range().start();
496 495
497 let range_search = TextRange::from_to(start, offset); 496 let range_search = TextRange::from_to(start, position.offset);
498 let mut commas: usize = arg_list 497 let mut commas: usize = arg_list
499 .syntax() 498 .syntax()
500 .text() 499 .text()
@@ -539,7 +538,7 @@ impl AnalysisImpl {
539 Some(name) => name.text(), 538 Some(name) => name.text(),
540 None => return Vec::new(), 539 None => return Vec::new(),
541 }; 540 };
542 let module_id = match module_tree.any_module_for_file(file_id) { 541 let module_id = match module_tree.any_module_for_source(ModuleSource::File(file_id)) {
543 Some(id) => id, 542 Some(id) => id,
544 None => return Vec::new(), 543 None => return Vec::new(),
545 }; 544 };
@@ -563,7 +562,7 @@ impl SourceChange {
563 file_system_edits: vec![], 562 file_system_edits: vec![],
564 cursor_position: edit 563 cursor_position: edit
565 .cursor_position 564 .cursor_position
566 .map(|offset| Position { offset, file_id }), 565 .map(|offset| FilePosition { offset, file_id }),
567 } 566 }
568 } 567 }
569} 568}
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 4e4c65f08..0ea9ebee7 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -119,18 +119,18 @@ impl AnalysisHost {
119 } 119 }
120} 120}
121 121
122#[derive(Clone, Copy, Debug)]
123pub struct FilePosition {
124 pub file_id: FileId,
125 pub offset: TextUnit,
126}
127
122#[derive(Debug)] 128#[derive(Debug)]
123pub struct SourceChange { 129pub struct SourceChange {
124 pub label: String, 130 pub label: String,
125 pub source_file_edits: Vec<SourceFileEdit>, 131 pub source_file_edits: Vec<SourceFileEdit>,
126 pub file_system_edits: Vec<FileSystemEdit>, 132 pub file_system_edits: Vec<FileSystemEdit>,
127 pub cursor_position: Option<Position>, 133 pub cursor_position: Option<FilePosition>,
128}
129
130#[derive(Debug)]
131pub struct Position {
132 pub file_id: FileId,
133 pub offset: TextUnit,
134} 134}
135 135
136#[derive(Debug)] 136#[derive(Debug)]
@@ -224,18 +224,18 @@ impl Analysis {
224 let file = self.imp.file_syntax(file_id); 224 let file = self.imp.file_syntax(file_id);
225 SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range)) 225 SourceChange::from_local_edit(file_id, "join lines", ra_editor::join_lines(&file, range))
226 } 226 }
227 pub fn on_enter(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { 227 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
228 let file = self.imp.file_syntax(file_id); 228 let file = self.imp.file_syntax(position.file_id);
229 let edit = ra_editor::on_enter(&file, offset)?; 229 let edit = ra_editor::on_enter(&file, position.offset)?;
230 let res = SourceChange::from_local_edit(file_id, "on enter", edit); 230 let res = SourceChange::from_local_edit(position.file_id, "on enter", edit);
231 Some(res) 231 Some(res)
232 } 232 }
233 pub fn on_eq_typed(&self, file_id: FileId, offset: TextUnit) -> Option<SourceChange> { 233 pub fn on_eq_typed(&self, position: FilePosition) -> Option<SourceChange> {
234 let file = self.imp.file_syntax(file_id); 234 let file = self.imp.file_syntax(position.file_id);
235 Some(SourceChange::from_local_edit( 235 Some(SourceChange::from_local_edit(
236 file_id, 236 position.file_id,
237 "add semicolon", 237 "add semicolon",
238 ra_editor::on_eq_typed(&file, offset)?, 238 ra_editor::on_eq_typed(&file, position.offset)?,
239 )) 239 ))
240 } 240 }
241 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> { 241 pub fn file_structure(&self, file_id: FileId) -> Vec<StructureNode> {
@@ -251,20 +251,15 @@ impl Analysis {
251 } 251 }
252 pub fn approximately_resolve_symbol( 252 pub fn approximately_resolve_symbol(
253 &self, 253 &self,
254 file_id: FileId, 254 position: FilePosition,
255 offset: TextUnit,
256 ) -> Cancelable<Vec<(FileId, FileSymbol)>> { 255 ) -> Cancelable<Vec<(FileId, FileSymbol)>> {
257 self.imp.approximately_resolve_symbol(file_id, offset) 256 self.imp.approximately_resolve_symbol(position)
258 } 257 }
259 pub fn find_all_refs( 258 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
260 &self, 259 Ok(self.imp.find_all_refs(position))
261 file_id: FileId,
262 offset: TextUnit,
263 ) -> Cancelable<Vec<(FileId, TextRange)>> {
264 Ok(self.imp.find_all_refs(file_id, offset))
265 } 260 }
266 pub fn parent_module(&self, file_id: FileId) -> Cancelable<Vec<(FileId, FileSymbol)>> { 261 pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
267 self.imp.parent_module(file_id) 262 self.imp.parent_module(position)
268 } 263 }
269 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> { 264 pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
270 self.imp.crate_for(file_id) 265 self.imp.crate_for(file_id)
@@ -280,12 +275,8 @@ impl Analysis {
280 let file = self.imp.file_syntax(file_id); 275 let file = self.imp.file_syntax(file_id);
281 Ok(ra_editor::highlight(&file)) 276 Ok(ra_editor::highlight(&file))
282 } 277 }
283 pub fn completions( 278 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
284 &self, 279 self.imp.completions(position)
285 file_id: FileId,
286 offset: TextUnit,
287 ) -> Cancelable<Option<Vec<CompletionItem>>> {
288 self.imp.completions(file_id, offset)
289 } 280 }
290 pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { 281 pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> {
291 Ok(self.imp.assists(file_id, range)) 282 Ok(self.imp.assists(file_id, range))
@@ -295,10 +286,9 @@ impl Analysis {
295 } 286 }
296 pub fn resolve_callable( 287 pub fn resolve_callable(
297 &self, 288 &self,
298 file_id: FileId, 289 position: FilePosition,
299 offset: TextUnit,
300 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> { 290 ) -> Cancelable<Option<(FnDescriptor, Option<usize>)>> {
301 self.imp.resolve_callable(file_id, offset) 291 self.imp.resolve_callable(position)
302 } 292 }
303} 293}
304 294
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index a7134a0e6..8e8f969f4 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,16 +1,9 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::TextUnit;
4use relative_path::{RelativePath, RelativePathBuf}; 3use relative_path::{RelativePath, RelativePathBuf};
5use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER};
6 5
7use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver}; 6use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FileResolver, FilePosition};
8
9#[derive(Debug)]
10pub struct FilePosition {
11 pub file_id: FileId,
12 pub offset: TextUnit,
13}
14 7
15/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 8/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
16/// from a set of in-memory files. 9/// from a set of in-memory files.
diff --git a/crates/ra_analysis/src/syntax_ptr.rs b/crates/ra_analysis/src/syntax_ptr.rs
index 4db1529c2..4afb1fc93 100644
--- a/crates/ra_analysis/src/syntax_ptr.rs
+++ b/crates/ra_analysis/src/syntax_ptr.rs
@@ -22,6 +22,10 @@ impl SyntaxPtr {
22 let local = LocalSyntaxPtr::new(node); 22 let local = LocalSyntaxPtr::new(node);
23 SyntaxPtr { file_id, local } 23 SyntaxPtr { file_id, local }
24 } 24 }
25
26 pub(crate) fn file_id(self) -> FileId {
27 self.file_id
28 }
25} 29}
26 30
27/// A pionter to a syntax node inside a file. 31/// A pionter to a syntax node inside a file.