aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src/references.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2019-02-08 10:51:22 +0000
committerAleksey Kladov <[email protected]>2019-02-08 11:34:30 +0000
commit4d0e58afef1722d5f5bf5970bed44594c27ecf34 (patch)
treebf66b8623925e58d29f7b331d834e25203af0b6d /crates/ra_ide_api/src/references.rs
parentbddd1242986f3155bdb1ca65495bc0623e3d211d (diff)
rename rename to references
Diffstat (limited to 'crates/ra_ide_api/src/references.rs')
-rw-r--r--crates/ra_ide_api/src/references.rs273
1 files changed, 273 insertions, 0 deletions
diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs
new file mode 100644
index 000000000..1c9491a0a
--- /dev/null
+++ b/crates/ra_ide_api/src/references.rs
@@ -0,0 +1,273 @@
1use relative_path::RelativePathBuf;
2
3use hir::{
4 self, ModuleSource, source_binder::module_from_declaration,
5};
6use ra_syntax::{
7 algo::find_node_at_offset,
8 ast,
9 AstNode,
10 SyntaxNode
11};
12
13use crate::{
14 db::RootDatabase,
15 FilePosition,
16 FileSystemEdit,
17 SourceChange,
18 SourceFileEdit,
19};
20use ra_db::SourceDatabase;
21use relative_path::RelativePath;
22
23pub(crate) fn rename(
24 db: &RootDatabase,
25 position: FilePosition,
26 new_name: &str,
27) -> Option<SourceChange> {
28 let source_file = db.parse(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
38fn 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::Module::cast(ast_name?.syntax().parent()?);
44
45 if let (Some(ast_module), Some(name)) = (ast_name_parent, ast_name) {
46 return Some((name, ast_module));
47 }
48 None
49}
50
51fn rename_mod(
52 db: &RootDatabase,
53 ast_name: &ast::Name,
54 ast_module: &ast::Module,
55 position: FilePosition,
56 new_name: &str,
57) -> Option<SourceChange> {
58 let mut source_file_edits = Vec::new();
59 let mut file_system_edits = Vec::new();
60 if let Some(module) = module_from_declaration(db, position.file_id, &ast_module) {
61 let (file_id, module_source) = module.definition_source(db);
62 match module_source {
63 ModuleSource::SourceFile(..) => {
64 let mod_path: RelativePathBuf = db.file_relative_path(file_id);
65 // mod is defined in path/to/dir/mod.rs
66 let dst_path = if mod_path.file_stem() == Some("mod") {
67 mod_path
68 .parent()
69 .and_then(|p| p.parent())
70 .or_else(|| Some(RelativePath::new("")))
71 .map(|p| p.join(new_name).join("mod.rs"))
72 } else {
73 Some(mod_path.with_file_name(new_name).with_extension("rs"))
74 };
75 if let Some(path) = dst_path {
76 let move_file = FileSystemEdit::MoveFile {
77 src: file_id,
78 dst_source_root: db.file_source_root(position.file_id),
79 dst_path: path,
80 };
81 file_system_edits.push(move_file);
82 }
83 }
84 ModuleSource::Module(..) => {}
85 }
86 }
87
88 let edit = SourceFileEdit {
89 file_id: position.file_id,
90 edit: {
91 let mut builder = ra_text_edit::TextEditBuilder::default();
92 builder.replace(ast_name.syntax().range(), new_name.into());
93 builder.finish()
94 },
95 };
96 source_file_edits.push(edit);
97
98 Some(SourceChange {
99 label: "rename".to_string(),
100 source_file_edits,
101 file_system_edits,
102 cursor_position: None,
103 })
104}
105
106fn rename_reference(
107 db: &RootDatabase,
108 position: FilePosition,
109 new_name: &str,
110) -> Option<SourceChange> {
111 let edit = db
112 .find_all_refs(position)
113 .iter()
114 .map(|(file_id, text_range)| SourceFileEdit {
115 file_id: *file_id,
116 edit: {
117 let mut builder = ra_text_edit::TextEditBuilder::default();
118 builder.replace(*text_range, new_name.into());
119 builder.finish()
120 },
121 })
122 .collect::<Vec<_>>();
123 if edit.is_empty() {
124 return None;
125 }
126
127 Some(SourceChange {
128 label: "rename".to_string(),
129 source_file_edits: edit,
130 file_system_edits: Vec::new(),
131 cursor_position: None,
132 })
133}
134
135#[cfg(test)]
136mod tests {
137 use insta::assert_debug_snapshot_matches;
138 use test_utils::assert_eq_text;
139 use crate::{
140 mock_analysis::single_file_with_position,
141 mock_analysis::analysis_and_position,
142 FileId
143};
144
145 #[test]
146 fn test_rename_for_local() {
147 test_rename(
148 r#"
149 fn main() {
150 let mut i = 1;
151 let j = 1;
152 i = i<|> + j;
153
154 {
155 i = 0;
156 }
157
158 i = 5;
159 }"#,
160 "k",
161 r#"
162 fn main() {
163 let mut k = 1;
164 let j = 1;
165 k = k + j;
166
167 {
168 k = 0;
169 }
170
171 k = 5;
172 }"#,
173 );
174 }
175
176 #[test]
177 fn test_rename_for_param_inside() {
178 test_rename(
179 r#"
180 fn foo(i : u32) -> u32 {
181 i<|>
182 }"#,
183 "j",
184 r#"
185 fn foo(j : u32) -> u32 {
186 j
187 }"#,
188 );
189 }
190
191 #[test]
192 fn test_rename_refs_for_fn_param() {
193 test_rename(
194 r#"
195 fn foo(i<|> : u32) -> u32 {
196 i
197 }"#,
198 "new_name",
199 r#"
200 fn foo(new_name : u32) -> u32 {
201 new_name
202 }"#,
203 );
204 }
205
206 #[test]
207 fn test_rename_for_mut_param() {
208 test_rename(
209 r#"
210 fn foo(mut i<|> : u32) -> u32 {
211 i
212 }"#,
213 "new_name",
214 r#"
215 fn foo(mut new_name : u32) -> u32 {
216 new_name
217 }"#,
218 );
219 }
220
221 #[test]
222 fn test_rename_mod() {
223 let (analysis, position) = analysis_and_position(
224 "
225 //- /lib.rs
226 mod bar;
227
228 //- /bar.rs
229 mod foo<|>;
230
231 //- /bar/foo.rs
232 // emtpy
233 ",
234 );
235 let new_name = "foo2";
236 let source_change = analysis.rename(position, new_name).unwrap();
237 assert_debug_snapshot_matches!("rename_mod", &source_change);
238 }
239
240 #[test]
241 fn test_rename_mod_in_dir() {
242 let (analysis, position) = analysis_and_position(
243 "
244 //- /lib.rs
245 mod fo<|>o;
246 //- /foo/mod.rs
247 // emtpy
248 ",
249 );
250 let new_name = "foo2";
251 let source_change = analysis.rename(position, new_name).unwrap();
252 assert_debug_snapshot_matches!("rename_mod_in_dir", &source_change);
253 }
254
255 fn test_rename(text: &str, new_name: &str, expected: &str) {
256 let (analysis, position) = single_file_with_position(text);
257 let source_change = analysis.rename(position, new_name).unwrap();
258 let mut text_edit_bulder = ra_text_edit::TextEditBuilder::default();
259 let mut file_id: Option<FileId> = None;
260 if let Some(change) = source_change {
261 for edit in change.source_file_edits {
262 file_id = Some(edit.file_id);
263 for atom in edit.edit.as_atoms() {
264 text_edit_bulder.replace(atom.delete, atom.insert.clone());
265 }
266 }
267 }
268 let result = text_edit_bulder
269 .finish()
270 .apply(&*analysis.file_text(file_id.unwrap()));
271 assert_eq_text!(expected, &*result);
272 }
273}