about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlongfangsong <longfangsong@icloud.com>2021-09-28 10:20:29 +0800
committerlongfangsong <longfangsong@icloud.com>2021-09-28 10:20:29 +0800
commit0049b5b0bc83c99f304eac9ce0dfba1bb02968a8 (patch)
tree7e32d503812ce9b9f9f3d61c2fb01859bb7d7dc3
parent533ca584c31a251bc47f978b55df9b69058dabba (diff)
downloadrust-0049b5b0bc83c99f304eac9ce0dfba1bb02968a8.tar.gz
rust-0049b5b0bc83c99f304eac9ce0dfba1bb02968a8.zip
initial commit
-rw-r--r--crates/ide_assists/src/handlers/move_from_mod_rs.rs131
-rw-r--r--crates/ide_assists/src/handlers/move_to_mod_rs.rs32
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs16
-rw-r--r--crates/ide_assists/src/utils.rs32
-rw-r--r--crates/vfs/src/vfs_path.rs3
7 files changed, 184 insertions, 34 deletions
diff --git a/crates/ide_assists/src/handlers/move_from_mod_rs.rs b/crates/ide_assists/src/handlers/move_from_mod_rs.rs
new file mode 100644
index 00000000000..2b1ee58d53b
--- /dev/null
+++ b/crates/ide_assists/src/handlers/move_from_mod_rs.rs
@@ -0,0 +1,131 @@
+use ide_db::{
+    assists::{AssistId, AssistKind},
+    base_db::AnchoredPathBuf,
+};
+use syntax::{ast, AstNode, TextRange};
+
+use crate::assist_context::{AssistContext, Assists};
+use crate::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.to_module_def(ctx.frange.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.frange.range);
+    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 = TextRange::new(
+        source_file.syntax().text_range().start(),
+        source_file.syntax().text_range().end(),
+    );
+    let module_name = module.name(ctx.db())?.to_string();
+    let path = format!("../{}.rs", module_name);
+    let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path };
+    acc.add(
+        AssistId("move_from_mod_rs", AssistKind::Refactor),
+        format!("Turn {}/mod.rs to {}.rs", module_name, module_name),
+        target,
+        |builder| {
+            builder.move_file(ctx.frange.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
+"#,
+        );
+    }
+}
diff --git a/crates/ide_assists/src/handlers/move_to_mod_rs.rs b/crates/ide_assists/src/handlers/move_to_mod_rs.rs
index 9b060bb710f..3f8340b7296 100644
--- a/crates/ide_assists/src/handlers/move_to_mod_rs.rs
+++ b/crates/ide_assists/src/handlers/move_to_mod_rs.rs
@@ -2,38 +2,10 @@ use ide_db::{
     assists::{AssistId, AssistKind},
     base_db::AnchoredPathBuf,
 };
-use syntax::{
-    ast::{self, Whitespace},
-    AstNode, AstToken, SourceFile, TextRange, TextSize,
-};
+use syntax::{ast, AstNode, TextRange};
 
 use crate::assist_context::{AssistContext, Assists};
-
-/// Trim(remove leading and trailing whitespace) `initial_range` in `source_file`, return the trimmed range.
-fn trimmed_text_range(source_file: &SourceFile, initial_range: TextRange) -> TextRange {
-    let mut trimmed_range = initial_range;
-    while source_file
-        .syntax()
-        .token_at_offset(trimmed_range.start())
-        .find_map(Whitespace::cast)
-        .is_some()
-        && trimmed_range.start() < trimmed_range.end()
-    {
-        let start = trimmed_range.start() + TextSize::from(1);
-        trimmed_range = TextRange::new(start, trimmed_range.end());
-    }
-    while source_file
-        .syntax()
-        .token_at_offset(trimmed_range.end())
-        .find_map(Whitespace::cast)
-        .is_some()
-        && trimmed_range.start() < trimmed_range.end()
-    {
-        let end = trimmed_range.end() - TextSize::from(1);
-        trimmed_range = TextRange::new(trimmed_range.start(), end);
-    }
-    trimmed_range
-}
+use crate::utils::trimmed_text_range;
 
 // Assist: move_to_mod_rs
 //
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 255ff47b801..d41d06f2d97 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -154,6 +154,7 @@ mod handlers {
     mod move_guard;
     mod move_module_to_file;
     mod move_to_mod_rs;
+    mod move_from_mod_rs;
     mod pull_assignment_up;
     mod qualify_path;
     mod raw_string;
@@ -229,6 +230,7 @@ mod handlers {
             move_guard::move_guard_to_arm_body,
             move_module_to_file::move_module_to_file,
             move_to_mod_rs::move_to_mod_rs,
+            move_from_mod_rs::move_from_mod_rs,
             pull_assignment_up::pull_assignment_up,
             qualify_path::qualify_path,
             raw_string::add_hash,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index e211b09987b..7f3c93d7403 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -169,7 +169,7 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
                 let sr = db.source_root(sr);
                 let mut base = sr.path_for_file(&dst.anchor).unwrap().clone();
                 base.pop();
-                let created_file_path = format!("{}{}", base.to_string(), &dst.path[1..]);
+                let created_file_path = base.join(&dst.path).unwrap();
                 format_to!(buf, "//- {}\n", created_file_path);
                 buf.push_str(&contents);
             }
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 87b3be34984..9ad7b6097a6 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -1297,6 +1297,22 @@ fn apply<T, U, F>(f: F, x: T) -> U where F: FnOnce(T) -> U {
 }
 
 #[test]
+fn doctest_move_from_mod_rs() {
+    check_doc_test(
+        "move_from_mod_rs",
+        r#####"
+//- /main.rs
+mod a;
+//- /a/mod.rs
+$0fn t() {}$0
+"#####,
+        r#####"
+fn t() {}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_move_guard_to_arm_body() {
     check_doc_test(
         "move_guard_to_arm_body",
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 9bc70bc3fe7..5273a3107b1 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -14,11 +14,11 @@ use syntax::{
         self,
         edit::{self, AstNodeEdit},
         edit_in_place::AttrsOwnerEdit,
-        make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds,
+        make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
     },
-    ted, AstNode, Direction, SmolStr,
+    ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
     SyntaxKind::*,
-    SyntaxNode, TextSize, T,
+    SyntaxNode, TextRange, TextSize, T,
 };
 
 use crate::assist_context::{AssistBuilder, AssistContext};
@@ -500,3 +500,29 @@ pub(crate) fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> {
         .filter(|f| f.name().is_some())
         .collect()
 }
+
+/// Trim(remove leading and trailing whitespace) `initial_range` in `source_file`, return the trimmed range.
+pub(crate) fn trimmed_text_range(source_file: &SourceFile, initial_range: TextRange) -> TextRange {
+    let mut trimmed_range = initial_range;
+    while source_file
+        .syntax()
+        .token_at_offset(trimmed_range.start())
+        .find_map(Whitespace::cast)
+        .is_some()
+        && trimmed_range.start() < trimmed_range.end()
+    {
+        let start = trimmed_range.start() + TextSize::from(1);
+        trimmed_range = TextRange::new(start, trimmed_range.end());
+    }
+    while source_file
+        .syntax()
+        .token_at_offset(trimmed_range.end())
+        .find_map(Whitespace::cast)
+        .is_some()
+        && trimmed_range.start() < trimmed_range.end()
+    {
+        let end = trimmed_range.end() - TextSize::from(1);
+        trimmed_range = TextRange::new(trimmed_range.start(), end);
+    }
+    trimmed_range
+}
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
index 0ad56e00d7c..9f704d6ebe5 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -359,6 +359,9 @@ impl VirtualPath {
             }
             path = &path["../".len()..]
         }
+        while path.starts_with("./") {
+            path = &path["./".len()..]
+        }
         res.0 = format!("{}/{}", res.0, path);
         Some(res)
     }