ide_assists/handlers/
move_from_mod_rs.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use ide_db::{assists::AssistId, base_db::AnchoredPathBuf};
use syntax::{AstNode, ToSmolStr, ast};

use crate::{
    assist_context::{AssistContext, Assists},
    utils::trimmed_text_range,
};

// Assist: move_from_mod_rs
//
// Moves xxx/mod.rs to xxx.rs.
//
// ```
// //- /main.rs
// mod a;
// //- /a/mod.rs
// $0fn t() {}$0
// ```
// ->
// ```
// fn t() {}
// ```
pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let source_file = ctx.find_node_at_offset::<ast::SourceFile>()?;
    let module = ctx.sema.file_to_module_def(ctx.file_id())?;
    // Enable this assist if the user select all "meaningful" content in the source file
    let trimmed_selected_range = trimmed_text_range(&source_file, ctx.selection_trimmed());
    let trimmed_file_range = trimmed_text_range(&source_file, source_file.syntax().text_range());
    if !module.is_mod_rs(ctx.db()) {
        cov_mark::hit!(not_mod_rs);
        return None;
    }
    if trimmed_selected_range != trimmed_file_range {
        cov_mark::hit!(not_all_selected);
        return None;
    }

    let target = source_file.syntax().text_range();
    let module_name = module.name(ctx.db())?.as_str().to_smolstr();
    let path = format!("../{module_name}.rs");
    let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
    acc.add(
        AssistId::refactor("move_from_mod_rs"),
        format!("Convert {module_name}/mod.rs to {module_name}.rs"),
        target,
        |builder| {
            builder.move_file(ctx.file_id(), dst);
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn trivial() {
        check_assist(
            move_from_mod_rs,
            r#"
//- /main.rs
mod a;
//- /a/mod.rs
$0fn t() {}
$0"#,
            r#"
//- /a.rs
fn t() {}
"#,
        );
    }

    #[test]
    fn must_select_all_file() {
        cov_mark::check!(not_all_selected);
        check_assist_not_applicable(
            move_from_mod_rs,
            r#"
//- /main.rs
mod a;
//- /a/mod.rs
fn t() {}$0
"#,
        );
        cov_mark::check!(not_all_selected);
        check_assist_not_applicable(
            move_from_mod_rs,
            r#"
//- /main.rs
mod a;
//- /a/mod.rs
$0fn$0 t() {}
"#,
        );
    }

    #[test]
    fn cannot_move_not_mod_rs() {
        cov_mark::check!(not_mod_rs);
        check_assist_not_applicable(
            move_from_mod_rs,
            r#"//- /main.rs
mod a;
//- /a.rs
$0fn t() {}$0
"#,
        );
    }

    #[test]
    fn cannot_downgrade_main_and_lib_rs() {
        check_assist_not_applicable(
            move_from_mod_rs,
            r#"//- /main.rs
$0fn t() {}$0
"#,
        );
        check_assist_not_applicable(
            move_from_mod_rs,
            r#"//- /lib.rs
$0fn t() {}$0
"#,
        );
    }
}