auto_import: struct variants for ImportAction

This commit is contained in:
Andrea Pretto 2019-02-08 22:12:49 +01:00
parent 1866fb2dda
commit ee9b0c89e6

View File

@ -140,30 +140,64 @@ fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool {
enum ImportAction<'a> {
Nothing,
// Add a brand new use statement.
AddNewUse(
Option<&'a SyntaxNode>, // anchor node
bool, // true if we want to add the new statement after the anchor
),
// In the following actions we keep track of how may segments matched,
// so we can choose the best action to take.
AddNewUse {
anchor: Option<&'a SyntaxNode>, // anchor node
add_after_anchor: bool,
},
// To split an existing use statement creating a nested import.
AddNestedImport(
usize,
&'a ast::Path, // the complete path we want to split
Option<&'a ast::PathSegment>, // the first segment of path we want to add into the new nested list
bool, // true if we want to add 'self' in addition to the segment
),
AddNestedImport {
// how may segments matched with the target path
common_segments: usize,
path_to_split: &'a ast::Path,
// the first segment of path_to_split we want to add into the new nested list
first_segment_to_split: Option<&'a ast::PathSegment>,
// Wether to add 'self' in addition to the target path
add_self: bool,
},
// To add the target path to an existing nested import tree list.
AddInTreeList(
usize,
&'a ast::UseTreeList,
bool, // true if we want to add 'self'
),
AddInTreeList {
common_segments: usize,
// The UseTreeList where to add the target path
tree_list: &'a ast::UseTreeList,
add_self: bool,
},
}
impl<'a> ImportAction<'a> {
fn add_new_use(anchor: Option<&'a SyntaxNode>, add_after_anchor: bool) -> Self {
ImportAction::AddNewUse {
anchor,
add_after_anchor,
}
}
fn add_nested_import(
common_segments: usize,
path_to_split: &'a ast::Path,
first_segment_to_split: Option<&'a ast::PathSegment>,
add_self: bool,
) -> Self {
ImportAction::AddNestedImport {
common_segments,
path_to_split,
first_segment_to_split,
add_self,
}
}
fn add_in_tree_list(
common_segments: usize,
tree_list: &'a ast::UseTreeList,
add_self: bool,
) -> Self {
ImportAction::AddInTreeList {
common_segments,
tree_list,
add_self,
}
}
fn better<'b>(left: &'b ImportAction<'a>, right: &'b ImportAction<'a>) -> &'b ImportAction<'a> {
if left.is_better(right) {
left
@ -175,13 +209,27 @@ impl<'a> ImportAction<'a> {
fn is_better(&self, other: &ImportAction) -> bool {
match (self, other) {
(ImportAction::Nothing, _) => true,
(ImportAction::AddInTreeList(..), ImportAction::Nothing) => false,
(ImportAction::AddNestedImport(n, ..), ImportAction::AddInTreeList(m, ..)) => n > m,
(ImportAction::AddInTreeList(n, ..), ImportAction::AddNestedImport(m, ..)) => n > m,
(ImportAction::AddInTreeList(..), _) => true,
(ImportAction::AddNestedImport(..), ImportAction::Nothing) => false,
(ImportAction::AddNestedImport(..), _) => true,
(ImportAction::AddNewUse(..), _) => false,
(ImportAction::AddInTreeList { .. }, ImportAction::Nothing) => false,
(
ImportAction::AddNestedImport {
common_segments: n, ..
},
ImportAction::AddInTreeList {
common_segments: m, ..
},
) => n > m,
(
ImportAction::AddInTreeList {
common_segments: n, ..
},
ImportAction::AddNestedImport {
common_segments: m, ..
},
) => n > m,
(ImportAction::AddInTreeList { .. }, _) => true,
(ImportAction::AddNestedImport { .. }, ImportAction::Nothing) => false,
(ImportAction::AddNestedImport { .. }, _) => true,
(ImportAction::AddNewUse { .. }, _) => false,
}
}
}
@ -205,7 +253,7 @@ fn walk_use_tree_for_best_action<'a>(
Some(path) => path,
None => {
// If the use item don't have a path, it means it's broken (syntax error)
return ImportAction::AddNewUse(
return ImportAction::add_new_use(
current_use_tree
.syntax()
.ancestors()
@ -231,7 +279,7 @@ fn walk_use_tree_for_best_action<'a>(
let right = current_path_segments.split_at(prev_len).1;
let common = compare_path_segments(left, right);
let mut action = match common {
0 => ImportAction::AddNewUse(
0 => ImportAction::add_new_use(
// e.g: target is std::fmt and we can have
// use foo::bar
// We add a brand new use statement
@ -259,7 +307,7 @@ fn walk_use_tree_for_best_action<'a>(
if has_self {
ImportAction::Nothing
} else {
ImportAction::AddInTreeList(current_path_segments.len(), list, true)
ImportAction::add_in_tree_list(current_path_segments.len(), list, true)
}
} else {
// Case 1
@ -271,7 +319,7 @@ fn walk_use_tree_for_best_action<'a>(
// use std::io;
// We need to split.
let segments_to_split = current_path_segments.split_at(prev_len + common).1;
ImportAction::AddNestedImport(
ImportAction::add_nested_import(
prev_len + common,
path,
Some(segments_to_split[0]),
@ -284,7 +332,7 @@ fn walk_use_tree_for_best_action<'a>(
// 2- use std::{ ... };
// fallback action
let mut better_action = ImportAction::AddNewUse(
let mut better_action = ImportAction::add_new_use(
current_use_tree
.syntax()
.ancestors()
@ -306,7 +354,7 @@ fn walk_use_tree_for_best_action<'a>(
}
} else {
// Case 1, split
better_action = ImportAction::AddNestedImport(prev_len + common, path, None, true)
better_action = ImportAction::add_nested_import(prev_len + common, path, None, true)
}
better_action
}
@ -314,7 +362,12 @@ fn walk_use_tree_for_best_action<'a>(
// e.g: target is std::fmt and we can have
// use std::fmt::Debug;
let segments_to_split = current_path_segments.split_at(prev_len + common).1;
ImportAction::AddNestedImport(prev_len + common, path, Some(segments_to_split[0]), true)
ImportAction::add_nested_import(
prev_len + common,
path,
Some(segments_to_split[0]),
true,
)
}
_ => unreachable!(),
};
@ -322,8 +375,8 @@ fn walk_use_tree_for_best_action<'a>(
// If we are inside a UseTreeList adding a use statement become adding to the existing
// tree list.
action = match (current_parent_use_tree_list, action) {
(Some(use_tree_list), ImportAction::AddNewUse(..)) => {
ImportAction::AddInTreeList(prev_len, use_tree_list, false)
(Some(use_tree_list), ImportAction::AddNewUse { .. }) => {
ImportAction::add_in_tree_list(prev_len, use_tree_list, false)
}
(_, _) => action,
};
@ -361,26 +414,36 @@ fn best_action_for_target<'b, 'a: 'b>(
.map(AstNode::syntax)
.or(Some(path.syntax()));
return ImportAction::AddNewUse(anchor, false);
return ImportAction::add_new_use(anchor, false);
}
}
}
fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) {
match action {
ImportAction::AddNewUse(anchor, after) => {
make_assist_add_new_use(anchor, *after, target, edit)
}
ImportAction::AddInTreeList(n, tree_list_node, add_self) => {
ImportAction::AddNewUse {
anchor,
add_after_anchor,
} => make_assist_add_new_use(anchor, *add_after_anchor, target, edit),
ImportAction::AddInTreeList {
common_segments,
tree_list,
add_self,
} => {
// We know that the fist n segments already exists in the use statement we want
// to modify, so we want to add only the last target.len() - n segments.
let segments_to_add = target.split_at(*n).1;
make_assist_add_in_tree_list(tree_list_node, segments_to_add, *add_self, edit)
let segments_to_add = target.split_at(*common_segments).1;
make_assist_add_in_tree_list(tree_list, segments_to_add, *add_self, edit)
}
ImportAction::AddNestedImport(n, path, first_segment_to_split, add_self) => {
let segments_to_add = target.split_at(*n).1;
ImportAction::AddNestedImport {
common_segments,
path_to_split,
first_segment_to_split,
add_self,
} => {
let segments_to_add = target.split_at(*common_segments).1;
make_assist_add_nested_import(
path,
path_to_split,
first_segment_to_split,
segments_to_add,
*add_self,