diff options
-rw-r--r-- | crates/assists/src/handlers/extract_module_to_file.rs | 169 |
1 files changed, 66 insertions, 103 deletions
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs index 5fc190fa6..3e67fdca2 100644 --- a/crates/assists/src/handlers/extract_module_to_file.rs +++ b/crates/assists/src/handlers/extract_module_to_file.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | use ast::edit::IndentLevel; | 1 | use ast::edit::IndentLevel; |
2 | use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt}; | 2 | use ide_db::base_db::AnchoredPathBuf; |
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, edit::AstNodeEdit, NameOwner}, | 4 | ast::{self, edit::AstNodeEdit, NameOwner}, |
5 | AstNode, | 5 | AstNode, |
@@ -21,43 +21,44 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
21 | // mod foo; | 21 | // mod foo; |
22 | // ``` | 22 | // ``` |
23 | pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 23 | pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
24 | let assist_id = AssistId("extract_module_to_file", AssistKind::RefactorExtract); | ||
25 | let assist_label = "Extract module to file"; | ||
26 | let db = ctx.db(); | ||
27 | let module_ast = ctx.find_node_at_offset::<ast::Module>()?; | 24 | let module_ast = ctx.find_node_at_offset::<ast::Module>()?; |
28 | let module_items = module_ast.item_list()?; | ||
29 | let dedent_module_items_text = module_items.dedent(IndentLevel(1)).to_string(); | ||
30 | let module_name = module_ast.name()?; | 25 | let module_name = module_ast.name()?; |
26 | |||
27 | let module_def = ctx.sema.to_def(&module_ast)?; | ||
28 | let parent_module = module_def.parent(ctx.db())?; | ||
29 | |||
30 | let module_items = module_ast.item_list()?; | ||
31 | let target = module_ast.syntax().text_range(); | 31 | let target = module_ast.syntax().text_range(); |
32 | let anchor_file_id = ctx.frange.file_id; | 32 | let anchor_file_id = ctx.frange.file_id; |
33 | let sr = db.file_source_root(anchor_file_id); | ||
34 | let sr = db.source_root(sr); | ||
35 | let file_path = sr.path_for_file(&anchor_file_id)?; | ||
36 | let (file_name, file_ext) = file_path.name_and_extension()?; | ||
37 | acc.add(assist_id, assist_label, target, |builder| { | ||
38 | builder.replace(target, format!("mod {};", module_name)); | ||
39 | let path = if is_main_or_lib(file_name) { | ||
40 | format!("./{}.{}", module_name, file_ext.unwrap()) | ||
41 | } else { | ||
42 | format!("./{}/{}.{}", file_name, module_name, file_ext.unwrap()) | ||
43 | }; | ||
44 | let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; | ||
45 | let contents = update_module_items_string(dedent_module_items_text); | ||
46 | builder.create_file(dst, contents); | ||
47 | }) | ||
48 | } | ||
49 | fn is_main_or_lib(file_name: &str) -> bool { | ||
50 | file_name == "main".to_string() || file_name == "lib".to_string() | ||
51 | } | ||
52 | fn update_module_items_string(items_str: String) -> String { | ||
53 | let mut items_string_lines: Vec<&str> = items_str.lines().collect(); | ||
54 | items_string_lines.pop(); // Delete last line | ||
55 | items_string_lines.reverse(); | ||
56 | items_string_lines.pop(); // Delete first line | ||
57 | items_string_lines.reverse(); | ||
58 | 33 | ||
59 | let string = items_string_lines.join("\n"); | 34 | acc.add( |
60 | format!("{}", string) | 35 | AssistId("extract_module_to_file", AssistKind::RefactorExtract), |
36 | "Extract module to file", | ||
37 | target, | ||
38 | |builder| { | ||
39 | let path = { | ||
40 | let dir = match parent_module.name(ctx.db()) { | ||
41 | Some(name) if !parent_module.is_mod_rs(ctx.db()) => format!("{}/", name), | ||
42 | _ => String::new(), | ||
43 | }; | ||
44 | format!("./{}{}.rs", dir, module_name) | ||
45 | }; | ||
46 | let contents = { | ||
47 | let items = module_items.dedent(IndentLevel(1)).to_string(); | ||
48 | let mut items = | ||
49 | items.trim_start_matches('{').trim_end_matches('}').trim().to_string(); | ||
50 | if !items.is_empty() { | ||
51 | items.push('\n'); | ||
52 | } | ||
53 | items | ||
54 | }; | ||
55 | |||
56 | builder.replace(target, format!("mod {};", module_name)); | ||
57 | |||
58 | let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; | ||
59 | builder.create_file(dst, contents); | ||
60 | }, | ||
61 | ) | ||
61 | } | 62 | } |
62 | 63 | ||
63 | #[cfg(test)] | 64 | #[cfg(test)] |
@@ -67,104 +68,66 @@ mod tests { | |||
67 | use super::*; | 68 | use super::*; |
68 | 69 | ||
69 | #[test] | 70 | #[test] |
70 | fn extract_module_to_file_with_basic_module() { | 71 | fn extract_from_root() { |
71 | check_assist( | 72 | check_assist( |
72 | extract_module_to_file, | 73 | extract_module_to_file, |
73 | r#" | 74 | r#" |
74 | //- /foo.rs crate:foo | ||
75 | mod tests {<|> | 75 | mod tests {<|> |
76 | #[test] fn t() {} | 76 | #[test] fn t() {} |
77 | } | 77 | } |
78 | "#, | 78 | "#, |
79 | r#" | 79 | r#" |
80 | //- /foo.rs | 80 | //- /main.rs |
81 | mod tests; | 81 | mod tests; |
82 | //- /foo/tests.rs | 82 | //- /tests.rs |
83 | #[test] fn t() {}"#, | 83 | #[test] fn t() {} |
84 | ) | ||
85 | } | ||
86 | |||
87 | #[test] | ||
88 | fn extract_module_to_file_with_file_path() { | ||
89 | check_assist( | ||
90 | extract_module_to_file, | ||
91 | r#" | ||
92 | //- /src/foo.rs crate:foo | ||
93 | mod bar {<|> | ||
94 | fn f() { | ||
95 | |||
96 | } | ||
97 | } | ||
98 | fn main() { | ||
99 | println!("Hello, world!"); | ||
100 | } | ||
101 | "#, | 84 | "#, |
102 | r#" | 85 | ); |
103 | //- /src/foo.rs | ||
104 | mod bar; | ||
105 | fn main() { | ||
106 | println!("Hello, world!"); | ||
107 | } | ||
108 | //- /src/foo/bar.rs | ||
109 | fn f() { | ||
110 | |||
111 | }"#, | ||
112 | ) | ||
113 | } | 86 | } |
114 | 87 | ||
115 | #[test] | 88 | #[test] |
116 | fn extract_module_to_file_with_main_filw() { | 89 | fn extract_from_submodule() { |
117 | check_assist( | 90 | check_assist( |
118 | extract_module_to_file, | 91 | extract_module_to_file, |
119 | r#" | 92 | r#" |
120 | //- /main.rs | 93 | //- /main.rs |
121 | mod foo {<|> | 94 | mod submodule; |
122 | fn f() { | 95 | //- /submodule.rs |
123 | 96 | mod inner<|> { | |
124 | } | 97 | fn f() {} |
125 | } | ||
126 | fn main() { | ||
127 | println!("Hello, world!"); | ||
128 | } | 98 | } |
99 | fn g() {} | ||
129 | "#, | 100 | "#, |
130 | r#" | 101 | r#" |
131 | //- /main.rs | 102 | //- /submodule.rs |
132 | mod foo; | 103 | mod inner; |
133 | fn main() { | 104 | fn g() {} |
134 | println!("Hello, world!"); | 105 | //- /submodule/inner.rs |
135 | } | 106 | fn f() {} |
136 | //- /foo.rs | 107 | "#, |
137 | fn f() { | 108 | ); |
138 | |||
139 | }"#, | ||
140 | ) | ||
141 | } | 109 | } |
142 | 110 | ||
143 | #[test] | 111 | #[test] |
144 | fn extract_module_to_file_with_lib_file() { | 112 | fn extract_from_mod_rs() { |
145 | check_assist( | 113 | check_assist( |
146 | extract_module_to_file, | 114 | extract_module_to_file, |
147 | r#" | 115 | r#" |
148 | //- /lib.rs | 116 | //- /main.rs |
149 | mod foo {<|> | 117 | mod submodule; |
150 | fn f() { | 118 | //- /submodule/mod.rs |
151 | 119 | mod inner<|> { | |
152 | } | 120 | fn f() {} |
153 | } | ||
154 | fn main() { | ||
155 | println!("Hello, world!"); | ||
156 | } | 121 | } |
122 | fn g() {} | ||
157 | "#, | 123 | "#, |
158 | r#" | 124 | r#" |
159 | //- /lib.rs | 125 | //- /submodule/mod.rs |
160 | mod foo; | 126 | mod inner; |
161 | fn main() { | 127 | fn g() {} |
162 | println!("Hello, world!"); | 128 | //- /submodule/inner.rs |
163 | } | 129 | fn f() {} |
164 | //- /foo.rs | 130 | "#, |
165 | fn f() { | 131 | ); |
166 | |||
167 | }"#, | ||
168 | ) | ||
169 | } | 132 | } |
170 | } | 133 | } |