diff options
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 9 | ||||
-rw-r--r-- | crates/ra_ide_api/src/hover.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api/src/imp.rs | 95 | ||||
-rw-r--r-- | crates/ra_ide_api/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/ra_ide_api/src/rename.rs | 136 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/main.rs | 20 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/snapshots/test__rename_mod.snap | 36 | ||||
-rw-r--r-- | crates/ra_ide_api/tests/test/snapshots/test__rename_mod_in_dir.snap | 36 |
8 files changed, 233 insertions, 105 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 7ab8eeae2..be82d804a 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -35,14 +35,9 @@ pub fn module_from_declaration( | |||
35 | let parent_module = module_from_file_id(db, file_id); | 35 | let parent_module = module_from_file_id(db, file_id); |
36 | let child_name = decl.name(); | 36 | let child_name = decl.name(); |
37 | match (parent_module, child_name) { | 37 | match (parent_module, child_name) { |
38 | (Some(parent_module), Some(child_name)) => { | 38 | (Some(parent_module), Some(child_name)) => parent_module.child(db, &child_name.as_name()), |
39 | if let Some(child) = parent_module.child(db, &child_name.as_name()) { | 39 | _ => None, |
40 | return Some(child); | ||
41 | } | ||
42 | } | ||
43 | _ => (), | ||
44 | } | 40 | } |
45 | None | ||
46 | } | 41 | } |
47 | 42 | ||
48 | /// Locates the module by position in the source code. | 43 | /// Locates the module by position in the source code. |
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index d91151c15..4d4bfbc4d 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ra_db::{SyntaxDatabase}; | 1 | use ra_db::{SyntaxDatabase}; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | AstNode, SyntaxNode, TreeArc, | 3 | AstNode, SyntaxNode, TreeArc, ast, |
4 | ast::self, | ||
5 | algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, | 4 | algo::{find_covering_node, find_node_at_offset, find_leaf_at_offset, visit::{visitor, Visitor}}, |
6 | }; | 5 | }; |
7 | 6 | ||
diff --git a/crates/ra_ide_api/src/imp.rs b/crates/ra_ide_api/src/imp.rs index 3f0de8b5b..b52a3f4d5 100644 --- a/crates/ra_ide_api/src/imp.rs +++ b/crates/ra_ide_api/src/imp.rs | |||
@@ -1,10 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use hir::{ | 3 | use hir::{ |
4 | self, Problem, source_binder::{ | 4 | self, Problem, source_binder |
5 | self, | ||
6 | module_from_declaration | ||
7 | }, ModuleSource, | ||
8 | }; | 5 | }; |
9 | use ra_db::{ | 6 | use ra_db::{ |
10 | FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase, | 7 | FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase, |
@@ -22,6 +19,7 @@ use crate::{ | |||
22 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, | 19 | CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit, |
23 | Query, RootChange, SourceChange, SourceFileEdit, | 20 | Query, RootChange, SourceChange, SourceFileEdit, |
24 | symbol_index::{FileSymbol, LibrarySymbolsQuery}, | 21 | symbol_index::{FileSymbol, LibrarySymbolsQuery}, |
22 | rename::rename | ||
25 | }; | 23 | }; |
26 | 24 | ||
27 | impl db::RootDatabase { | 25 | impl db::RootDatabase { |
@@ -234,94 +232,11 @@ impl db::RootDatabase { | |||
234 | .collect() | 232 | .collect() |
235 | } | 233 | } |
236 | 234 | ||
237 | <<<<<<< HEAD | 235 | pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Option<SourceChange> { |
238 | pub(crate) fn rename(&self, position: FilePosition, new_name: &str) -> Vec<SourceFileEdit> { | 236 | rename(self, position, new_name) |
239 | self.find_all_refs(position) | ||
240 | .iter() | ||
241 | .map(|(file_id, text_range)| SourceFileEdit { | ||
242 | file_id: *file_id, | ||
243 | ======= | ||
244 | pub(crate) fn rename( | ||
245 | &self, | ||
246 | position: FilePosition, | ||
247 | new_name: &str, | ||
248 | ) -> Cancelable<Option<SourceChange>> { | ||
249 | let mut source_file_edits = Vec::new(); | ||
250 | let mut file_system_edits = Vec::new(); | ||
251 | |||
252 | let source_file = self.source_file(position.file_id); | ||
253 | let syntax = source_file.syntax(); | ||
254 | // We are rename a mod | ||
255 | if let (Some(ast_module), Some(name)) = ( | ||
256 | find_node_at_offset::<ast::Module>(syntax, position.offset), | ||
257 | find_node_at_offset::<ast::Name>(syntax, position.offset), | ||
258 | ) { | ||
259 | if let Some(module) = module_from_declaration(self, position.file_id, &ast_module)? { | ||
260 | let (file_id, module_source) = module.definition_source(self)?; | ||
261 | match module_source { | ||
262 | ModuleSource::SourceFile(..) => { | ||
263 | let move_file = FileSystemEdit::MoveFile { | ||
264 | src: file_id, | ||
265 | dst_source_root: self.file_source_root(position.file_id), | ||
266 | dst_path: self | ||
267 | .file_relative_path(file_id) | ||
268 | .with_file_name(new_name) | ||
269 | .with_extension("rs"), | ||
270 | }; | ||
271 | file_system_edits.push(move_file); | ||
272 | } | ||
273 | ModuleSource::Module(..) => {} | ||
274 | } | ||
275 | } | ||
276 | |||
277 | let edit = SourceFileEdit { | ||
278 | file_id: position.file_id, | ||
279 | >>>>>>> rename mod | ||
280 | edit: { | ||
281 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
282 | builder.replace(name.syntax().range(), new_name.into()); | ||
283 | builder.finish() | ||
284 | }, | ||
285 | <<<<<<< HEAD | ||
286 | }) | ||
287 | .collect::<Vec<_>>() | ||
288 | } | 237 | } |
289 | pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> { | ||
290 | ======= | ||
291 | }; | ||
292 | source_file_edits.push(edit); | ||
293 | } | ||
294 | // rename references | ||
295 | else { | ||
296 | let edit = self | ||
297 | .find_all_refs(position)? | ||
298 | .iter() | ||
299 | .map(|(file_id, text_range)| SourceFileEdit { | ||
300 | file_id: *file_id, | ||
301 | edit: { | ||
302 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
303 | builder.replace(*text_range, new_name.into()); | ||
304 | builder.finish() | ||
305 | }, | ||
306 | }) | ||
307 | .collect::<Vec<_>>(); | ||
308 | if edit.is_empty() { | ||
309 | return Ok(None); | ||
310 | } | ||
311 | 238 | ||
312 | source_file_edits = edit; | 239 | pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Vec<FileSymbol> { |
313 | } | ||
314 | |||
315 | return Ok(Some(SourceChange { | ||
316 | label: "rename".to_string(), | ||
317 | source_file_edits, | ||
318 | file_system_edits, | ||
319 | cursor_position: None, | ||
320 | })); | ||
321 | } | ||
322 | |||
323 | pub(crate) fn index_resolve(&self, name_ref: &ast::NameRef) -> Cancelable<Vec<FileSymbol>> { | ||
324 | >>>>>>> rename mod | ||
325 | let name = name_ref.text(); | 240 | let name = name_ref.text(); |
326 | let mut query = Query::new(name.to_string()); | 241 | let mut query = Query::new(name.to_string()); |
327 | query.exact(); | 242 | query.exact(); |
diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 7b47d7b6d..1845bf443 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs | |||
@@ -23,6 +23,7 @@ mod hover; | |||
23 | mod call_info; | 23 | mod call_info; |
24 | mod syntax_highlighting; | 24 | mod syntax_highlighting; |
25 | mod parent_module; | 25 | mod parent_module; |
26 | mod rename; | ||
26 | 27 | ||
27 | use std::{fmt, sync::Arc}; | 28 | use std::{fmt, sync::Arc}; |
28 | 29 | ||
@@ -464,7 +465,7 @@ impl Analysis { | |||
464 | &self, | 465 | &self, |
465 | position: FilePosition, | 466 | position: FilePosition, |
466 | new_name: &str, | 467 | new_name: &str, |
467 | ) -> Cancelable<Vec<SourceFileEdit>> { | 468 | ) -> Cancelable<Option<SourceChange>> { |
468 | self.with_db(|db| db.rename(position, new_name)) | 469 | self.with_db(|db| db.rename(position, new_name)) |
469 | } | 470 | } |
470 | 471 | ||
diff --git a/crates/ra_ide_api/src/rename.rs b/crates/ra_ide_api/src/rename.rs new file mode 100644 index 000000000..9f8a00ae7 --- /dev/null +++ b/crates/ra_ide_api/src/rename.rs | |||
@@ -0,0 +1,136 @@ | |||
1 | use relative_path::RelativePathBuf; | ||
2 | |||
3 | use hir::{ | ||
4 | self, ModuleSource, source_binder::module_from_declaration, | ||
5 | }; | ||
6 | use ra_syntax::{ | ||
7 | algo::find_node_at_offset, | ||
8 | ast, | ||
9 | AstNode, | ||
10 | SyntaxNode | ||
11 | }; | ||
12 | |||
13 | use crate::{ | ||
14 | db::RootDatabase, | ||
15 | FilePosition, | ||
16 | FileSystemEdit, | ||
17 | SourceChange, | ||
18 | SourceFileEdit, | ||
19 | }; | ||
20 | use ra_db::{FilesDatabase, SyntaxDatabase}; | ||
21 | use relative_path::RelativePath; | ||
22 | |||
23 | pub(crate) fn rename( | ||
24 | db: &RootDatabase, | ||
25 | position: FilePosition, | ||
26 | new_name: &str, | ||
27 | ) -> Option<SourceChange> { | ||
28 | let source_file = db.source_file(position.file_id); | ||
29 | let syntax = source_file.syntax(); | ||
30 | |||
31 | if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { | ||
32 | rename_mod(db, ast_name, ast_module, position, new_name) | ||
33 | } else { | ||
34 | rename_reference(db, position, new_name) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | fn find_name_and_module_at_offset( | ||
39 | syntax: &SyntaxNode, | ||
40 | position: FilePosition, | ||
41 | ) -> Option<(&ast::Name, &ast::Module)> { | ||
42 | let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset); | ||
43 | let ast_name_parent = ast_name | ||
44 | .and_then(|n| n.syntax().parent()) | ||
45 | .and_then(|p| ast::Module::cast(p)); | ||
46 | |||
47 | if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) { | ||
48 | return Some((name, ast_module)); | ||
49 | } | ||
50 | None | ||
51 | } | ||
52 | |||
53 | fn rename_mod( | ||
54 | db: &RootDatabase, | ||
55 | ast_name: &ast::Name, | ||
56 | ast_module: &ast::Module, | ||
57 | position: FilePosition, | ||
58 | new_name: &str, | ||
59 | ) -> Option<SourceChange> { | ||
60 | let mut source_file_edits = Vec::new(); | ||
61 | let mut file_system_edits = Vec::new(); | ||
62 | |||
63 | if let Some(module) = module_from_declaration(db, position.file_id, &ast_module) { | ||
64 | let (file_id, module_source) = module.definition_source(db); | ||
65 | match module_source { | ||
66 | ModuleSource::SourceFile(..) => { | ||
67 | let mod_path: RelativePathBuf = db.file_relative_path(file_id); | ||
68 | // mod is defined in path/to/dir/mod.rs | ||
69 | let dst_path = if mod_path.file_stem() == Some("mod") { | ||
70 | mod_path | ||
71 | .parent() | ||
72 | .and_then(|p| p.parent()) | ||
73 | .or_else(|| Some(RelativePath::new(""))) | ||
74 | .map(|p| p.join(new_name).join("mod.rs")) | ||
75 | } else { | ||
76 | Some(mod_path.with_file_name(new_name).with_extension("rs")) | ||
77 | }; | ||
78 | if let Some(path) = dst_path { | ||
79 | let move_file = FileSystemEdit::MoveFile { | ||
80 | src: file_id, | ||
81 | dst_source_root: db.file_source_root(position.file_id), | ||
82 | dst_path: path, | ||
83 | }; | ||
84 | file_system_edits.push(move_file); | ||
85 | } | ||
86 | } | ||
87 | ModuleSource::Module(..) => {} | ||
88 | } | ||
89 | } | ||
90 | |||
91 | let edit = SourceFileEdit { | ||
92 | file_id: position.file_id, | ||
93 | edit: { | ||
94 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
95 | builder.replace(ast_name.syntax().range(), new_name.into()); | ||
96 | builder.finish() | ||
97 | }, | ||
98 | }; | ||
99 | source_file_edits.push(edit); | ||
100 | |||
101 | return Some(SourceChange { | ||
102 | label: "rename".to_string(), | ||
103 | source_file_edits, | ||
104 | file_system_edits, | ||
105 | cursor_position: None, | ||
106 | }); | ||
107 | } | ||
108 | |||
109 | fn rename_reference( | ||
110 | db: &RootDatabase, | ||
111 | position: FilePosition, | ||
112 | new_name: &str, | ||
113 | ) -> Option<SourceChange> { | ||
114 | let edit = db | ||
115 | .find_all_refs(position) | ||
116 | .iter() | ||
117 | .map(|(file_id, text_range)| SourceFileEdit { | ||
118 | file_id: *file_id, | ||
119 | edit: { | ||
120 | let mut builder = ra_text_edit::TextEditBuilder::default(); | ||
121 | builder.replace(*text_range, new_name.into()); | ||
122 | builder.finish() | ||
123 | }, | ||
124 | }) | ||
125 | .collect::<Vec<_>>(); | ||
126 | if edit.is_empty() { | ||
127 | return None; | ||
128 | } | ||
129 | |||
130 | return Some(SourceChange { | ||
131 | label: "rename".to_string(), | ||
132 | source_file_edits: edit, | ||
133 | file_system_edits: Vec::new(), | ||
134 | cursor_position: None, | ||
135 | }); | ||
136 | } | ||
diff --git a/crates/ra_ide_api/tests/test/main.rs b/crates/ra_ide_api/tests/test/main.rs index 4aa13b0e7..2077a89ce 100644 --- a/crates/ra_ide_api/tests/test/main.rs +++ b/crates/ra_ide_api/tests/test/main.rs | |||
@@ -7,8 +7,6 @@ use ra_syntax::TextRange; | |||
7 | use test_utils::assert_eq_text; | 7 | use test_utils::assert_eq_text; |
8 | use insta::assert_debug_snapshot_matches; | 8 | use insta::assert_debug_snapshot_matches; |
9 | 9 | ||
10 | mod runnables; | ||
11 | |||
12 | #[test] | 10 | #[test] |
13 | fn test_unresolved_module_diagnostic() { | 11 | fn test_unresolved_module_diagnostic() { |
14 | let (analysis, file_id) = single_file("mod foo;"); | 12 | let (analysis, file_id) = single_file("mod foo;"); |
@@ -182,10 +180,22 @@ fn test_rename_mod() { | |||
182 | ); | 180 | ); |
183 | let new_name = "foo2"; | 181 | let new_name = "foo2"; |
184 | let source_change = analysis.rename(position, new_name).unwrap(); | 182 | let source_change = analysis.rename(position, new_name).unwrap(); |
185 | assert_eq_dbg( | 183 | assert_debug_snapshot_matches!("rename_mod", &source_change); |
186 | r#"Some(SourceChange { label: "rename", source_file_edits: [SourceFileEdit { file_id: FileId(1), edit: TextEdit { atoms: [AtomTextEdit { delete: [4; 7), insert: "foo2" }] } }], file_system_edits: [MoveFile { src: FileId(2), dst_source_root: SourceRootId(0), dst_path: "bar/foo2.rs" }], cursor_position: None })"#, | 184 | } |
187 | &source_change, | 185 | |
186 | #[test] | ||
187 | fn test_rename_mod_in_dir() { | ||
188 | let (analysis, position) = analysis_and_position( | ||
189 | " | ||
190 | //- /lib.rs | ||
191 | mod fo<|>o; | ||
192 | //- /foo/mod.rs | ||
193 | // emtpy | ||
194 | ", | ||
188 | ); | 195 | ); |
196 | let new_name = "foo2"; | ||
197 | let source_change = analysis.rename(position, new_name).unwrap(); | ||
198 | assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change); | ||
189 | } | 199 | } |
190 | 200 | ||
191 | fn test_rename(text: &str, new_name: &str, expected: &str) { | 201 | fn test_rename(text: &str, new_name: &str, expected: &str) { |
diff --git a/crates/ra_ide_api/tests/test/snapshots/test__rename_mod.snap b/crates/ra_ide_api/tests/test/snapshots/test__rename_mod.snap new file mode 100644 index 000000000..54f622b95 --- /dev/null +++ b/crates/ra_ide_api/tests/test/snapshots/test__rename_mod.snap | |||
@@ -0,0 +1,36 @@ | |||
1 | Created: 2019-01-16T14:12:39.379431+00:00 | ||
2 | Creator: [email protected] | ||
3 | Source: crates/ra_ide_api/tests/test/main.rs | ||
4 | |||
5 | Some( | ||
6 | SourceChange { | ||
7 | label: "rename", | ||
8 | source_file_edits: [ | ||
9 | SourceFileEdit { | ||
10 | file_id: FileId( | ||
11 | 1 | ||
12 | ), | ||
13 | edit: TextEdit { | ||
14 | atoms: [ | ||
15 | AtomTextEdit { | ||
16 | delete: [4; 7), | ||
17 | insert: "foo2" | ||
18 | } | ||
19 | ] | ||
20 | } | ||
21 | } | ||
22 | ], | ||
23 | file_system_edits: [ | ||
24 | MoveFile { | ||
25 | src: FileId( | ||
26 | 2 | ||
27 | ), | ||
28 | dst_source_root: SourceRootId( | ||
29 | 0 | ||
30 | ), | ||
31 | dst_path: "bar/foo2.rs" | ||
32 | } | ||
33 | ], | ||
34 | cursor_position: None | ||
35 | } | ||
36 | ) | ||
diff --git a/crates/ra_ide_api/tests/test/snapshots/test__rename_mod_in_dir.snap b/crates/ra_ide_api/tests/test/snapshots/test__rename_mod_in_dir.snap new file mode 100644 index 000000000..aac30e89f --- /dev/null +++ b/crates/ra_ide_api/tests/test/snapshots/test__rename_mod_in_dir.snap | |||
@@ -0,0 +1,36 @@ | |||
1 | Created: 2019-01-16T14:12:39.379358+00:00 | ||
2 | Creator: [email protected] | ||
3 | Source: crates/ra_ide_api/tests/test/main.rs | ||
4 | |||
5 | Some( | ||
6 | SourceChange { | ||
7 | label: "rename", | ||
8 | source_file_edits: [ | ||
9 | SourceFileEdit { | ||
10 | file_id: FileId( | ||
11 | 1 | ||
12 | ), | ||
13 | edit: TextEdit { | ||
14 | atoms: [ | ||
15 | AtomTextEdit { | ||
16 | delete: [4; 7), | ||
17 | insert: "foo2" | ||
18 | } | ||
19 | ] | ||
20 | } | ||
21 | } | ||
22 | ], | ||
23 | file_system_edits: [ | ||
24 | MoveFile { | ||
25 | src: FileId( | ||
26 | 2 | ||
27 | ), | ||
28 | dst_source_root: SourceRootId( | ||
29 | 0 | ||
30 | ), | ||
31 | dst_path: "foo2/mod.rs" | ||
32 | } | ||
33 | ], | ||
34 | cursor_position: None | ||
35 | } | ||
36 | ) | ||