From 0049b5b0bc83c99f304eac9ce0dfba1bb02968a8 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Tue, 28 Sep 2021 10:20:29 +0800 Subject: [PATCH 1/4] initial commit --- .../src/handlers/move_from_mod_rs.rs | 131 ++++++++++++++++++ .../src/handlers/move_to_mod_rs.rs | 32 +---- crates/ide_assists/src/lib.rs | 2 + crates/ide_assists/src/tests.rs | 2 +- crates/ide_assists/src/tests/generated.rs | 16 +++ crates/ide_assists/src/utils.rs | 32 ++++- crates/vfs/src/vfs_path.rs | 3 + 7 files changed, 184 insertions(+), 34 deletions(-) create mode 100644 crates/ide_assists/src/handlers/move_from_mod_rs.rs 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 000000000000..2b1ee58d53bb --- /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::()?; + 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 9b060bb710f0..3f8340b72963 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 255ff47b8017..d41d06f2d971 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 e211b09987b0..7f3c93d74031 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 87b3be34984c..9ad7b6097a6f 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -1296,6 +1296,22 @@ fn apply(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( diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index 9bc70bc3fe72..5273a3107b19 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 { .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 0ad56e00d7cb..9f704d6ebe52 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) } From 8f5e8e0a13cb9987c789a244f9c31e60d148a3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E6=96=B9=E6=B7=9E?= Date: Tue, 28 Sep 2021 10:20:35 +0800 Subject: [PATCH 2/4] Update crates/ide_assists/src/handlers/move_from_mod_rs.rs Co-authored-by: Lukas Wirth --- crates/ide_assists/src/handlers/move_from_mod_rs.rs | 3 +-- 1 file changed, 1 insertion(+), 2 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 index 2b1ee58d53bb..28482b85f48b 100644 --- a/crates/ide_assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide_assists/src/handlers/move_from_mod_rs.rs @@ -4,8 +4,7 @@ use ide_db::{ }; use syntax::{ast, AstNode, TextRange}; -use crate::assist_context::{AssistContext, Assists}; -use crate::utils::trimmed_text_range; +use crate::{assist_context::{AssistContext, Assists}, utils::trimmed_text_range}; // Assist: move_from_mod_rs // From 51b1f7707b0742d22241562158c8a2cf22e684d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BE=99=E6=96=B9=E6=B7=9E?= Date: Tue, 28 Sep 2021 10:20:35 +0800 Subject: [PATCH 3/4] Update crates/vfs/src/vfs_path.rs Co-authored-by: Lukas Wirth --- crates/vfs/src/vfs_path.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs index 9f704d6ebe52..ffd673573a5e 100644 --- a/crates/vfs/src/vfs_path.rs +++ b/crates/vfs/src/vfs_path.rs @@ -359,9 +359,7 @@ impl VirtualPath { } path = &path["../".len()..] } - while path.starts_with("./") { - path = &path["./".len()..] - } + path = path.trim_start_matches("./"); res.0 = format!("{}/{}", res.0, path); Some(res) } From 7e3224f419d17c95ea4344e58d3e26688c77c6d7 Mon Sep 17 00:00:00 2001 From: longfangsong Date: Tue, 28 Sep 2021 10:20:35 +0800 Subject: [PATCH 4/4] Address comments --- .../ide_assists/src/handlers/move_from_mod_rs.rs | 14 +++++++------- crates/ide_assists/src/handlers/move_to_mod_rs.rs | 15 +++++++-------- 2 files changed, 14 insertions(+), 15 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 index 28482b85f48b..026f4aaf6c45 100644 --- a/crates/ide_assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide_assists/src/handlers/move_from_mod_rs.rs @@ -2,9 +2,12 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode, TextRange}; +use syntax::{ast, AstNode}; -use crate::{assist_context::{AssistContext, Assists}, utils::trimmed_text_range}; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::trimmed_text_range, +}; // Assist: move_from_mod_rs // @@ -35,16 +38,13 @@ pub(crate) fn move_from_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!("../{}.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), + format!("Convert {}/mod.rs to {}.rs", module_name, module_name), target, |builder| { builder.move_file(ctx.frange.file_id, dst); 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 3f8340b72963..6f505de4cf0c 100644 --- a/crates/ide_assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide_assists/src/handlers/move_to_mod_rs.rs @@ -2,10 +2,12 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode, TextRange}; +use syntax::{ast, AstNode}; -use crate::assist_context::{AssistContext, Assists}; -use crate::utils::trimmed_text_range; +use crate::{ + assist_context::{AssistContext, Assists}, + utils::trimmed_text_range, +}; // Assist: move_to_mod_rs // @@ -36,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);