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