From 414576fb3059c8431f32a5bbe8fa117d4f3d83b7 Mon Sep 17 00:00:00 2001 From: Daiki Ihara Date: Tue, 8 Dec 2020 01:17:54 +0900 Subject: Add extract_module_to_file assist --- .../assists/src/handlers/extract_module_to_file.rs | 170 +++++++++++++++++++++ crates/assists/src/lib.rs | 2 + crates/assists/src/tests/generated.rs | 15 ++ 3 files changed, 187 insertions(+) create mode 100644 crates/assists/src/handlers/extract_module_to_file.rs diff --git a/crates/assists/src/handlers/extract_module_to_file.rs b/crates/assists/src/handlers/extract_module_to_file.rs new file mode 100644 index 000000000..5fc190fa6 --- /dev/null +++ b/crates/assists/src/handlers/extract_module_to_file.rs @@ -0,0 +1,170 @@ +use ast::edit::IndentLevel; +use ide_db::base_db::{AnchoredPathBuf, SourceDatabaseExt}; +use syntax::{ + ast::{self, edit::AstNodeEdit, NameOwner}, + AstNode, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Assist: extract_module_to_file +// +// This assist extract module to file. +// +// ``` +// mod foo {<|> +// fn t() {} +// } +// ``` +// -> +// ``` +// mod foo; +// ``` +pub(crate) fn extract_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let assist_id = AssistId("extract_module_to_file", AssistKind::RefactorExtract); + let assist_label = "Extract module to file"; + let db = ctx.db(); + let module_ast = ctx.find_node_at_offset::()?; + let module_items = module_ast.item_list()?; + let dedent_module_items_text = module_items.dedent(IndentLevel(1)).to_string(); + let module_name = module_ast.name()?; + let target = module_ast.syntax().text_range(); + let anchor_file_id = ctx.frange.file_id; + let sr = db.file_source_root(anchor_file_id); + let sr = db.source_root(sr); + let file_path = sr.path_for_file(&anchor_file_id)?; + let (file_name, file_ext) = file_path.name_and_extension()?; + acc.add(assist_id, assist_label, target, |builder| { + builder.replace(target, format!("mod {};", module_name)); + let path = if is_main_or_lib(file_name) { + format!("./{}.{}", module_name, file_ext.unwrap()) + } else { + format!("./{}/{}.{}", file_name, module_name, file_ext.unwrap()) + }; + let dst = AnchoredPathBuf { anchor: anchor_file_id, path }; + let contents = update_module_items_string(dedent_module_items_text); + builder.create_file(dst, contents); + }) +} +fn is_main_or_lib(file_name: &str) -> bool { + file_name == "main".to_string() || file_name == "lib".to_string() +} +fn update_module_items_string(items_str: String) -> String { + let mut items_string_lines: Vec<&str> = items_str.lines().collect(); + items_string_lines.pop(); // Delete last line + items_string_lines.reverse(); + items_string_lines.pop(); // Delete first line + items_string_lines.reverse(); + + let string = items_string_lines.join("\n"); + format!("{}", string) +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn extract_module_to_file_with_basic_module() { + check_assist( + extract_module_to_file, + r#" +//- /foo.rs crate:foo +mod tests {<|> + #[test] fn t() {} +} +"#, + r#" +//- /foo.rs +mod tests; +//- /foo/tests.rs +#[test] fn t() {}"#, + ) + } + + #[test] + fn extract_module_to_file_with_file_path() { + check_assist( + extract_module_to_file, + r#" +//- /src/foo.rs crate:foo +mod bar {<|> + fn f() { + + } +} +fn main() { + println!("Hello, world!"); +} +"#, + r#" +//- /src/foo.rs +mod bar; +fn main() { + println!("Hello, world!"); +} +//- /src/foo/bar.rs +fn f() { + +}"#, + ) + } + + #[test] + fn extract_module_to_file_with_main_filw() { + check_assist( + extract_module_to_file, + r#" +//- /main.rs +mod foo {<|> + fn f() { + + } +} +fn main() { + println!("Hello, world!"); +} +"#, + r#" +//- /main.rs +mod foo; +fn main() { + println!("Hello, world!"); +} +//- /foo.rs +fn f() { + +}"#, + ) + } + + #[test] + fn extract_module_to_file_with_lib_file() { + check_assist( + extract_module_to_file, + r#" +//- /lib.rs +mod foo {<|> + fn f() { + + } +} +fn main() { + println!("Hello, world!"); +} +"#, + r#" +//- /lib.rs +mod foo; +fn main() { + println!("Hello, world!"); +} +//- /foo.rs +fn f() { + +}"#, + ) + } +} diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 6e736ccb3..6b89b2d04 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs @@ -129,6 +129,7 @@ mod handlers { mod convert_integer_literal; mod early_return; mod expand_glob_import; + mod extract_module_to_file; mod extract_struct_from_enum_variant; mod extract_variable; mod fill_match_arms; @@ -179,6 +180,7 @@ mod handlers { convert_integer_literal::convert_integer_literal, early_return::convert_to_guarded_return, expand_glob_import::expand_glob_import, + extract_module_to_file::extract_module_to_file, extract_struct_from_enum_variant::extract_struct_from_enum_variant, extract_variable::extract_variable, fill_match_arms::fill_match_arms, diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index cc7c4a343..e9093ec53 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs @@ -235,6 +235,21 @@ fn qux(bar: Bar, baz: Baz) {} ) } +#[test] +fn doctest_extract_module_to_file() { + check_doc_test( + "extract_module_to_file", + r#####" +mod foo {<|> + fn t() {} +} +"#####, + r#####" +mod foo; +"#####, + ) +} + #[test] fn doctest_extract_struct_from_enum_variant() { check_doc_test( -- cgit v1.2.3