diff options
Diffstat (limited to 'crates/assists/src/handlers')
-rw-r--r-- | crates/assists/src/handlers/extract_module_to_file.rs | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs new file mode 100644 index 000000000..5fc190fa6 --- /dev/null +++ b/crates/assists/src/handlers/extract_module_to_file.rs | |||
@@ -0,0 +1,170 @@ | |||
1 | use ast::edit::IndentLevel; | ||
2 | use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt}; | ||
3 | use syntax::{ | ||
4 | ast::{self, edit::AstNodeEdit, NameOwner}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | ||
9 | |||
10 | // Assist: extract_module_to_file | ||
11 | // | ||
12 | // This assist extract module to file. | ||
13 | // | ||
14 | // ``` | ||
15 | // mod foo {<|> | ||
16 | // fn t() {} | ||
17 | // } | ||
18 | // ``` | ||
19 | // -> | ||
20 | // ``` | ||
21 | // mod foo; | ||
22 | // ``` | ||
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>()?; | ||
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()?; | ||
31 | let target = module_ast.syntax().text_range(); | ||
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 | |||
59 | let string = items_string_lines.join("\n"); | ||
60 | format!("{}", string) | ||
61 | } | ||
62 | |||
63 | #[cfg(test)] | ||
64 | mod tests { | ||
65 | use crate::tests::check_assist; | ||
66 | |||
67 | use super::*; | ||
68 | |||
69 | #[test] | ||
70 | fn extract_module_to_file_with_basic_module() { | ||
71 | check_assist( | ||
72 | extract_module_to_file, | ||
73 | r#" | ||
74 | //- /foo.rs crate:foo | ||
75 | mod tests {<|> | ||
76 | #[test] fn t() {} | ||
77 | } | ||
78 | "#, | ||
79 | r#" | ||
80 | //- /foo.rs | ||
81 | mod tests; | ||
82 | //- /foo/tests.rs | ||
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 | "#, | ||
102 | r#" | ||
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 | } | ||
114 | |||
115 | #[test] | ||
116 | fn extract_module_to_file_with_main_filw() { | ||
117 | check_assist( | ||
118 | extract_module_to_file, | ||
119 | r#" | ||
120 | //- /main.rs | ||
121 | mod foo {<|> | ||
122 | fn f() { | ||
123 | |||
124 | } | ||
125 | } | ||
126 | fn main() { | ||
127 | println!("Hello, world!"); | ||
128 | } | ||
129 | "#, | ||
130 | r#" | ||
131 | //- /main.rs | ||
132 | mod foo; | ||
133 | fn main() { | ||
134 | println!("Hello, world!"); | ||
135 | } | ||
136 | //- /foo.rs | ||
137 | fn f() { | ||
138 | |||
139 | }"#, | ||
140 | ) | ||
141 | } | ||
142 | |||
143 | #[test] | ||
144 | fn extract_module_to_file_with_lib_file() { | ||
145 | check_assist( | ||
146 | extract_module_to_file, | ||
147 | r#" | ||
148 | //- /lib.rs | ||
149 | mod foo {<|> | ||
150 | fn f() { | ||
151 | |||
152 | } | ||
153 | } | ||
154 | fn main() { | ||
155 | println!("Hello, world!"); | ||
156 | } | ||
157 | "#, | ||
158 | r#" | ||
159 | //- /lib.rs | ||
160 | mod foo; | ||
161 | fn main() { | ||
162 | println!("Hello, world!"); | ||
163 | } | ||
164 | //- /foo.rs | ||
165 | fn f() { | ||
166 | |||
167 | }"#, | ||
168 | ) | ||
169 | } | ||
170 | } | ||