diff options
author | Ekaterina Babshukova <[email protected]> | 2019-10-10 16:51:51 +0100 |
---|---|---|
committer | Ekaterina Babshukova <[email protected]> | 2019-10-22 21:47:31 +0100 |
commit | 5b03773fbeea55d86f64e5fb69a0d0f1d6a4f7e8 (patch) | |
tree | 1f3ce5c71343535ae0ca0751982a1d0f89480e45 /crates | |
parent | 01853e8d6c6f4b44801c74f4fcdef735d9e77b48 (diff) |
implement search of references
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide_api/src/name_kind.rs | 83 | ||||
-rw-r--r-- | crates/ra_ide_api/src/references.rs | 50 | ||||
-rw-r--r-- | crates/ra_ide_api/src/search_scope.rs | 77 |
3 files changed, 134 insertions, 76 deletions
diff --git a/crates/ra_ide_api/src/name_kind.rs b/crates/ra_ide_api/src/name_kind.rs index 6d1eb153f..8eef540f6 100644 --- a/crates/ra_ide_api/src/name_kind.rs +++ b/crates/ra_ide_api/src/name_kind.rs | |||
@@ -10,6 +10,7 @@ use ra_syntax::{ast, ast::VisibilityOwner, AstNode, AstPtr}; | |||
10 | 10 | ||
11 | use crate::db::RootDatabase; | 11 | use crate::db::RootDatabase; |
12 | 12 | ||
13 | #[derive(PartialEq, Eq)] | ||
13 | pub enum NameKind { | 14 | pub enum NameKind { |
14 | Macro(MacroDef), | 15 | Macro(MacroDef), |
15 | FieldAccess(StructField), | 16 | FieldAccess(StructField), |
@@ -21,23 +22,24 @@ pub enum NameKind { | |||
21 | GenericParam(u32), | 22 | GenericParam(u32), |
22 | } | 23 | } |
23 | 24 | ||
24 | pub(crate) struct Declaration { | 25 | #[derive(PartialEq, Eq)] |
26 | pub(crate) struct Definition { | ||
25 | pub visibility: Option<ast::Visibility>, | 27 | pub visibility: Option<ast::Visibility>, |
26 | pub container: Module, | 28 | pub container: Module, |
27 | pub item: NameKind, | 29 | pub item: NameKind, |
28 | } | 30 | } |
29 | 31 | ||
30 | trait HasDeclaration { | 32 | trait HasDefinition { |
31 | type Def; | 33 | type Def; |
32 | type Ref; | 34 | type Ref; |
33 | 35 | ||
34 | fn declaration(self, db: &RootDatabase) -> Declaration; | 36 | fn definition(self, db: &RootDatabase) -> Definition; |
35 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Declaration>; | 37 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition>; |
36 | fn from_ref( | 38 | fn from_ref( |
37 | db: &RootDatabase, | 39 | db: &RootDatabase, |
38 | analyzer: &SourceAnalyzer, | 40 | analyzer: &SourceAnalyzer, |
39 | refer: Self::Ref, | 41 | refer: Self::Ref, |
40 | ) -> Option<Declaration>; | 42 | ) -> Option<Definition>; |
41 | } | 43 | } |
42 | 44 | ||
43 | macro_rules! match_ast { | 45 | macro_rules! match_ast { |
@@ -55,7 +57,7 @@ pub(crate) fn classify_name_ref( | |||
55 | file_id: FileId, | 57 | file_id: FileId, |
56 | analyzer: &SourceAnalyzer, | 58 | analyzer: &SourceAnalyzer, |
57 | name_ref: &ast::NameRef, | 59 | name_ref: &ast::NameRef, |
58 | ) -> Option<Declaration> { | 60 | ) -> Option<Definition> { |
59 | let parent = name_ref.syntax().parent()?; | 61 | let parent = name_ref.syntax().parent()?; |
60 | match_ast! { | 62 | match_ast! { |
61 | match parent { | 63 | match parent { |
@@ -64,7 +66,7 @@ pub(crate) fn classify_name_ref( | |||
64 | }, | 66 | }, |
65 | ast::FieldExpr(it) => { | 67 | ast::FieldExpr(it) => { |
66 | if let Some(field) = analyzer.resolve_field(&it) { | 68 | if let Some(field) = analyzer.resolve_field(&it) { |
67 | return Some(field.declaration(db)); | 69 | return Some(field.definition(db)); |
68 | } | 70 | } |
69 | }, | 71 | }, |
70 | ast::RecordField(it) => { | 72 | ast::RecordField(it) => { |
@@ -73,7 +75,7 @@ pub(crate) fn classify_name_ref( | |||
73 | let hir_path = Path::from_name_ref(name_ref); | 75 | let hir_path = Path::from_name_ref(name_ref); |
74 | let hir_name = hir_path.as_ident()?; | 76 | let hir_name = hir_path.as_ident()?; |
75 | let field = variant_def.field(db, hir_name)?; | 77 | let field = variant_def.field(db, hir_name)?; |
76 | return Some(field.declaration(db)); | 78 | return Some(field.definition(db)); |
77 | } | 79 | } |
78 | }, | 80 | }, |
79 | _ => (), | 81 | _ => (), |
@@ -83,12 +85,13 @@ pub(crate) fn classify_name_ref( | |||
83 | let ast = ModuleSource::from_child_node(db, file_id, &parent); | 85 | let ast = ModuleSource::from_child_node(db, file_id, &parent); |
84 | let file_id = file_id.into(); | 86 | let file_id = file_id.into(); |
85 | let container = Module::from_definition(db, Source { file_id, ast })?; | 87 | let container = Module::from_definition(db, Source { file_id, ast })?; |
88 | let visibility = None; | ||
86 | 89 | ||
87 | if let Some(macro_call) = | 90 | if let Some(macro_call) = |
88 | parent.parent().and_then(|node| node.parent()).and_then(ast::MacroCall::cast) | 91 | parent.parent().and_then(|node| node.parent()).and_then(ast::MacroCall::cast) |
89 | { | 92 | { |
90 | if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) { | 93 | if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) { |
91 | return Some(Declaration { item: NameKind::Macro(mac), container, visibility: None }); | 94 | return Some(Definition { item: NameKind::Macro(mac), container, visibility }); |
92 | } | 95 | } |
93 | } | 96 | } |
94 | 97 | ||
@@ -96,24 +99,24 @@ pub(crate) fn classify_name_ref( | |||
96 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; | 99 | let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; |
97 | let resolved = analyzer.resolve_path(db, &path)?; | 100 | let resolved = analyzer.resolve_path(db, &path)?; |
98 | match resolved { | 101 | match resolved { |
99 | PathResolution::Def(def) => Some(def.declaration(db)), | 102 | PathResolution::Def(def) => Some(def.definition(db)), |
100 | PathResolution::LocalBinding(Either::A(pat)) => decl_from_pat(db, file_id, pat), | 103 | PathResolution::LocalBinding(Either::A(pat)) => decl_from_pat(db, file_id, pat), |
101 | PathResolution::LocalBinding(Either::B(par)) => { | 104 | PathResolution::LocalBinding(Either::B(par)) => { |
102 | Some(Declaration { item: NameKind::SelfParam(par), container, visibility: None }) | 105 | Some(Definition { item: NameKind::SelfParam(par), container, visibility }) |
103 | } | 106 | } |
104 | PathResolution::GenericParam(par) => { | 107 | PathResolution::GenericParam(par) => { |
105 | // FIXME: get generic param def | 108 | // FIXME: get generic param def |
106 | Some(Declaration { item: NameKind::GenericParam(par), container, visibility: None }) | 109 | Some(Definition { item: NameKind::GenericParam(par), container, visibility }) |
107 | } | 110 | } |
108 | PathResolution::Macro(def) => { | 111 | PathResolution::Macro(def) => { |
109 | Some(Declaration { item: NameKind::Macro(def), container, visibility: None }) | 112 | Some(Definition { item: NameKind::Macro(def), container, visibility }) |
110 | } | 113 | } |
111 | PathResolution::SelfType(impl_block) => { | 114 | PathResolution::SelfType(impl_block) => { |
112 | let ty = impl_block.target_ty(db); | 115 | let ty = impl_block.target_ty(db); |
113 | let container = impl_block.module(); | 116 | let container = impl_block.module(); |
114 | Some(Declaration { item: NameKind::SelfType(ty), container, visibility: None }) | 117 | Some(Definition { item: NameKind::SelfType(ty), container, visibility }) |
115 | } | 118 | } |
116 | PathResolution::AssocItem(assoc) => Some(assoc.declaration(db)), | 119 | PathResolution::AssocItem(assoc) => Some(assoc.definition(db)), |
117 | } | 120 | } |
118 | } | 121 | } |
119 | 122 | ||
@@ -121,7 +124,7 @@ pub(crate) fn classify_name( | |||
121 | db: &RootDatabase, | 124 | db: &RootDatabase, |
122 | file_id: FileId, | 125 | file_id: FileId, |
123 | name: &ast::Name, | 126 | name: &ast::Name, |
124 | ) -> Option<Declaration> { | 127 | ) -> Option<Definition> { |
125 | let parent = name.syntax().parent()?; | 128 | let parent = name.syntax().parent()?; |
126 | let file_id = file_id.into(); | 129 | let file_id = file_id.into(); |
127 | 130 | ||
@@ -145,7 +148,7 @@ pub(crate) fn classify_name( | |||
145 | ast::EnumVariant(it) => { | 148 | ast::EnumVariant(it) => { |
146 | let src = hir::Source { file_id, ast: it.clone() }; | 149 | let src = hir::Source { file_id, ast: it.clone() }; |
147 | let def: ModuleDef = EnumVariant::from_source(db, src)?.into(); | 150 | let def: ModuleDef = EnumVariant::from_source(db, src)?.into(); |
148 | Some(def.declaration(db)) | 151 | Some(def.definition(db)) |
149 | }, | 152 | }, |
150 | ast::ModuleItem(it) => { | 153 | ast::ModuleItem(it) => { |
151 | ModuleDef::from_def(db, file_id, it) | 154 | ModuleDef::from_def(db, file_id, it) |
@@ -159,7 +162,7 @@ fn decl_from_pat( | |||
159 | db: &RootDatabase, | 162 | db: &RootDatabase, |
160 | file_id: HirFileId, | 163 | file_id: HirFileId, |
161 | pat: AstPtr<ast::BindPat>, | 164 | pat: AstPtr<ast::BindPat>, |
162 | ) -> Option<Declaration> { | 165 | ) -> Option<Definition> { |
163 | let root = db.parse_or_expand(file_id)?; | 166 | let root = db.parse_or_expand(file_id)?; |
164 | // FIXME: use match_ast! | 167 | // FIXME: use match_ast! |
165 | let def = pat.to_node(&root).syntax().ancestors().find_map(|node| { | 168 | let def = pat.to_node(&root).syntax().ancestors().find_map(|node| { |
@@ -178,14 +181,14 @@ fn decl_from_pat( | |||
178 | })?; | 181 | })?; |
179 | let item = NameKind::Pat((def, pat)); | 182 | let item = NameKind::Pat((def, pat)); |
180 | let container = def.module(db); | 183 | let container = def.module(db); |
181 | Some(Declaration { item, container, visibility: None }) | 184 | Some(Definition { item, container, visibility: None }) |
182 | } | 185 | } |
183 | 186 | ||
184 | impl HasDeclaration for StructField { | 187 | impl HasDefinition for StructField { |
185 | type Def = ast::RecordFieldDef; | 188 | type Def = ast::RecordFieldDef; |
186 | type Ref = ast::FieldExpr; | 189 | type Ref = ast::FieldExpr; |
187 | 190 | ||
188 | fn declaration(self, db: &RootDatabase) -> Declaration { | 191 | fn definition(self, db: &RootDatabase) -> Definition { |
189 | let item = NameKind::FieldAccess(self); | 192 | let item = NameKind::FieldAccess(self); |
190 | let parent = self.parent_def(db); | 193 | let parent = self.parent_def(db); |
191 | let container = parent.module(db); | 194 | let container = parent.module(db); |
@@ -193,30 +196,30 @@ impl HasDeclaration for StructField { | |||
193 | VariantDef::Struct(s) => s.source(db).ast.visibility(), | 196 | VariantDef::Struct(s) => s.source(db).ast.visibility(), |
194 | VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), | 197 | VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), |
195 | }; | 198 | }; |
196 | Declaration { item, container, visibility } | 199 | Definition { item, container, visibility } |
197 | } | 200 | } |
198 | 201 | ||
199 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Declaration> { | 202 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> { |
200 | let src = hir::Source { file_id, ast: hir::FieldSource::Named(def) }; | 203 | let src = hir::Source { file_id, ast: hir::FieldSource::Named(def) }; |
201 | let field = StructField::from_source(db, src)?; | 204 | let field = StructField::from_source(db, src)?; |
202 | Some(field.declaration(db)) | 205 | Some(field.definition(db)) |
203 | } | 206 | } |
204 | 207 | ||
205 | fn from_ref( | 208 | fn from_ref( |
206 | db: &RootDatabase, | 209 | db: &RootDatabase, |
207 | analyzer: &SourceAnalyzer, | 210 | analyzer: &SourceAnalyzer, |
208 | refer: Self::Ref, | 211 | refer: Self::Ref, |
209 | ) -> Option<Declaration> { | 212 | ) -> Option<Definition> { |
210 | let field = analyzer.resolve_field(&refer)?; | 213 | let field = analyzer.resolve_field(&refer)?; |
211 | Some(field.declaration(db)) | 214 | Some(field.definition(db)) |
212 | } | 215 | } |
213 | } | 216 | } |
214 | 217 | ||
215 | impl HasDeclaration for AssocItem { | 218 | impl HasDefinition for AssocItem { |
216 | type Def = ast::ImplItem; | 219 | type Def = ast::ImplItem; |
217 | type Ref = ast::MethodCallExpr; | 220 | type Ref = ast::MethodCallExpr; |
218 | 221 | ||
219 | fn declaration(self, db: &RootDatabase) -> Declaration { | 222 | fn definition(self, db: &RootDatabase) -> Definition { |
220 | let item = NameKind::AssocItem(self); | 223 | let item = NameKind::AssocItem(self); |
221 | let container = self.module(db); | 224 | let container = self.module(db); |
222 | let visibility = match self { | 225 | let visibility = match self { |
@@ -224,30 +227,30 @@ impl HasDeclaration for AssocItem { | |||
224 | AssocItem::Const(c) => c.source(db).ast.visibility(), | 227 | AssocItem::Const(c) => c.source(db).ast.visibility(), |
225 | AssocItem::TypeAlias(a) => a.source(db).ast.visibility(), | 228 | AssocItem::TypeAlias(a) => a.source(db).ast.visibility(), |
226 | }; | 229 | }; |
227 | Declaration { item, container, visibility } | 230 | Definition { item, container, visibility } |
228 | } | 231 | } |
229 | 232 | ||
230 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Declaration> { | 233 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> { |
231 | let src = hir::Source { file_id, ast: def }; | 234 | let src = hir::Source { file_id, ast: def }; |
232 | let item = AssocItem::from_source(db, src)?; | 235 | let item = AssocItem::from_source(db, src)?; |
233 | Some(item.declaration(db)) | 236 | Some(item.definition(db)) |
234 | } | 237 | } |
235 | 238 | ||
236 | fn from_ref( | 239 | fn from_ref( |
237 | db: &RootDatabase, | 240 | db: &RootDatabase, |
238 | analyzer: &SourceAnalyzer, | 241 | analyzer: &SourceAnalyzer, |
239 | refer: Self::Ref, | 242 | refer: Self::Ref, |
240 | ) -> Option<Declaration> { | 243 | ) -> Option<Definition> { |
241 | let func: AssocItem = analyzer.resolve_method_call(&refer)?.into(); | 244 | let func: AssocItem = analyzer.resolve_method_call(&refer)?.into(); |
242 | Some(func.declaration(db)) | 245 | Some(func.definition(db)) |
243 | } | 246 | } |
244 | } | 247 | } |
245 | 248 | ||
246 | impl HasDeclaration for ModuleDef { | 249 | impl HasDefinition for ModuleDef { |
247 | type Def = ast::ModuleItem; | 250 | type Def = ast::ModuleItem; |
248 | type Ref = ast::Path; | 251 | type Ref = ast::Path; |
249 | 252 | ||
250 | fn declaration(self, db: &RootDatabase) -> Declaration { | 253 | fn definition(self, db: &RootDatabase) -> Definition { |
251 | let (container, visibility) = match self { | 254 | let (container, visibility) = match self { |
252 | ModuleDef::Module(it) => { | 255 | ModuleDef::Module(it) => { |
253 | let container = it.parent(db).or_else(|| Some(it)).unwrap(); | 256 | let container = it.parent(db).or_else(|| Some(it)).unwrap(); |
@@ -270,22 +273,22 @@ impl HasDeclaration for ModuleDef { | |||
270 | ModuleDef::BuiltinType(..) => unreachable!(), | 273 | ModuleDef::BuiltinType(..) => unreachable!(), |
271 | }; | 274 | }; |
272 | let item = NameKind::Def(self); | 275 | let item = NameKind::Def(self); |
273 | Declaration { item, container, visibility } | 276 | Definition { item, container, visibility } |
274 | } | 277 | } |
275 | 278 | ||
276 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Declaration> { | 279 | fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> { |
277 | let src = hir::Source { file_id, ast: def }; | 280 | let src = hir::Source { file_id, ast: def }; |
278 | let def = ModuleDef::from_source(db, src)?; | 281 | let def = ModuleDef::from_source(db, src)?; |
279 | Some(def.declaration(db)) | 282 | Some(def.definition(db)) |
280 | } | 283 | } |
281 | 284 | ||
282 | fn from_ref( | 285 | fn from_ref( |
283 | db: &RootDatabase, | 286 | db: &RootDatabase, |
284 | analyzer: &SourceAnalyzer, | 287 | analyzer: &SourceAnalyzer, |
285 | refer: Self::Ref, | 288 | refer: Self::Ref, |
286 | ) -> Option<Declaration> { | 289 | ) -> Option<Definition> { |
287 | None | 290 | None |
288 | } | 291 | } |
289 | } | 292 | } |
290 | 293 | ||
291 | // FIXME: impl HasDeclaration for hir::MacroDef | 294 | // FIXME: impl HasDefinition for hir::MacroDef |
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 24453452b..24cd7e827 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs | |||
@@ -7,10 +7,8 @@ use relative_path::{RelativePath, RelativePathBuf}; | |||
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | db::RootDatabase, | 9 | db::RootDatabase, |
10 | name_kind::{ | 10 | name_kind::{classify_name, classify_name_ref, Definition, NameKind::*}, |
11 | classify_name, classify_name_ref, | 11 | search_scope::find_refs, |
12 | NameKind::{self, *}, | ||
13 | }, | ||
14 | FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange, | 12 | FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange, |
15 | SourceFileEdit, TextRange, | 13 | SourceFileEdit, TextRange, |
16 | }; | 14 | }; |
@@ -58,9 +56,9 @@ pub(crate) fn find_all_refs( | |||
58 | ) -> Option<RangeInfo<ReferenceSearchResult>> { | 56 | ) -> Option<RangeInfo<ReferenceSearchResult>> { |
59 | let parse = db.parse(position.file_id); | 57 | let parse = db.parse(position.file_id); |
60 | let syntax = parse.tree().syntax().clone(); | 58 | let syntax = parse.tree().syntax().clone(); |
61 | let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?; | 59 | let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; |
62 | 60 | ||
63 | let declaration = match name_kind { | 61 | let declaration = match def.item { |
64 | Macro(mac) => NavigationTarget::from_macro_def(db, mac), | 62 | Macro(mac) => NavigationTarget::from_macro_def(db, mac), |
65 | FieldAccess(field) => NavigationTarget::from_field(db, field), | 63 | FieldAccess(field) => NavigationTarget::from_field(db, field), |
66 | AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), | 64 | AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), |
@@ -74,14 +72,19 @@ pub(crate) fn find_all_refs( | |||
74 | GenericParam(_) => return None, | 72 | GenericParam(_) => return None, |
75 | }; | 73 | }; |
76 | 74 | ||
77 | let references = match name_kind { | 75 | // let references = match name_kind { |
78 | Pat((_, pat)) => analyzer | 76 | // Pat((_, pat)) => analyzer |
79 | .find_all_refs(&pat.to_node(&syntax)) | 77 | // .find_all_refs(&pat.to_node(&syntax)) |
80 | .into_iter() | 78 | // .into_iter() |
81 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) | 79 | // .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) |
82 | .collect::<Vec<_>>(), | 80 | // .collect::<Vec<_>>(), |
83 | _ => vec![], | 81 | // _ => vec![], |
84 | }; | 82 | // }; |
83 | let references = find_refs(db, def, name); | ||
84 | let references = references | ||
85 | .into_iter() | ||
86 | .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) | ||
87 | .collect::<Vec<_>>(); | ||
85 | 88 | ||
86 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); | 89 | return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); |
87 | 90 | ||
@@ -89,18 +92,17 @@ pub(crate) fn find_all_refs( | |||
89 | db: &RootDatabase, | 92 | db: &RootDatabase, |
90 | syntax: &SyntaxNode, | 93 | syntax: &SyntaxNode, |
91 | position: FilePosition, | 94 | position: FilePosition, |
92 | ) -> Option<RangeInfo<(hir::SourceAnalyzer, NameKind)>> { | 95 | ) -> Option<RangeInfo<(String, Definition)>> { |
93 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { | 96 | if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { |
94 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name.syntax(), None); | 97 | let def = classify_name(db, position.file_id, &name)?; |
95 | let name_kind = classify_name(db, position.file_id, &name)?.item; | ||
96 | let range = name.syntax().text_range(); | 98 | let range = name.syntax().text_range(); |
97 | return Some(RangeInfo::new(range, (analyzer, name_kind))); | 99 | return Some(RangeInfo::new(range, (name.text().to_string(), def))); |
98 | } | 100 | } |
99 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; | 101 | let name_ref = find_node_at_offset::<ast::NameRef>(&syntax, position.offset)?; |
100 | let range = name_ref.syntax().text_range(); | 102 | let range = name_ref.syntax().text_range(); |
101 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); | 103 | let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); |
102 | let name_kind = classify_name_ref(db, position.file_id, &analyzer, &name_ref)?.item; | 104 | let def = classify_name_ref(db, position.file_id, &analyzer, &name_ref)?; |
103 | Some(RangeInfo::new(range, (analyzer, name_kind))) | 105 | Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) |
104 | } | 106 | } |
105 | } | 107 | } |
106 | 108 | ||
@@ -271,12 +273,16 @@ mod tests { | |||
271 | let code = r#" | 273 | let code = r#" |
272 | //- /lib.rs | 274 | //- /lib.rs |
273 | struct Foo { | 275 | struct Foo { |
274 | spam<|>: u32, | 276 | pub spam<|>: u32, |
277 | } | ||
278 | |||
279 | fn main(s: Foo) { | ||
280 | let f = s.spam; | ||
275 | } | 281 | } |
276 | "#; | 282 | "#; |
277 | 283 | ||
278 | let refs = get_all_refs(code); | 284 | let refs = get_all_refs(code); |
279 | assert_eq!(refs.len(), 1); | 285 | assert_eq!(refs.len(), 2); |
280 | } | 286 | } |
281 | 287 | ||
282 | #[test] | 288 | #[test] |
diff --git a/crates/ra_ide_api/src/search_scope.rs b/crates/ra_ide_api/src/search_scope.rs index 1cf1aed37..9fcbdcc3a 100644 --- a/crates/ra_ide_api/src/search_scope.rs +++ b/crates/ra_ide_api/src/search_scope.rs | |||
@@ -1,18 +1,69 @@ | |||
1 | use hir::{DefWithBody, HasSource, ModuleSource}; | 1 | use hir::{ |
2 | source_binder::ReferenceDescriptor, DefWithBody, HasSource, ModuleSource, SourceAnalyzer, | ||
3 | }; | ||
2 | use ra_db::{FileId, SourceDatabase}; | 4 | use ra_db::{FileId, SourceDatabase}; |
3 | use ra_syntax::{AstNode, TextRange}; | 5 | use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, TextRange, TextUnit}; |
4 | 6 | ||
5 | use crate::{ | 7 | use crate::{ |
6 | db::RootDatabase, | 8 | db::RootDatabase, |
7 | name_kind::{Declaration, NameKind}, | 9 | name_kind::{classify_name_ref, Definition, NameKind}, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub struct SearchScope { | 12 | pub(crate) struct SearchScope { |
11 | pub scope: Vec<(FileId, Option<TextRange>)>, | 13 | pub scope: Vec<(FileId, Option<TextRange>)>, |
12 | } | 14 | } |
13 | 15 | ||
14 | impl Declaration { | 16 | pub(crate) fn find_refs( |
15 | pub fn scope(self, db: &RootDatabase) -> Option<SearchScope> { | 17 | db: &RootDatabase, |
18 | def: Definition, | ||
19 | name: String, | ||
20 | ) -> Vec<ReferenceDescriptor> { | ||
21 | let pat = name.as_str(); | ||
22 | let scope = def.scope(db).scope; | ||
23 | let mut refs = vec![]; | ||
24 | |||
25 | let is_match = |file_id: FileId, name_ref: &ast::NameRef| -> bool { | ||
26 | let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); | ||
27 | let classified = classify_name_ref(db, file_id, &analyzer, &name_ref); | ||
28 | if let Some(d) = classified { | ||
29 | d == def | ||
30 | } else { | ||
31 | false | ||
32 | } | ||
33 | }; | ||
34 | |||
35 | for (file_id, text_range) in scope { | ||
36 | let text = db.file_text(file_id); | ||
37 | let parse = SourceFile::parse(&text); | ||
38 | let syntax = parse.tree().syntax().clone(); | ||
39 | |||
40 | for (idx, _) in text.match_indices(pat) { | ||
41 | let offset = TextUnit::from_usize(idx); | ||
42 | if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&syntax, offset) { | ||
43 | let name_range = name_ref.syntax().text_range(); | ||
44 | |||
45 | if let Some(range) = text_range { | ||
46 | if name_range.is_subrange(&range) && is_match(file_id, &name_ref) { | ||
47 | refs.push(ReferenceDescriptor { | ||
48 | name: name_ref.text().to_string(), | ||
49 | range: name_ref.syntax().text_range(), | ||
50 | }); | ||
51 | } | ||
52 | } else if is_match(file_id, &name_ref) { | ||
53 | refs.push(ReferenceDescriptor { | ||
54 | name: name_ref.text().to_string(), | ||
55 | range: name_ref.syntax().text_range(), | ||
56 | }); | ||
57 | } | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return refs; | ||
63 | } | ||
64 | |||
65 | impl Definition { | ||
66 | pub fn scope(&self, db: &RootDatabase) -> SearchScope { | ||
16 | let module_src = self.container.definition_source(db); | 67 | let module_src = self.container.definition_source(db); |
17 | let file_id = module_src.file_id.original_file(db); | 68 | let file_id = module_src.file_id.original_file(db); |
18 | 69 | ||
@@ -22,16 +73,16 @@ impl Declaration { | |||
22 | DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), | 73 | DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), |
23 | DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), | 74 | DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), |
24 | }; | 75 | }; |
25 | return Some(SearchScope { scope: vec![(file_id, Some(range))] }); | 76 | return SearchScope { scope: vec![(file_id, Some(range))] }; |
26 | } | 77 | } |
27 | 78 | ||
28 | if let Some(vis) = self.visibility { | 79 | if let Some(ref vis) = self.visibility { |
29 | let source_root_id = db.file_source_root(file_id); | 80 | let source_root_id = db.file_source_root(file_id); |
30 | let source_root = db.source_root(source_root_id); | 81 | let source_root = db.source_root(source_root_id); |
31 | let mut files = source_root.walk().map(|id| (id.into(), None)).collect::<Vec<_>>(); | 82 | let mut files = source_root.walk().map(|id| (id.into(), None)).collect::<Vec<_>>(); |
32 | 83 | ||
33 | if vis.syntax().text() == "pub(crate)" { | 84 | if vis.syntax().text() == "pub(crate)" { |
34 | return Some(SearchScope { scope: files }); | 85 | return SearchScope { scope: files }; |
35 | } | 86 | } |
36 | if vis.syntax().text() == "pub" { | 87 | if vis.syntax().text() == "pub" { |
37 | let krate = self.container.krate(db).unwrap(); | 88 | let krate = self.container.krate(db).unwrap(); |
@@ -48,17 +99,15 @@ impl Declaration { | |||
48 | } | 99 | } |
49 | } | 100 | } |
50 | 101 | ||
51 | return Some(SearchScope { scope: files }); | 102 | return SearchScope { scope: files }; |
52 | } | 103 | } |
53 | // FIXME: extend to "pub(super)" and "pub(in path)" cases, | 104 | // FIXME: "pub(super)", "pub(in path)" |
54 | // then remove `Option` | ||
55 | return None; | ||
56 | } | 105 | } |
57 | 106 | ||
58 | let range = match module_src.ast { | 107 | let range = match module_src.ast { |
59 | ModuleSource::Module(m) => Some(m.syntax().text_range()), | 108 | ModuleSource::Module(m) => Some(m.syntax().text_range()), |
60 | ModuleSource::SourceFile(_) => None, | 109 | ModuleSource::SourceFile(_) => None, |
61 | }; | 110 | }; |
62 | Some(SearchScope { scope: vec![(file_id, range)] }) | 111 | SearchScope { scope: vec![(file_id, range)] } |
63 | } | 112 | } |
64 | } | 113 | } |