ide_assists/handlers/
move_from_mod_rs.rs

1use ide_db::{assists::AssistId, base_db::AnchoredPathBuf};
2use syntax::{AstNode, ToSmolStr, ast};
3
4use crate::{
5    assist_context::{AssistContext, Assists},
6    utils::trimmed_text_range,
7};
8
9// Assist: move_from_mod_rs
10//
11// Moves xxx/mod.rs to xxx.rs.
12//
13// ```
14// //- /main.rs
15// mod a;
16// //- /a/mod.rs
17// $0fn t() {}$0
18// ```
19// ->
20// ```
21// fn t() {}
22// ```
23pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
24    let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
25    let module = ctx.sema.file_to_module_def(ctx.vfs_file_id())?;
26    // Enable this assist if the user select all "meaningful" content in the source file
27    let trimmed_selected_range = trimmed_text_range(&source_file, ctx.selection_trimmed());
28    let trimmed_file_range = trimmed_text_range(&source_file, source_file.syntax().text_range());
29    if !module.is_mod_rs(ctx.db()) {
30        cov_mark::hit!(not_mod_rs);
31        return None;
32    }
33    if trimmed_selected_range != trimmed_file_range {
34        cov_mark::hit!(not_all_selected);
35        return None;
36    }
37
38    let target = source_file.syntax().text_range();
39    let module_name = module.name(ctx.db())?.as_str().to_smolstr();
40    let path = format!("../{module_name}.rs");
41    let dst = AnchoredPathBuf { anchor: ctx.vfs_file_id(), path };
42    acc.add(
43        AssistId::refactor("move_from_mod_rs"),
44        format!("Convert {module_name}/mod.rs to {module_name}.rs"),
45        target,
46        |builder| {
47            builder.move_file(ctx.vfs_file_id(), dst);
48        },
49    )
50}
51
52#[cfg(test)]
53mod tests {
54    use crate::tests::{check_assist, check_assist_not_applicable};
55
56    use super::*;
57
58    #[test]
59    fn trivial() {
60        check_assist(
61            move_from_mod_rs,
62            r#"
63//- /main.rs
64mod a;
65//- /a/mod.rs
66$0fn t() {}
67$0"#,
68            r#"
69//- /a.rs
70fn t() {}
71"#,
72        );
73    }
74
75    #[test]
76    fn must_select_all_file() {
77        cov_mark::check!(not_all_selected);
78        check_assist_not_applicable(
79            move_from_mod_rs,
80            r#"
81//- /main.rs
82mod a;
83//- /a/mod.rs
84fn t() {}$0
85"#,
86        );
87        cov_mark::check!(not_all_selected);
88        check_assist_not_applicable(
89            move_from_mod_rs,
90            r#"
91//- /main.rs
92mod a;
93//- /a/mod.rs
94$0fn$0 t() {}
95"#,
96        );
97    }
98
99    #[test]
100    fn cannot_move_not_mod_rs() {
101        cov_mark::check!(not_mod_rs);
102        check_assist_not_applicable(
103            move_from_mod_rs,
104            r#"//- /main.rs
105mod a;
106//- /a.rs
107$0fn t() {}$0
108"#,
109        );
110    }
111
112    #[test]
113    fn cannot_downgrade_main_and_lib_rs() {
114        check_assist_not_applicable(
115            move_from_mod_rs,
116            r#"//- /main.rs
117$0fn t() {}$0
118"#,
119        );
120        check_assist_not_applicable(
121            move_from_mod_rs,
122            r#"//- /lib.rs
123$0fn t() {}$0
124"#,
125        );
126    }
127}