aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/references
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api/src/references')
-rw-r--r--crates/ra_ide_api/src/references/classify.rs143
-rw-r--r--crates/ra_ide_api/src/references/definition.rs177
-rw-r--r--crates/ra_ide_api/src/references/rename.rs467
-rw-r--r--crates/ra_ide_api/src/references/search_scope.rs61
4 files changed, 848 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs
new file mode 100644
index 000000000..0b604a5cf
--- /dev/null
+++ b/crates/ra_ide_api/src/references/classify.rs
@@ -0,0 +1,143 @@
1use hir::{
2 AssocItem, Either, EnumVariant, FromSource, Module, ModuleDef, ModuleSource, Path,
3 PathResolution, Source, SourceAnalyzer, StructField,
4};
5use ra_db::FileId;
6use ra_syntax::{ast, match_ast, AstNode, AstPtr};
7
8use super::{definition::HasDefinition, Definition, NameKind};
9use crate::db::RootDatabase;
10
11use hir::{db::AstDatabase, HirFileId};
12
13pub(crate) fn classify_name(
14 db: &RootDatabase,
15 file_id: FileId,
16 name: &ast::Name,
17) -> Option<Definition> {
18 let parent = name.syntax().parent()?;
19 let file_id = file_id.into();
20
21 match_ast! {
22 match parent {
23 ast::BindPat(it) => {
24 decl_from_pat(db, file_id, AstPtr::new(&it))
25 },
26 ast::RecordFieldDef(it) => {
27 StructField::from_def(db, file_id, it)
28 },
29 ast::ImplItem(it) => {
30 AssocItem::from_def(db, file_id, it.clone()).or_else(|| {
31 match it {
32 ast::ImplItem::FnDef(f) => ModuleDef::from_def(db, file_id, f.into()),
33 ast::ImplItem::ConstDef(c) => ModuleDef::from_def(db, file_id, c.into()),
34 ast::ImplItem::TypeAliasDef(a) => ModuleDef::from_def(db, file_id, a.into()),
35 }
36 })
37 },
38 ast::EnumVariant(it) => {
39 let src = hir::Source { file_id, ast: it.clone() };
40 let def: ModuleDef = EnumVariant::from_source(db, src)?.into();
41 Some(def.definition(db))
42 },
43 ast::ModuleItem(it) => {
44 ModuleDef::from_def(db, file_id, it)
45 },
46 _ => None,
47 }
48 }
49}
50
51pub(crate) fn classify_name_ref(
52 db: &RootDatabase,
53 file_id: FileId,
54 name_ref: &ast::NameRef,
55) -> Option<Definition> {
56 let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None);
57 let parent = name_ref.syntax().parent()?;
58 match_ast! {
59 match parent {
60 ast::MethodCallExpr(it) => {
61 return AssocItem::from_ref(db, &analyzer, it);
62 },
63 ast::FieldExpr(it) => {
64 if let Some(field) = analyzer.resolve_field(&it) {
65 return Some(field.definition(db));
66 }
67 },
68 ast::RecordField(it) => {
69 if let Some(record_lit) = it.syntax().ancestors().find_map(ast::RecordLit::cast) {
70 let variant_def = analyzer.resolve_record_literal(&record_lit)?;
71 let hir_path = Path::from_name_ref(name_ref);
72 let hir_name = hir_path.as_ident()?;
73 let field = variant_def.field(db, hir_name)?;
74 return Some(field.definition(db));
75 }
76 },
77 _ => (),
78 }
79 }
80
81 let ast = ModuleSource::from_child_node(db, file_id, &parent);
82 let file_id = file_id.into();
83 let container = Module::from_definition(db, Source { file_id, ast })?;
84 let visibility = None;
85
86 if let Some(macro_call) =
87 parent.parent().and_then(|node| node.parent()).and_then(ast::MacroCall::cast)
88 {
89 if let Some(mac) = analyzer.resolve_macro_call(db, &macro_call) {
90 return Some(Definition { item: NameKind::Macro(mac), container, visibility });
91 }
92 }
93
94 // General case, a path or a local:
95 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
96 let resolved = analyzer.resolve_path(db, &path)?;
97 match resolved {
98 PathResolution::Def(def) => Some(def.definition(db)),
99 PathResolution::LocalBinding(Either::A(pat)) => decl_from_pat(db, file_id, pat),
100 PathResolution::LocalBinding(Either::B(par)) => {
101 Some(Definition { item: NameKind::SelfParam(par), container, visibility })
102 }
103 PathResolution::GenericParam(par) => {
104 // FIXME: get generic param def
105 Some(Definition { item: NameKind::GenericParam(par), container, visibility })
106 }
107 PathResolution::Macro(def) => {
108 Some(Definition { item: NameKind::Macro(def), container, visibility })
109 }
110 PathResolution::SelfType(impl_block) => {
111 let ty = impl_block.target_ty(db);
112 let container = impl_block.module();
113 Some(Definition { item: NameKind::SelfType(ty), container, visibility })
114 }
115 PathResolution::AssocItem(assoc) => Some(assoc.definition(db)),
116 }
117}
118
119fn decl_from_pat(
120 db: &RootDatabase,
121 file_id: HirFileId,
122 pat: AstPtr<ast::BindPat>,
123) -> Option<Definition> {
124 let root = db.parse_or_expand(file_id)?;
125 // FIXME: use match_ast!
126 let def = pat.to_node(&root).syntax().ancestors().find_map(|node| {
127 if let Some(it) = ast::FnDef::cast(node.clone()) {
128 let src = hir::Source { file_id, ast: it };
129 Some(hir::Function::from_source(db, src)?.into())
130 } else if let Some(it) = ast::ConstDef::cast(node.clone()) {
131 let src = hir::Source { file_id, ast: it };
132 Some(hir::Const::from_source(db, src)?.into())
133 } else if let Some(it) = ast::StaticDef::cast(node.clone()) {
134 let src = hir::Source { file_id, ast: it };
135 Some(hir::Static::from_source(db, src)?.into())
136 } else {
137 None
138 }
139 })?;
140 let item = NameKind::Pat((def, pat));
141 let container = def.module(db);
142 Some(Definition { item, container, visibility: None })
143}
diff --git a/crates/ra_ide_api/src/references/definition.rs b/crates/ra_ide_api/src/references/definition.rs
new file mode 100644
index 000000000..65b1f8dd7
--- /dev/null
+++ b/crates/ra_ide_api/src/references/definition.rs
@@ -0,0 +1,177 @@
1use hir::{
2 db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef,
3 Module, ModuleDef, SourceAnalyzer, StructField, Ty, VariantDef,
4};
5use ra_syntax::{ast, ast::VisibilityOwner, AstNode, AstPtr};
6
7use crate::db::RootDatabase;
8
9#[derive(Debug, PartialEq, Eq)]
10pub enum NameKind {
11 Macro(MacroDef),
12 FieldAccess(StructField),
13 AssocItem(AssocItem),
14 Def(ModuleDef),
15 SelfType(Ty),
16 Pat((DefWithBody, AstPtr<ast::BindPat>)),
17 SelfParam(AstPtr<ast::SelfParam>),
18 GenericParam(u32),
19}
20
21#[derive(PartialEq, Eq)]
22pub(crate) struct Definition {
23 pub visibility: Option<ast::Visibility>,
24 pub container: Module,
25 pub item: NameKind,
26}
27
28pub(super) trait HasDefinition {
29 type Def;
30 type Ref;
31
32 fn definition(self, db: &RootDatabase) -> Definition;
33 fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition>;
34 fn from_ref(
35 db: &RootDatabase,
36 analyzer: &SourceAnalyzer,
37 refer: Self::Ref,
38 ) -> Option<Definition>;
39}
40
41// fn decl_from_pat(
42// db: &RootDatabase,
43// file_id: HirFileId,
44// pat: AstPtr<ast::BindPat>,
45// ) -> Option<Definition> {
46// let root = db.parse_or_expand(file_id)?;
47// // FIXME: use match_ast!
48// let def = pat.to_node(&root).syntax().ancestors().find_map(|node| {
49// if let Some(it) = ast::FnDef::cast(node.clone()) {
50// let src = hir::Source { file_id, ast: it };
51// Some(hir::Function::from_source(db, src)?.into())
52// } else if let Some(it) = ast::ConstDef::cast(node.clone()) {
53// let src = hir::Source { file_id, ast: it };
54// Some(hir::Const::from_source(db, src)?.into())
55// } else if let Some(it) = ast::StaticDef::cast(node.clone()) {
56// let src = hir::Source { file_id, ast: it };
57// Some(hir::Static::from_source(db, src)?.into())
58// } else {
59// None
60// }
61// })?;
62// let item = NameKind::Pat((def, pat));
63// let container = def.module(db);
64// Some(Definition { item, container, visibility: None })
65// }
66
67impl HasDefinition for StructField {
68 type Def = ast::RecordFieldDef;
69 type Ref = ast::FieldExpr;
70
71 fn definition(self, db: &RootDatabase) -> Definition {
72 let item = NameKind::FieldAccess(self);
73 let parent = self.parent_def(db);
74 let container = parent.module(db);
75 let visibility = match parent {
76 VariantDef::Struct(s) => s.source(db).ast.visibility(),
77 VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(),
78 };
79 Definition { item, container, visibility }
80 }
81
82 fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> {
83 let src = hir::Source { file_id, ast: hir::FieldSource::Named(def) };
84 let field = StructField::from_source(db, src)?;
85 Some(field.definition(db))
86 }
87
88 fn from_ref(
89 db: &RootDatabase,
90 analyzer: &SourceAnalyzer,
91 refer: Self::Ref,
92 ) -> Option<Definition> {
93 let field = analyzer.resolve_field(&refer)?;
94 Some(field.definition(db))
95 }
96}
97
98impl HasDefinition for AssocItem {
99 type Def = ast::ImplItem;
100 type Ref = ast::MethodCallExpr;
101
102 fn definition(self, db: &RootDatabase) -> Definition {
103 let item = NameKind::AssocItem(self);
104 let container = self.module(db);
105 let visibility = match self {
106 AssocItem::Function(f) => f.source(db).ast.visibility(),
107 AssocItem::Const(c) => c.source(db).ast.visibility(),
108 AssocItem::TypeAlias(a) => a.source(db).ast.visibility(),
109 };
110 Definition { item, container, visibility }
111 }
112
113 fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> {
114 if def.syntax().parent().and_then(ast::ItemList::cast).is_none() {
115 return None;
116 }
117 let src = hir::Source { file_id, ast: def };
118 let item = AssocItem::from_source(db, src)?;
119 Some(item.definition(db))
120 }
121
122 fn from_ref(
123 db: &RootDatabase,
124 analyzer: &SourceAnalyzer,
125 refer: Self::Ref,
126 ) -> Option<Definition> {
127 let func: AssocItem = analyzer.resolve_method_call(&refer)?.into();
128 Some(func.definition(db))
129 }
130}
131
132impl HasDefinition for ModuleDef {
133 type Def = ast::ModuleItem;
134 type Ref = ast::Path;
135
136 fn definition(self, db: &RootDatabase) -> Definition {
137 let (container, visibility) = match self {
138 ModuleDef::Module(it) => {
139 let container = it.parent(db).or_else(|| Some(it)).unwrap();
140 let visibility = it.declaration_source(db).and_then(|s| s.ast.visibility());
141 (container, visibility)
142 }
143 ModuleDef::EnumVariant(it) => {
144 let container = it.module(db);
145 let visibility = it.source(db).ast.parent_enum().visibility();
146 (container, visibility)
147 }
148 ModuleDef::Function(it) => (it.module(db), it.source(db).ast.visibility()),
149 ModuleDef::Const(it) => (it.module(db), it.source(db).ast.visibility()),
150 ModuleDef::Static(it) => (it.module(db), it.source(db).ast.visibility()),
151 ModuleDef::Trait(it) => (it.module(db), it.source(db).ast.visibility()),
152 ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).ast.visibility()),
153 ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).ast.visibility()),
154 ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).ast.visibility()),
155 ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).ast.visibility()),
156 ModuleDef::BuiltinType(..) => unreachable!(),
157 };
158 let item = NameKind::Def(self);
159 Definition { item, container, visibility }
160 }
161
162 fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option<Definition> {
163 let src = hir::Source { file_id, ast: def };
164 let def = ModuleDef::from_source(db, src)?;
165 Some(def.definition(db))
166 }
167
168 fn from_ref(
169 db: &RootDatabase,
170 analyzer: &SourceAnalyzer,
171 refer: Self::Ref,
172 ) -> Option<Definition> {
173 None
174 }
175}
176
177// FIXME: impl HasDefinition for hir::MacroDef
diff --git a/crates/ra_ide_api/src/references/rename.rs b/crates/ra_ide_api/src/references/rename.rs
new file mode 100644
index 000000000..7e564a40e
--- /dev/null
+++ b/crates/ra_ide_api/src/references/rename.rs
@@ -0,0 +1,467 @@
1use hir::ModuleSource;
2use ra_db::SourceDatabase;
3use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode};
4use relative_path::{RelativePath, RelativePathBuf};
5
6use crate::{
7 db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange,
8 SourceFileEdit, TextRange,
9};
10
11use super::find_all_refs;
12
13pub(crate) fn rename(
14 db: &RootDatabase,
15 position: FilePosition,
16 new_name: &str,
17) -> Option<RangeInfo<SourceChange>> {
18 let parse = db.parse(position.file_id);
19 if let Some((ast_name, ast_module)) =
20 find_name_and_module_at_offset(parse.tree().syntax(), position)
21 {
22 let range = ast_name.syntax().text_range();
23 rename_mod(db, &ast_name, &ast_module, position, new_name)
24 .map(|info| RangeInfo::new(range, info))
25 } else {
26 rename_reference(db, position, new_name)
27 }
28}
29
30fn find_name_and_module_at_offset(
31 syntax: &SyntaxNode,
32 position: FilePosition,
33) -> Option<(ast::Name, ast::Module)> {
34 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?;
35 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?;
36 Some((ast_name, ast_module))
37}
38
39fn source_edit_from_file_id_range(
40 file_id: FileId,
41 range: TextRange,
42 new_name: &str,
43) -> SourceFileEdit {
44 SourceFileEdit {
45 file_id,
46 edit: {
47 let mut builder = ra_text_edit::TextEditBuilder::default();
48 builder.replace(range, new_name.into());
49 builder.finish()
50 },
51 }
52}
53
54fn rename_mod(
55 db: &RootDatabase,
56 ast_name: &ast::Name,
57 ast_module: &ast::Module,
58 position: FilePosition,
59 new_name: &str,
60) -> Option<SourceChange> {
61 let mut source_file_edits = Vec::new();
62 let mut file_system_edits = Vec::new();
63 let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() };
64 if let Some(module) = hir::Module::from_declaration(db, module_src) {
65 let src = module.definition_source(db);
66 let file_id = src.file_id.original_file(db);
67 match src.ast {
68 ModuleSource::SourceFile(..) => {
69 let mod_path: RelativePathBuf = db.file_relative_path(file_id);
70 // mod is defined in path/to/dir/mod.rs
71 let dst_path = if mod_path.file_stem() == Some("mod") {
72 mod_path
73 .parent()
74 .and_then(|p| p.parent())
75 .or_else(|| Some(RelativePath::new("")))
76 .map(|p| p.join(new_name).join("mod.rs"))
77 } else {
78 Some(mod_path.with_file_name(new_name).with_extension("rs"))
79 };
80 if let Some(path) = dst_path {
81 let move_file = FileSystemEdit::MoveFile {
82 src: file_id,
83 dst_source_root: db.file_source_root(position.file_id),
84 dst_path: path,
85 };
86 file_system_edits.push(move_file);
87 }
88 }
89 ModuleSource::Module(..) => {}
90 }
91 }
92
93 let edit = SourceFileEdit {
94 file_id: position.file_id,
95 edit: {
96 let mut builder = ra_text_edit::TextEditBuilder::default();
97 builder.replace(ast_name.syntax().text_range(), new_name.into());
98 builder.finish()
99 },
100 };
101 source_file_edits.push(edit);
102
103 Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits))
104}
105
106fn rename_reference(
107 db: &RootDatabase,
108 position: FilePosition,
109 new_name: &str,
110) -> Option<RangeInfo<SourceChange>> {
111 let RangeInfo { range, info: refs } = find_all_refs(db, position)?;
112
113 let edit = refs
114 .into_iter()
115 .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name))
116 .collect::<Vec<_>>();
117
118 if edit.is_empty() {
119 return None;
120 }
121
122 Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit)))
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::{
128 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId,
129 ReferenceSearchResult,
130 };
131 use insta::assert_debug_snapshot;
132 use test_utils::assert_eq_text;
133
134 #[test]
135 fn test_find_all_refs_for_local() {
136 let code = r#"
137 fn main() {
138 let mut i = 1;
139 let j = 1;
140 i = i<|> + j;
141
142 {
143 i = 0;
144 }
145
146 i = 5;
147 }"#;
148
149 let refs = get_all_refs(code);
150 assert_eq!(refs.len(), 5);
151 }
152
153 #[test]
154 fn test_find_all_refs_for_param_inside() {
155 let code = r#"
156 fn foo(i : u32) -> u32 {
157 i<|>
158 }"#;
159
160 let refs = get_all_refs(code);
161 assert_eq!(refs.len(), 2);
162 }
163
164 #[test]
165 fn test_find_all_refs_for_fn_param() {
166 let code = r#"
167 fn foo(i<|> : u32) -> u32 {
168 i
169 }"#;
170
171 let refs = get_all_refs(code);
172 assert_eq!(refs.len(), 2);
173 }
174
175 #[test]
176 fn test_find_all_refs_field_name() {
177 let code = r#"
178 //- /lib.rs
179 struct Foo {
180 pub spam<|>: u32,
181 }
182
183 fn main(s: Foo) {
184 let f = s.spam;
185 }
186 "#;
187
188 let refs = get_all_refs(code);
189 assert_eq!(refs.len(), 2);
190 }
191
192 #[test]
193 fn test_find_all_refs_impl_item_name() {
194 let code = r#"
195 //- /lib.rs
196 struct Foo;
197 impl Foo {
198 fn f<|>(&self) { }
199 }
200 "#;
201
202 let refs = get_all_refs(code);
203 assert_eq!(refs.len(), 1);
204 }
205
206 #[test]
207 fn test_find_all_refs_enum_var_name() {
208 let code = r#"
209 //- /lib.rs
210 enum Foo {
211 A,
212 B<|>,
213 C,
214 }
215 "#;
216
217 let refs = get_all_refs(code);
218 assert_eq!(refs.len(), 1);
219 }
220
221 #[test]
222 fn test_find_all_refs_modules() {
223 let code = r#"
224 //- /lib.rs
225 pub mod foo;
226 pub mod bar;
227
228 fn f() {
229 let i = foo::Foo { n: 5 };
230 }
231
232 //- /foo.rs
233 use crate::bar;
234
235 pub struct Foo {
236 pub n: u32,
237 }
238
239 fn f() {
240 let i = bar::Bar { n: 5 };
241 }
242
243 //- /bar.rs
244 use crate::foo;
245
246 pub struct Bar {
247 pub n: u32,
248 }
249
250 fn f() {
251 let i = foo::Foo<|> { n: 5 };
252 }
253 "#;
254
255 let (analysis, pos) = analysis_and_position(code);
256 let refs = analysis.find_all_refs(pos).unwrap().unwrap();
257 assert_eq!(refs.len(), 3);
258 }
259
260 fn get_all_refs(text: &str) -> ReferenceSearchResult {
261 let (analysis, position) = single_file_with_position(text);
262 analysis.find_all_refs(position).unwrap().unwrap()
263 }
264
265 #[test]
266 fn test_rename_for_local() {
267 test_rename(
268 r#"
269 fn main() {
270 let mut i = 1;
271 let j = 1;
272 i = i<|> + j;
273
274 {
275 i = 0;
276 }
277
278 i = 5;
279 }"#,
280 "k",
281 r#"
282 fn main() {
283 let mut k = 1;
284 let j = 1;
285 k = k + j;
286
287 {
288 k = 0;
289 }
290
291 k = 5;
292 }"#,
293 );
294 }
295
296 #[test]
297 fn test_rename_for_param_inside() {
298 test_rename(
299 r#"
300 fn foo(i : u32) -> u32 {
301 i<|>
302 }"#,
303 "j",
304 r#"
305 fn foo(j : u32) -> u32 {
306 j
307 }"#,
308 );
309 }
310
311 #[test]
312 fn test_rename_refs_for_fn_param() {
313 test_rename(
314 r#"
315 fn foo(i<|> : u32) -> u32 {
316 i
317 }"#,
318 "new_name",
319 r#"
320 fn foo(new_name : u32) -> u32 {
321 new_name
322 }"#,
323 );
324 }
325
326 #[test]
327 fn test_rename_for_mut_param() {
328 test_rename(
329 r#"
330 fn foo(mut i<|> : u32) -> u32 {
331 i
332 }"#,
333 "new_name",
334 r#"
335 fn foo(mut new_name : u32) -> u32 {
336 new_name
337 }"#,
338 );
339 }
340
341 #[test]
342 fn test_rename_mod() {
343 let (analysis, position) = analysis_and_position(
344 "
345 //- /lib.rs
346 mod bar;
347
348 //- /bar.rs
349 mod foo<|>;
350
351 //- /bar/foo.rs
352 // emtpy
353 ",
354 );
355 let new_name = "foo2";
356 let source_change = analysis.rename(position, new_name).unwrap();
357 assert_debug_snapshot!(&source_change,
358@r###"
359 Some(
360 RangeInfo {
361 range: [4; 7),
362 info: SourceChange {
363 label: "rename",
364 source_file_edits: [
365 SourceFileEdit {
366 file_id: FileId(
367 2,
368 ),
369 edit: TextEdit {
370 atoms: [
371 AtomTextEdit {
372 delete: [4; 7),
373 insert: "foo2",
374 },
375 ],
376 },
377 },
378 ],
379 file_system_edits: [
380 MoveFile {
381 src: FileId(
382 3,
383 ),
384 dst_source_root: SourceRootId(
385 0,
386 ),
387 dst_path: "bar/foo2.rs",
388 },
389 ],
390 cursor_position: None,
391 },
392 },
393 )
394 "###);
395 }
396
397 #[test]
398 fn test_rename_mod_in_dir() {
399 let (analysis, position) = analysis_and_position(
400 "
401 //- /lib.rs
402 mod fo<|>o;
403 //- /foo/mod.rs
404 // emtpy
405 ",
406 );
407 let new_name = "foo2";
408 let source_change = analysis.rename(position, new_name).unwrap();
409 assert_debug_snapshot!(&source_change,
410 @r###"
411 Some(
412 RangeInfo {
413 range: [4; 7),
414 info: SourceChange {
415 label: "rename",
416 source_file_edits: [
417 SourceFileEdit {
418 file_id: FileId(
419 1,
420 ),
421 edit: TextEdit {
422 atoms: [
423 AtomTextEdit {
424 delete: [4; 7),
425 insert: "foo2",
426 },
427 ],
428 },
429 },
430 ],
431 file_system_edits: [
432 MoveFile {
433 src: FileId(
434 2,
435 ),
436 dst_source_root: SourceRootId(
437 0,
438 ),
439 dst_path: "foo2/mod.rs",
440 },
441 ],
442 cursor_position: None,
443 },
444 },
445 )
446 "###
447 );
448 }
449
450 fn test_rename(text: &str, new_name: &str, expected: &str) {
451 let (analysis, position) = single_file_with_position(text);
452 let source_change = analysis.rename(position, new_name).unwrap();
453 let mut text_edit_builder = ra_text_edit::TextEditBuilder::default();
454 let mut file_id: Option<FileId> = None;
455 if let Some(change) = source_change {
456 for edit in change.info.source_file_edits {
457 file_id = Some(edit.file_id);
458 for atom in edit.edit.as_atoms() {
459 text_edit_builder.replace(atom.delete, atom.insert.clone());
460 }
461 }
462 }
463 let result =
464 text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap());
465 assert_eq_text!(expected, &*result);
466 }
467}
diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs
new file mode 100644
index 000000000..557ee7b57
--- /dev/null
+++ b/crates/ra_ide_api/src/references/search_scope.rs
@@ -0,0 +1,61 @@
1use hir::{DefWithBody, HasSource, ModuleSource};
2use ra_db::{FileId, SourceDatabase};
3use ra_syntax::{AstNode, TextRange};
4
5use crate::db::RootDatabase;
6
7use super::{Definition, NameKind};
8
9pub(crate) struct SearchScope {
10 pub scope: Vec<(FileId, Option<TextRange>)>,
11}
12
13impl Definition {
14 pub fn scope(&self, db: &RootDatabase) -> SearchScope {
15 let module_src = self.container.definition_source(db);
16 let file_id = module_src.file_id.original_file(db);
17
18 if let NameKind::Pat((def, _)) = self.item {
19 let range = match def {
20 DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(),
21 DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(),
22 DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(),
23 };
24 return SearchScope { scope: vec![(file_id, Some(range))] };
25 }
26
27 if let Some(ref vis) = self.visibility {
28 let source_root_id = db.file_source_root(file_id);
29 let source_root = db.source_root(source_root_id);
30 let mut files = source_root.walk().map(|id| (id.into(), None)).collect::<Vec<_>>();
31
32 if vis.syntax().to_string().as_str() == "pub(crate)" {
33 return SearchScope { scope: files };
34 }
35 if vis.syntax().to_string().as_str() == "pub" {
36 let krate = self.container.krate(db).unwrap();
37 let crate_graph = db.crate_graph();
38
39 for crate_id in crate_graph.iter() {
40 let mut crate_deps = crate_graph.dependencies(crate_id);
41
42 if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) {
43 let root_file = crate_graph.crate_root(crate_id);
44 let source_root_id = db.file_source_root(root_file);
45 let source_root = db.source_root(source_root_id);
46 files.extend(source_root.walk().map(|id| (id.into(), None)));
47 }
48 }
49
50 return SearchScope { scope: files };
51 }
52 // FIXME: "pub(super)", "pub(in path)"
53 }
54
55 let range = match module_src.ast {
56 ModuleSource::Module(m) => Some(m.syntax().text_range()),
57 ModuleSource::SourceFile(_) => None,
58 };
59 SearchScope { scope: vec![(file_id, range)] }
60 }
61}