Merge #10211
10211: assists: Promote module to folder r=jonas-schievink a=longfangsong Close part of #10143. This PR adds a assist to promote module to directory, which means make a .rs file module into a directory style module with the same name. ![未命名(1)](https://user-images.githubusercontent.com/13777628/132958377-14555d6f-a64a-4b9b-9154-90a3b86fd685.gif) Co-authored-by: longfangsong <longfangsong@icloud.com>
This commit is contained in:
commit
d401f2a062
@ -294,6 +294,10 @@ pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into<St
|
||||
let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() };
|
||||
self.source_change.push_file_system_edit(file_system_edit);
|
||||
}
|
||||
pub(crate) fn move_file(&mut self, src: FileId, dst: AnchoredPathBuf) {
|
||||
let file_system_edit = FileSystemEdit::MoveFile { src, dst };
|
||||
self.source_change.push_file_system_edit(file_system_edit);
|
||||
}
|
||||
|
||||
fn finish(mut self) -> SourceChange {
|
||||
self.commit();
|
||||
|
180
crates/ide_assists/src/handlers/move_to_mod_rs.rs
Normal file
180
crates/ide_assists/src/handlers/move_to_mod_rs.rs
Normal file
@ -0,0 +1,180 @@
|
||||
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};
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
// Assist: move_to_mod_rs
|
||||
//
|
||||
// Moves xxx.rs to xxx/mod.rs.
|
||||
//
|
||||
// ```
|
||||
// //- /main.rs
|
||||
// mod a;
|
||||
// //- /a.rs
|
||||
// $0fn t() {}$0
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// fn t() {}
|
||||
// ```
|
||||
pub(crate) fn move_to_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!(already_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!("./{}/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),
|
||||
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_to_mod_rs,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod a;
|
||||
//- /a.rs
|
||||
$0fn t() {}
|
||||
$0"#,
|
||||
r#"
|
||||
//- /a/mod.rs
|
||||
fn t() {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn must_select_all_file() {
|
||||
cov_mark::check!(not_all_selected);
|
||||
check_assist_not_applicable(
|
||||
move_to_mod_rs,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod a;
|
||||
//- /a.rs
|
||||
fn t() {}$0
|
||||
"#,
|
||||
);
|
||||
cov_mark::check!(not_all_selected);
|
||||
check_assist_not_applicable(
|
||||
move_to_mod_rs,
|
||||
r#"
|
||||
//- /main.rs
|
||||
mod a;
|
||||
//- /a.rs
|
||||
$0fn$0 t() {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_promote_mod_rs() {
|
||||
cov_mark::check!(already_mod_rs);
|
||||
check_assist_not_applicable(
|
||||
move_to_mod_rs,
|
||||
r#"//- /main.rs
|
||||
mod a;
|
||||
//- /a/mod.rs
|
||||
$0fn t() {}$0
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cannot_promote_main_and_lib_rs() {
|
||||
check_assist_not_applicable(
|
||||
move_to_mod_rs,
|
||||
r#"//- /main.rs
|
||||
$0fn t() {}$0
|
||||
"#,
|
||||
);
|
||||
check_assist_not_applicable(
|
||||
move_to_mod_rs,
|
||||
r#"//- /lib.rs
|
||||
$0fn t() {}$0
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_in_mod() {
|
||||
// note: /a/b.rs remains untouched
|
||||
check_assist(
|
||||
move_to_mod_rs,
|
||||
r#"//- /main.rs
|
||||
mod a;
|
||||
//- /a.rs
|
||||
$0mod b;
|
||||
fn t() {}$0
|
||||
//- /a/b.rs
|
||||
fn t1() {}
|
||||
"#,
|
||||
r#"
|
||||
//- /a/mod.rs
|
||||
mod b;
|
||||
fn t() {}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
@ -153,6 +153,7 @@ mod handlers {
|
||||
mod move_bounds;
|
||||
mod move_guard;
|
||||
mod move_module_to_file;
|
||||
mod move_to_mod_rs;
|
||||
mod pull_assignment_up;
|
||||
mod qualify_path;
|
||||
mod raw_string;
|
||||
@ -226,6 +227,7 @@ pub(crate) fn all() -> &'static [Handler] {
|
||||
move_guard::move_arm_cond_to_match_guard,
|
||||
move_guard::move_guard_to_arm_body,
|
||||
move_module_to_file::move_module_to_file,
|
||||
move_to_mod_rs::move_to_mod_rs,
|
||||
pull_assignment_up::pull_assignment_up,
|
||||
qualify_path::qualify_path,
|
||||
raw_string::add_hash,
|
||||
|
@ -142,7 +142,6 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
|
||||
(Some(assist), ExpectedResult::After(after)) => {
|
||||
let source_change =
|
||||
assist.source_change.expect("Assist did not contain any source changes");
|
||||
assert!(!source_change.source_file_edits.is_empty());
|
||||
let skip_header = source_change.source_file_edits.len() == 1
|
||||
&& source_change.file_system_edits.len() == 0;
|
||||
|
||||
@ -160,15 +159,19 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label:
|
||||
}
|
||||
|
||||
for file_system_edit in source_change.file_system_edits {
|
||||
if let FileSystemEdit::CreateFile { dst, initial_contents } = file_system_edit {
|
||||
let (dst, contents) = match file_system_edit {
|
||||
FileSystemEdit::CreateFile { dst, initial_contents } => (dst, initial_contents),
|
||||
FileSystemEdit::MoveFile { src, dst } => {
|
||||
(dst, db.file_text(src).as_ref().to_owned())
|
||||
}
|
||||
};
|
||||
let sr = db.file_source_root(dst.anchor);
|
||||
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..]);
|
||||
format_to!(buf, "//- {}\n", created_file_path);
|
||||
buf.push_str(&initial_contents);
|
||||
}
|
||||
buf.push_str(&contents);
|
||||
}
|
||||
|
||||
assert_eq_text!(after, &buf);
|
||||
|
@ -1303,6 +1303,22 @@ fn t() {}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_move_to_mod_rs() {
|
||||
check_doc_test(
|
||||
"move_to_mod_rs",
|
||||
r#####"
|
||||
//- /main.rs
|
||||
mod a;
|
||||
//- /a.rs
|
||||
$0fn t() {}$0
|
||||
"#####,
|
||||
r#####"
|
||||
fn t() {}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_pull_assignment_up() {
|
||||
check_doc_test(
|
||||
|
Loading…
Reference in New Issue
Block a user