aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-12-22 16:13:53 +0000
committerAleksey Kladov <[email protected]>2020-12-22 16:13:53 +0000
commit41bc32368e907f939bffb25e73bce08ae912674c (patch)
tree2f3da42b26e98cbfdc34b6eb8651e3ffc832c06e /crates/assists/src
parentb98ee075ee8baa6dc4284f04df4c7012baccda28 (diff)
Improve extract_module_to_file assist
* simplify code * correctly handle crate roots and mod.rs files (nested inline modules are still mishandled) * make sure that new text contains a trailing newline
Diffstat (limited to 'crates/assists/src')
-rw-r--r--crates/assists/src/handlers/extract_module_to_file.rs169
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 @@
1use ast::edit::IndentLevel; 1use ast::edit::IndentLevel;
2use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt}; 2use ide_db::base_db::AnchoredPathBuf;
3use syntax::{ 3use 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// ```
23pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 23pub(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}
49fn is_main_or_lib(file_name: &str) -> bool {
50 file_name == "main".to_string() || file_name == "lib".to_string()
51}
52fn 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
75mod tests {<|> 75mod tests {<|>
76 #[test] fn t() {} 76 #[test] fn t() {}
77} 77}
78"#, 78"#,
79 r#" 79 r#"
80//- /foo.rs 80//- /main.rs
81mod tests; 81mod 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
93mod bar {<|>
94 fn f() {
95
96 }
97}
98fn main() {
99 println!("Hello, world!");
100}
101"#, 84"#,
102 r#" 85 );
103//- /src/foo.rs
104mod bar;
105fn main() {
106 println!("Hello, world!");
107}
108//- /src/foo/bar.rs
109fn 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
121mod foo {<|> 94mod submodule;
122 fn f() { 95//- /submodule.rs
123 96mod inner<|> {
124 } 97 fn f() {}
125}
126fn main() {
127 println!("Hello, world!");
128} 98}
99fn g() {}
129"#, 100"#,
130 r#" 101 r#"
131//- /main.rs 102//- /submodule.rs
132mod foo; 103mod inner;
133fn main() { 104fn g() {}
134 println!("Hello, world!"); 105//- /submodule/inner.rs
135} 106fn f() {}
136//- /foo.rs 107"#,
137fn 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
149mod foo {<|> 117mod submodule;
150 fn f() { 118//- /submodule/mod.rs
151 119mod inner<|> {
152 } 120 fn f() {}
153}
154fn main() {
155 println!("Hello, world!");
156} 121}
122fn g() {}
157"#, 123"#,
158 r#" 124 r#"
159//- /lib.rs 125//- /submodule/mod.rs
160mod foo; 126mod inner;
161fn main() { 127fn g() {}
162 println!("Hello, world!"); 128//- /submodule/inner.rs
163} 129fn f() {}
164//- /foo.rs 130"#,
165fn f() { 131 );
166
167}"#,
168 )
169 } 132 }
170} 133}