Implement import-granularity guessing

This commit is contained in:
Lukas Tobias Wirth 2021-05-18 20:10:39 +02:00
parent 64f7072c25
commit b8a99692d1

View File

@ -4,7 +4,7 @@
use hir::Semantics; use hir::Semantics;
use syntax::{ use syntax::{
algo, algo,
ast::{self, make, AstNode, PathSegmentKind}, ast::{self, make, AstNode, ModuleItemOwner, PathSegmentKind},
ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken, ted, AstToken, Direction, NodeOrToken, SyntaxNode, SyntaxToken,
}; };
@ -88,15 +88,46 @@ pub fn clone_for_update(&self) -> Self {
ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()), ImportScope::Module(item_list) => ImportScope::Module(item_list.clone_for_update()),
} }
} }
fn guess_merge_behavior_from_scope(&self) -> Option<MergeBehavior> {
let use_stmt = |item| match item {
ast::Item::Use(use_) => use_.use_tree(),
_ => None,
};
let use_stmts = match self {
ImportScope::File(f) => f.items(),
ImportScope::Module(m) => m.items(),
}
.filter_map(use_stmt);
let mut res = None;
for tree in use_stmts {
if let Some(list) = tree.use_tree_list() {
if list.use_trees().any(|tree| tree.use_tree_list().is_some()) {
// double nested tree list, can only be a crate style import at this point
return Some(MergeBehavior::Crate);
}
// has to be at least a module style based import, might be crate style tho so look further
res = Some(MergeBehavior::Module);
}
}
res
}
} }
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur. /// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) { pub fn insert_use<'a>(scope: &ImportScope, path: ast::Path, cfg: InsertUseConfig) {
let _p = profile::span("insert_use"); let _p = profile::span("insert_use");
let mb = match cfg.granularity {
ImportGranularity::Preserve => scope.guess_merge_behavior_from_scope(),
ImportGranularity::Crate => Some(MergeBehavior::Crate),
ImportGranularity::Module => Some(MergeBehavior::Module),
ImportGranularity::Item => None,
};
let use_item = let use_item =
make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update(); make::use_(None, make::use_tree(path.clone(), None, None, false)).clone_for_update();
// merge into existing imports if possible // merge into existing imports if possible
if let Some(mb) = cfg.granularity.merge_behavior() { if let Some(mb) = mb {
for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) {
if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) {
ted::replace(existing_use.syntax(), merged.syntax()); ted::replace(existing_use.syntax(), merged.syntax());