Skip to content

Commit ea0c124

Browse files
committed
Rerail split_import API onto AST
The code is more verbose and less efficient now, but should be reusable in add_import context as well
1 parent d75577f commit ea0c124

File tree

3 files changed

+72
-19
lines changed

3 files changed

+72
-19
lines changed

crates/ra_assists/src/handlers/split_import.rs

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
use std::iter::successors;
1+
use std::iter::{once, successors};
22

3-
use ra_syntax::{ast, AstNode, TextUnit, T};
3+
use ra_syntax::{
4+
ast::{self, make},
5+
AstNode, T,
6+
};
47

58
use crate::{Assist, AssistCtx, AssistId};
69

@@ -17,39 +20,50 @@ use crate::{Assist, AssistCtx, AssistId};
1720
// ```
1821
pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
1922
let colon_colon = ctx.find_token_at_offset(T![::])?;
20-
let path = ast::Path::cast(colon_colon.parent())?;
21-
let top_path = successors(Some(path), |it| it.parent_path()).last()?;
23+
let path = ast::Path::cast(colon_colon.parent())?.qualifier()?;
24+
let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?;
2225

23-
let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast);
24-
if use_tree.is_none() {
25-
return None;
26-
}
26+
let use_tree = top_path.syntax().ancestors().find_map(ast::UseTree::cast)?;
2727

28-
let l_curly = colon_colon.text_range().end();
29-
let r_curly = match top_path.syntax().parent().and_then(ast::UseTree::cast) {
30-
Some(tree) => tree.syntax().text_range().end(),
31-
None => top_path.syntax().text_range().end(),
32-
};
28+
let new_tree = split_use_tree_prefix(&use_tree, &path)?;
29+
let cursor = ctx.frange.range.start();
3330

3431
ctx.add_assist(AssistId("split_import"), "Split import", |edit| {
3532
edit.target(colon_colon.text_range());
36-
edit.insert(l_curly, "{");
37-
edit.insert(r_curly, "}");
38-
edit.set_cursor(l_curly + TextUnit::of_str("{"));
33+
edit.replace_ast(use_tree, new_tree);
34+
edit.set_cursor(cursor);
3935
})
4036
}
4137

38+
fn split_use_tree_prefix(use_tree: &ast::UseTree, prefix: &ast::Path) -> Option<ast::UseTree> {
39+
let suffix = split_path_prefix(&prefix)?;
40+
let use_tree = make::use_tree(suffix.clone(), use_tree.use_tree_list(), use_tree.alias());
41+
let nested = make::use_tree_list(once(use_tree));
42+
let res = make::use_tree(prefix.clone(), Some(nested), None);
43+
Some(res)
44+
}
45+
46+
fn split_path_prefix(prefix: &ast::Path) -> Option<ast::Path> {
47+
let parent = prefix.parent_path()?;
48+
let mut res = make::path_unqualified(parent.segment()?);
49+
for p in successors(parent.parent_path(), |it| it.parent_path()) {
50+
res = make::path_qualified(res, p.segment()?);
51+
}
52+
Some(res)
53+
}
54+
4255
#[cfg(test)]
4356
mod tests {
44-
use super::*;
4557
use crate::helpers::{check_assist, check_assist_target};
4658

59+
use super::*;
60+
4761
#[test]
4862
fn test_split_import() {
4963
check_assist(
5064
split_import,
5165
"use crate::<|>db::RootDatabase;",
52-
"use crate::{<|>db::RootDatabase};",
66+
"use crate::<|>{db::RootDatabase};",
5367
)
5468
}
5569

@@ -58,7 +72,7 @@ mod tests {
5872
check_assist(
5973
split_import,
6074
"use crate:<|>:db::{RootDatabase, FileSymbol}",
61-
"use crate::{<|>db::{RootDatabase, FileSymbol}}",
75+
"use crate:<|>:{db::{RootDatabase, FileSymbol}}",
6276
)
6377
}
6478

crates/ra_syntax/src/ast/edit.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,24 @@ impl ast::UseItem {
259259
}
260260
}
261261

262+
impl ast::UseTree {
263+
#[must_use]
264+
pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
265+
if let Some(old) = self.path() {
266+
return replace_descendants(self, iter::once((old, path)));
267+
}
268+
self.clone()
269+
}
270+
271+
#[must_use]
272+
pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
273+
if let Some(old) = self.use_tree_list() {
274+
return replace_descendants(self, iter::once((old, use_tree_list)));
275+
}
276+
self.clone()
277+
}
278+
}
279+
262280
#[must_use]
263281
pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
264282
N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap()

crates/ra_syntax/src/ast/make.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,27 @@ fn path_from_text(text: &str) -> ast::Path {
2525
ast_from_text(text)
2626
}
2727

28+
pub fn use_tree(
29+
path: ast::Path,
30+
use_tree_list: Option<ast::UseTreeList>,
31+
alias: Option<ast::Alias>,
32+
) -> ast::UseTree {
33+
let mut buf = "use ".to_string();
34+
buf += &path.syntax().to_string();
35+
if let Some(use_tree_list) = use_tree_list {
36+
buf += &format!("::{}", use_tree_list.syntax());
37+
}
38+
if let Some(alias) = alias {
39+
buf += &format!(" {}", alias.syntax());
40+
}
41+
ast_from_text(&buf)
42+
}
43+
44+
pub fn use_tree_list(use_trees: impl IntoIterator<Item = ast::UseTree>) -> ast::UseTreeList {
45+
let use_trees = use_trees.into_iter().map(|it| it.syntax().clone()).join(", ");
46+
ast_from_text(&format!("use {{{}}};", use_trees))
47+
}
48+
2849
pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordField {
2950
return match expr {
3051
Some(expr) => from_text(&format!("{}: {}", name.syntax(), expr.syntax())),

0 commit comments

Comments
 (0)