]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / move_to_mod_rs.rs
1 use ide_db::{
2     assists::{AssistId, AssistKind},
3     base_db::AnchoredPathBuf,
4 };
5 use syntax::{ast, AstNode};
6
7 use crate::{
8     assist_context::{AssistContext, Assists},
9     utils::trimmed_text_range,
10 };
11
12 // Assist: move_to_mod_rs
13 //
14 // Moves xxx.rs to xxx/mod.rs.
15 //
16 // ```
17 // //- /main.rs
18 // mod a;
19 // //- /a.rs
20 // $0fn t() {}$0
21 // ```
22 // ->
23 // ```
24 // fn t() {}
25 // ```
26 pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
27     let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
28     let module = ctx.sema.to_module_def(ctx.file_id())?;
29     // Enable this assist if the user select all "meaningful" content in the source file
30     let trimmed_selected_range = trimmed_text_range(&source_file, ctx.selection_trimmed());
31     let trimmed_file_range = trimmed_text_range(&source_file, source_file.syntax().text_range());
32     if module.is_mod_rs(ctx.db()) {
33         cov_mark::hit!(already_mod_rs);
34         return None;
35     }
36     if trimmed_selected_range != trimmed_file_range {
37         cov_mark::hit!(not_all_selected);
38         return None;
39     }
40
41     let target = source_file.syntax().text_range();
42     let module_name = module.name(ctx.db())?.to_string();
43     let path = format!("./{module_name}/mod.rs");
44     let dst = AnchoredPathBuf { anchor: ctx.file_id(), path };
45     acc.add(
46         AssistId("move_to_mod_rs", AssistKind::Refactor),
47         format!("Convert {module_name}.rs to {module_name}/mod.rs"),
48         target,
49         |builder| {
50             builder.move_file(ctx.file_id(), dst);
51         },
52     )
53 }
54
55 #[cfg(test)]
56 mod tests {
57     use crate::tests::{check_assist, check_assist_not_applicable};
58
59     use super::*;
60
61     #[test]
62     fn trivial() {
63         check_assist(
64             move_to_mod_rs,
65             r#"
66 //- /main.rs
67 mod a;
68 //- /a.rs
69 $0fn t() {}
70 $0"#,
71             r#"
72 //- /a/mod.rs
73 fn t() {}
74 "#,
75         );
76     }
77
78     #[test]
79     fn must_select_all_file() {
80         cov_mark::check!(not_all_selected);
81         check_assist_not_applicable(
82             move_to_mod_rs,
83             r#"
84 //- /main.rs
85 mod a;
86 //- /a.rs
87 fn t() {}$0
88 "#,
89         );
90         cov_mark::check!(not_all_selected);
91         check_assist_not_applicable(
92             move_to_mod_rs,
93             r#"
94 //- /main.rs
95 mod a;
96 //- /a.rs
97 $0fn$0 t() {}
98 "#,
99         );
100     }
101
102     #[test]
103     fn cannot_promote_mod_rs() {
104         cov_mark::check!(already_mod_rs);
105         check_assist_not_applicable(
106             move_to_mod_rs,
107             r#"//- /main.rs
108 mod a;
109 //- /a/mod.rs
110 $0fn t() {}$0
111 "#,
112         );
113     }
114
115     #[test]
116     fn cannot_promote_main_and_lib_rs() {
117         check_assist_not_applicable(
118             move_to_mod_rs,
119             r#"//- /main.rs
120 $0fn t() {}$0
121 "#,
122         );
123         check_assist_not_applicable(
124             move_to_mod_rs,
125             r#"//- /lib.rs
126 $0fn t() {}$0
127 "#,
128         );
129     }
130
131     #[test]
132     fn works_in_mod() {
133         // note: /a/b.rs remains untouched
134         check_assist(
135             move_to_mod_rs,
136             r#"//- /main.rs
137 mod a;
138 //- /a.rs
139 $0mod b;
140 fn t() {}$0
141 //- /a/b.rs
142 fn t1() {}
143 "#,
144             r#"
145 //- /a/mod.rs
146 mod b;
147 fn t() {}
148 "#,
149         );
150     }
151 }