Unnamed repository; edit this file 'description' to name the repository.
Merge #10362
10362: feat: Convert a/mod.rs into a.rs r=Veykril a=longfangsong This is the reverse operation of #10211. ![demo](https://user-images.githubusercontent.com/13777628/134837717-074c23e9-1ca2-4207-b780-8443b2241272.gif) Close #10143. Co-authored-by: longfangsong <[email protected]> Co-authored-by: 龙方淞 <[email protected]>
bors[bot] 2021-09-28
parent 533ca58 · parent 7e3224f · commit dd21ad6
-rw-r--r--crates/ide_assists/src/handlers/move_from_mod_rs.rs130
-rw-r--r--crates/ide_assists/src/handlers/move_to_mod_rs.rs43
-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.rs1
7 files changed, 186 insertions, 40 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 0000000000..026f4aaf6c
--- /dev/null
+++ b/crates/ide_assists/src/handlers/move_from_mod_rs.rs
@@ -0,0 +1,130 @@
+use ide_db::{
+ assists::{AssistId, AssistKind},
+ base_db::AnchoredPathBuf,
+};
+use syntax::{ast, AstNode};
+
+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.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 = source_file.syntax().text_range();
+ 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!("Convert {}/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 9b060bb710..6f505de4cf 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,12 @@ use ide_db::{
assists::{AssistId, AssistKind},
base_db::AnchoredPathBuf,
};
-use syntax::{
- ast::{self, Whitespace},
- AstNode, AstToken, SourceFile, TextRange, TextSize,
-};
-
-use crate::assist_context::{AssistContext, Assists};
+use syntax::{ast, AstNode};
-/// 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::{
+ assist_context::{AssistContext, Assists},
+ utils::trimmed_text_range,
+};
// Assist: move_to_mod_rs
//
@@ -64,16 +38,13 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext) -> Option<(
return None;
}
- let target = TextRange::new(
- source_file.syntax().text_range().start(),
- source_file.syntax().text_range().end(),
- );
+ let target = source_file.syntax().text_range();
let module_name = module.name(ctx.db())?.to_string();
let path = format!("./{}/mod.rs", module_name);
let dst = AnchoredPathBuf { anchor: ctx.frange.file_id, path };
acc.add(
AssistId("move_to_mod_rs", AssistKind::Refactor),
- format!("Turn {}.rs to {}/mod.rs", module_name, module_name),
+ format!("Convert {}.rs to {}/mod.rs", module_name, module_name),
target,
|builder| {
builder.move_file(ctx.frange.file_id, dst);
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 255ff47b80..d41d06f2d9 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 e211b09987..7f3c93d740 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 87b3be3498..9ad7b6097a 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 9bc70bc3fe..5273a3107b 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 0ad56e00d7..ffd673573a 100644
--- a/crates/vfs/src/vfs_path.rs
+++ b/crates/vfs/src/vfs_path.rs
@@ -359,6 +359,7 @@ impl VirtualPath {
}
path = &path["../".len()..]
}
+ path = path.trim_start_matches("./");
res.0 = format!("{}/{}", res.0, path);
Some(res)
}