Merge pull request #21618 from Shourya742/2026-02-10-migrate-destructure-tuple-binding-to-syntaxeditor
Migrate destructure tuple binding assist to syntaxEditor
This commit is contained in:
commit
49228dc09f
3 changed files with 181 additions and 95 deletions
|
|
@ -8,8 +8,8 @@ use ide_db::{
|
|||
use itertools::Itertools;
|
||||
use syntax::{
|
||||
T,
|
||||
ast::{self, AstNode, FieldExpr, HasName, IdentPat, make},
|
||||
ted,
|
||||
ast::{self, AstNode, FieldExpr, HasName, IdentPat, syntax_factory::SyntaxFactory},
|
||||
syntax_editor::{Position, SyntaxEditor},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -89,13 +89,20 @@ fn destructure_tuple_edit_impl(
|
|||
data: &TupleData,
|
||||
in_sub_pattern: bool,
|
||||
) {
|
||||
let assignment_edit = edit_tuple_assignment(ctx, edit, data, in_sub_pattern);
|
||||
let current_file_usages_edit = edit_tuple_usages(data, edit, ctx, in_sub_pattern);
|
||||
let mut syntax_editor = edit.make_editor(data.ident_pat.syntax());
|
||||
let syntax_factory = SyntaxFactory::with_mappings();
|
||||
|
||||
assignment_edit.apply();
|
||||
let assignment_edit =
|
||||
edit_tuple_assignment(ctx, edit, &mut syntax_editor, &syntax_factory, data, in_sub_pattern);
|
||||
let current_file_usages_edit = edit_tuple_usages(data, ctx, &syntax_factory, in_sub_pattern);
|
||||
|
||||
assignment_edit.apply(&mut syntax_editor, &syntax_factory);
|
||||
if let Some(usages_edit) = current_file_usages_edit {
|
||||
usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit))
|
||||
usages_edit.into_iter().for_each(|usage_edit| usage_edit.apply(edit, &mut syntax_editor))
|
||||
}
|
||||
|
||||
syntax_editor.add_mappings(syntax_factory.finish_with_mappings());
|
||||
edit.add_file_edits(ctx.vfs_file_id(), syntax_editor);
|
||||
}
|
||||
|
||||
fn collect_data(ident_pat: IdentPat, ctx: &AssistContext<'_>) -> Option<TupleData> {
|
||||
|
|
@ -165,11 +172,11 @@ struct TupleData {
|
|||
fn edit_tuple_assignment(
|
||||
ctx: &AssistContext<'_>,
|
||||
edit: &mut SourceChangeBuilder,
|
||||
editor: &mut SyntaxEditor,
|
||||
make: &SyntaxFactory,
|
||||
data: &TupleData,
|
||||
in_sub_pattern: bool,
|
||||
) -> AssignmentEdit {
|
||||
let ident_pat = edit.make_mut(data.ident_pat.clone());
|
||||
|
||||
let tuple_pat = {
|
||||
let original = &data.ident_pat;
|
||||
let is_ref = original.ref_token().is_some();
|
||||
|
|
@ -177,10 +184,11 @@ fn edit_tuple_assignment(
|
|||
let fields = data
|
||||
.field_names
|
||||
.iter()
|
||||
.map(|name| ast::Pat::from(make::ident_pat(is_ref, is_mut, make::name(name))));
|
||||
make::tuple_pat(fields).clone_for_update()
|
||||
.map(|name| ast::Pat::from(make.ident_pat(is_ref, is_mut, make.name(name))));
|
||||
make.tuple_pat(fields)
|
||||
};
|
||||
let is_shorthand_field = ident_pat
|
||||
let is_shorthand_field = data
|
||||
.ident_pat
|
||||
.name()
|
||||
.as_ref()
|
||||
.and_then(ast::RecordPatField::for_field_name)
|
||||
|
|
@ -189,14 +197,20 @@ fn edit_tuple_assignment(
|
|||
if let Some(cap) = ctx.config.snippet_cap {
|
||||
// place cursor on first tuple name
|
||||
if let Some(ast::Pat::IdentPat(first_pat)) = tuple_pat.fields().next() {
|
||||
edit.add_tabstop_before(
|
||||
cap,
|
||||
first_pat.name().expect("first ident pattern should have a name"),
|
||||
)
|
||||
let annotation = edit.make_tabstop_before(cap);
|
||||
editor.add_annotation(
|
||||
first_pat.name().expect("first ident pattern should have a name").syntax(),
|
||||
annotation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AssignmentEdit { ident_pat, tuple_pat, in_sub_pattern, is_shorthand_field }
|
||||
AssignmentEdit {
|
||||
ident_pat: data.ident_pat.clone(),
|
||||
tuple_pat,
|
||||
in_sub_pattern,
|
||||
is_shorthand_field,
|
||||
}
|
||||
}
|
||||
struct AssignmentEdit {
|
||||
ident_pat: ast::IdentPat,
|
||||
|
|
@ -206,23 +220,30 @@ struct AssignmentEdit {
|
|||
}
|
||||
|
||||
impl AssignmentEdit {
|
||||
fn apply(self) {
|
||||
fn apply(self, syntax_editor: &mut SyntaxEditor, syntax_mapping: &SyntaxFactory) {
|
||||
// with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)`
|
||||
if self.in_sub_pattern {
|
||||
self.ident_pat.set_pat(Some(self.tuple_pat.into()))
|
||||
self.ident_pat.set_pat_with_editor(
|
||||
Some(self.tuple_pat.into()),
|
||||
syntax_editor,
|
||||
syntax_mapping,
|
||||
)
|
||||
} else if self.is_shorthand_field {
|
||||
ted::insert(ted::Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax());
|
||||
ted::insert_raw(ted::Position::after(self.ident_pat.syntax()), make::token(T![:]));
|
||||
syntax_editor.insert(Position::after(self.ident_pat.syntax()), self.tuple_pat.syntax());
|
||||
syntax_editor
|
||||
.insert(Position::after(self.ident_pat.syntax()), syntax_mapping.whitespace(" "));
|
||||
syntax_editor
|
||||
.insert(Position::after(self.ident_pat.syntax()), syntax_mapping.token(T![:]));
|
||||
} else {
|
||||
ted::replace(self.ident_pat.syntax(), self.tuple_pat.syntax())
|
||||
syntax_editor.replace(self.ident_pat.syntax(), self.tuple_pat.syntax())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn edit_tuple_usages(
|
||||
data: &TupleData,
|
||||
edit: &mut SourceChangeBuilder,
|
||||
ctx: &AssistContext<'_>,
|
||||
make: &SyntaxFactory,
|
||||
in_sub_pattern: bool,
|
||||
) -> Option<Vec<EditTupleUsage>> {
|
||||
// We need to collect edits first before actually applying them
|
||||
|
|
@ -238,20 +259,20 @@ fn edit_tuple_usages(
|
|||
.as_ref()?
|
||||
.as_slice()
|
||||
.iter()
|
||||
.filter_map(|r| edit_tuple_usage(ctx, edit, r, data, in_sub_pattern))
|
||||
.filter_map(|r| edit_tuple_usage(ctx, make, r, data, in_sub_pattern))
|
||||
.collect_vec();
|
||||
|
||||
Some(edits)
|
||||
}
|
||||
fn edit_tuple_usage(
|
||||
ctx: &AssistContext<'_>,
|
||||
builder: &mut SourceChangeBuilder,
|
||||
make: &SyntaxFactory,
|
||||
usage: &FileReference,
|
||||
data: &TupleData,
|
||||
in_sub_pattern: bool,
|
||||
) -> Option<EditTupleUsage> {
|
||||
match detect_tuple_index(usage, data) {
|
||||
Some(index) => Some(edit_tuple_field_usage(ctx, builder, data, index)),
|
||||
Some(index) => Some(edit_tuple_field_usage(ctx, make, data, index)),
|
||||
None if in_sub_pattern => {
|
||||
cov_mark::hit!(destructure_tuple_call_with_subpattern);
|
||||
None
|
||||
|
|
@ -262,20 +283,18 @@ fn edit_tuple_usage(
|
|||
|
||||
fn edit_tuple_field_usage(
|
||||
ctx: &AssistContext<'_>,
|
||||
builder: &mut SourceChangeBuilder,
|
||||
make: &SyntaxFactory,
|
||||
data: &TupleData,
|
||||
index: TupleIndex,
|
||||
) -> EditTupleUsage {
|
||||
let field_name = &data.field_names[index.index];
|
||||
let field_name = make::expr_path(make::ext::ident_path(field_name));
|
||||
let field_name = make.expr_path(make.ident_path(field_name));
|
||||
|
||||
if data.ref_type.is_some() {
|
||||
let (replace_expr, ref_data) = determine_ref_and_parens(ctx, &index.field_expr);
|
||||
let replace_expr = builder.make_mut(replace_expr);
|
||||
EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr(field_name))
|
||||
EditTupleUsage::ReplaceExpr(replace_expr, ref_data.wrap_expr_with_factory(field_name, make))
|
||||
} else {
|
||||
let field_expr = builder.make_mut(index.field_expr);
|
||||
EditTupleUsage::ReplaceExpr(field_expr.into(), field_name)
|
||||
EditTupleUsage::ReplaceExpr(index.field_expr.into(), field_name)
|
||||
}
|
||||
}
|
||||
enum EditTupleUsage {
|
||||
|
|
@ -291,14 +310,14 @@ enum EditTupleUsage {
|
|||
}
|
||||
|
||||
impl EditTupleUsage {
|
||||
fn apply(self, edit: &mut SourceChangeBuilder) {
|
||||
fn apply(self, edit: &mut SourceChangeBuilder, syntax_editor: &mut SyntaxEditor) {
|
||||
match self {
|
||||
EditTupleUsage::NoIndex(range) => {
|
||||
edit.insert(range.start(), "/*");
|
||||
edit.insert(range.end(), "*/");
|
||||
}
|
||||
EditTupleUsage::ReplaceExpr(target_expr, replace_with) => {
|
||||
ted::replace(target_expr.syntax(), replace_with.clone_for_update().syntax())
|
||||
syntax_editor.replace(target_expr.syntax(), replace_with.syntax())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
//! based on the parent of the existing expression.
|
||||
use syntax::{
|
||||
AstNode, T,
|
||||
ast::{self, FieldExpr, MethodCallExpr, make},
|
||||
ast::{self, FieldExpr, MethodCallExpr, make, syntax_factory::SyntaxFactory},
|
||||
};
|
||||
|
||||
use crate::AssistContext;
|
||||
|
|
@ -130,4 +130,20 @@ impl RefData {
|
|||
|
||||
expr
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_expr_with_factory(
|
||||
&self,
|
||||
mut expr: ast::Expr,
|
||||
syntax_factory: &SyntaxFactory,
|
||||
) -> ast::Expr {
|
||||
if self.needs_deref {
|
||||
expr = syntax_factory.expr_prefix(T![*], expr).into();
|
||||
}
|
||||
|
||||
if self.needs_parentheses {
|
||||
expr = syntax_factory.expr_paren(expr).into();
|
||||
}
|
||||
|
||||
expr
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,9 @@ use crate::{
|
|||
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
||||
SyntaxNode, SyntaxToken,
|
||||
algo::{self, neighbor},
|
||||
ast::{self, HasGenericParams, edit::IndentLevel, make},
|
||||
ted::{self, Position},
|
||||
ast::{self, HasGenericParams, edit::IndentLevel, make, syntax_factory::SyntaxFactory},
|
||||
syntax_editor::{Position, SyntaxEditor},
|
||||
ted,
|
||||
};
|
||||
|
||||
use super::{GenericParam, HasName};
|
||||
|
|
@ -26,13 +27,13 @@ impl GenericParamsOwnerEdit for ast::Fn {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = if let Some(name) = self.name() {
|
||||
Position::after(name.syntax)
|
||||
ted::Position::after(name.syntax)
|
||||
} else if let Some(fn_token) = self.fn_token() {
|
||||
Position::after(fn_token)
|
||||
ted::Position::after(fn_token)
|
||||
} else if let Some(param_list) = self.param_list() {
|
||||
Position::before(param_list.syntax)
|
||||
ted::Position::before(param_list.syntax)
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -42,11 +43,11 @@ impl GenericParamsOwnerEdit for ast::Fn {
|
|||
fn get_or_create_where_clause(&self) -> ast::WhereClause {
|
||||
if self.where_clause().is_none() {
|
||||
let position = if let Some(ty) = self.ret_type() {
|
||||
Position::after(ty.syntax())
|
||||
ted::Position::after(ty.syntax())
|
||||
} else if let Some(param_list) = self.param_list() {
|
||||
Position::after(param_list.syntax())
|
||||
ted::Position::after(param_list.syntax())
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_where_clause(position);
|
||||
}
|
||||
|
|
@ -60,8 +61,8 @@ impl GenericParamsOwnerEdit for ast::Impl {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = match self.impl_token() {
|
||||
Some(imp_token) => Position::after(imp_token),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(imp_token) => ted::Position::after(imp_token),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -71,8 +72,8 @@ impl GenericParamsOwnerEdit for ast::Impl {
|
|||
fn get_or_create_where_clause(&self) -> ast::WhereClause {
|
||||
if self.where_clause().is_none() {
|
||||
let position = match self.assoc_item_list() {
|
||||
Some(items) => Position::before(items.syntax()),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(items) => ted::Position::before(items.syntax()),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
};
|
||||
create_where_clause(position);
|
||||
}
|
||||
|
|
@ -86,11 +87,11 @@ impl GenericParamsOwnerEdit for ast::Trait {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = if let Some(name) = self.name() {
|
||||
Position::after(name.syntax)
|
||||
ted::Position::after(name.syntax)
|
||||
} else if let Some(trait_token) = self.trait_token() {
|
||||
Position::after(trait_token)
|
||||
ted::Position::after(trait_token)
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -100,9 +101,9 @@ impl GenericParamsOwnerEdit for ast::Trait {
|
|||
fn get_or_create_where_clause(&self) -> ast::WhereClause {
|
||||
if self.where_clause().is_none() {
|
||||
let position = match (self.assoc_item_list(), self.semicolon_token()) {
|
||||
(Some(items), _) => Position::before(items.syntax()),
|
||||
(_, Some(tok)) => Position::before(tok),
|
||||
(None, None) => Position::last_child_of(self.syntax()),
|
||||
(Some(items), _) => ted::Position::before(items.syntax()),
|
||||
(_, Some(tok)) => ted::Position::before(tok),
|
||||
(None, None) => ted::Position::last_child_of(self.syntax()),
|
||||
};
|
||||
create_where_clause(position);
|
||||
}
|
||||
|
|
@ -116,11 +117,11 @@ impl GenericParamsOwnerEdit for ast::TypeAlias {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = if let Some(name) = self.name() {
|
||||
Position::after(name.syntax)
|
||||
ted::Position::after(name.syntax)
|
||||
} else if let Some(trait_token) = self.type_token() {
|
||||
Position::after(trait_token)
|
||||
ted::Position::after(trait_token)
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -130,10 +131,10 @@ impl GenericParamsOwnerEdit for ast::TypeAlias {
|
|||
fn get_or_create_where_clause(&self) -> ast::WhereClause {
|
||||
if self.where_clause().is_none() {
|
||||
let position = match self.eq_token() {
|
||||
Some(tok) => Position::before(tok),
|
||||
Some(tok) => ted::Position::before(tok),
|
||||
None => match self.semicolon_token() {
|
||||
Some(tok) => Position::before(tok),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(tok) => ted::Position::before(tok),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
},
|
||||
};
|
||||
create_where_clause(position);
|
||||
|
|
@ -148,11 +149,11 @@ impl GenericParamsOwnerEdit for ast::Struct {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = if let Some(name) = self.name() {
|
||||
Position::after(name.syntax)
|
||||
ted::Position::after(name.syntax)
|
||||
} else if let Some(struct_token) = self.struct_token() {
|
||||
Position::after(struct_token)
|
||||
ted::Position::after(struct_token)
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -166,13 +167,13 @@ impl GenericParamsOwnerEdit for ast::Struct {
|
|||
ast::FieldList::TupleFieldList(it) => Some(it),
|
||||
});
|
||||
let position = if let Some(tfl) = tfl {
|
||||
Position::after(tfl.syntax())
|
||||
ted::Position::after(tfl.syntax())
|
||||
} else if let Some(gpl) = self.generic_param_list() {
|
||||
Position::after(gpl.syntax())
|
||||
ted::Position::after(gpl.syntax())
|
||||
} else if let Some(name) = self.name() {
|
||||
Position::after(name.syntax())
|
||||
ted::Position::after(name.syntax())
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_where_clause(position);
|
||||
}
|
||||
|
|
@ -186,11 +187,11 @@ impl GenericParamsOwnerEdit for ast::Enum {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let position = if let Some(name) = self.name() {
|
||||
Position::after(name.syntax)
|
||||
ted::Position::after(name.syntax)
|
||||
} else if let Some(enum_token) = self.enum_token() {
|
||||
Position::after(enum_token)
|
||||
ted::Position::after(enum_token)
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_generic_param_list(position)
|
||||
}
|
||||
|
|
@ -200,11 +201,11 @@ impl GenericParamsOwnerEdit for ast::Enum {
|
|||
fn get_or_create_where_clause(&self) -> ast::WhereClause {
|
||||
if self.where_clause().is_none() {
|
||||
let position = if let Some(gpl) = self.generic_param_list() {
|
||||
Position::after(gpl.syntax())
|
||||
ted::Position::after(gpl.syntax())
|
||||
} else if let Some(name) = self.name() {
|
||||
Position::after(name.syntax())
|
||||
ted::Position::after(name.syntax())
|
||||
} else {
|
||||
Position::last_child_of(self.syntax())
|
||||
ted::Position::last_child_of(self.syntax())
|
||||
};
|
||||
create_where_clause(position);
|
||||
}
|
||||
|
|
@ -212,12 +213,12 @@ impl GenericParamsOwnerEdit for ast::Enum {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_where_clause(position: Position) {
|
||||
fn create_where_clause(position: ted::Position) {
|
||||
let where_clause = make::where_clause(empty()).clone_for_update();
|
||||
ted::insert(position, where_clause.syntax());
|
||||
}
|
||||
|
||||
fn create_generic_param_list(position: Position) -> ast::GenericParamList {
|
||||
fn create_generic_param_list(position: ted::Position) -> ast::GenericParamList {
|
||||
let gpl = make::generic_param_list(empty()).clone_for_update();
|
||||
ted::insert_raw(position, gpl.syntax());
|
||||
gpl
|
||||
|
|
@ -253,7 +254,7 @@ impl ast::GenericParamList {
|
|||
pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
|
||||
match self.generic_params().last() {
|
||||
Some(last_param) => {
|
||||
let position = Position::after(last_param.syntax());
|
||||
let position = ted::Position::after(last_param.syntax());
|
||||
let elements = vec![
|
||||
make::token(T![,]).into(),
|
||||
make::tokens::single_space().into(),
|
||||
|
|
@ -262,7 +263,7 @@ impl ast::GenericParamList {
|
|||
ted::insert_all(position, elements);
|
||||
}
|
||||
None => {
|
||||
let after_l_angle = Position::after(self.l_angle_token().unwrap());
|
||||
let after_l_angle = ted::Position::after(self.l_angle_token().unwrap());
|
||||
ted::insert(after_l_angle, generic_param.syntax());
|
||||
}
|
||||
}
|
||||
|
|
@ -412,7 +413,7 @@ impl ast::UseTree {
|
|||
match self.use_tree_list() {
|
||||
Some(it) => it,
|
||||
None => {
|
||||
let position = Position::last_child_of(self.syntax());
|
||||
let position = ted::Position::last_child_of(self.syntax());
|
||||
let use_tree_list = make::use_tree_list(empty()).clone_for_update();
|
||||
let mut elements = Vec::with_capacity(2);
|
||||
if self.coloncolon_token().is_none() {
|
||||
|
|
@ -458,7 +459,7 @@ impl ast::UseTree {
|
|||
// Next, transform 'suffix' use tree into 'prefix::{suffix}'
|
||||
let subtree = self.clone_subtree().clone_for_update();
|
||||
ted::remove_all_iter(self.syntax().children_with_tokens());
|
||||
ted::insert(Position::first_child_of(self.syntax()), prefix.syntax());
|
||||
ted::insert(ted::Position::first_child_of(self.syntax()), prefix.syntax());
|
||||
self.get_or_create_use_tree_list().add_use_tree(subtree);
|
||||
|
||||
fn split_path_prefix(prefix: &ast::Path) -> Option<()> {
|
||||
|
|
@ -507,7 +508,7 @@ impl ast::UseTreeList {
|
|||
pub fn add_use_tree(&self, use_tree: ast::UseTree) {
|
||||
let (position, elements) = match self.use_trees().last() {
|
||||
Some(last_tree) => (
|
||||
Position::after(last_tree.syntax()),
|
||||
ted::Position::after(last_tree.syntax()),
|
||||
vec![
|
||||
make::token(T![,]).into(),
|
||||
make::tokens::single_space().into(),
|
||||
|
|
@ -516,8 +517,8 @@ impl ast::UseTreeList {
|
|||
),
|
||||
None => {
|
||||
let position = match self.l_curly_token() {
|
||||
Some(l_curly) => Position::after(l_curly),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(l_curly) => ted::Position::after(l_curly),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
};
|
||||
(position, vec![use_tree.syntax.into()])
|
||||
}
|
||||
|
|
@ -582,15 +583,15 @@ impl ast::AssocItemList {
|
|||
let (indent, position, whitespace) = match self.assoc_items().last() {
|
||||
Some(last_item) => (
|
||||
IndentLevel::from_node(last_item.syntax()),
|
||||
Position::after(last_item.syntax()),
|
||||
ted::Position::after(last_item.syntax()),
|
||||
"\n\n",
|
||||
),
|
||||
None => match self.l_curly_token() {
|
||||
Some(l_curly) => {
|
||||
normalize_ws_between_braces(self.syntax());
|
||||
(IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
|
||||
(IndentLevel::from_token(&l_curly) + 1, ted::Position::after(&l_curly), "\n")
|
||||
}
|
||||
None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"),
|
||||
None => (IndentLevel::single(), ted::Position::last_child_of(self.syntax()), "\n"),
|
||||
},
|
||||
};
|
||||
let elements: Vec<SyntaxElement> = vec![
|
||||
|
|
@ -618,17 +619,17 @@ impl ast::RecordExprFieldList {
|
|||
let position = match self.fields().last() {
|
||||
Some(last_field) => {
|
||||
let comma = get_or_insert_comma_after(last_field.syntax());
|
||||
Position::after(comma)
|
||||
ted::Position::after(comma)
|
||||
}
|
||||
None => match self.l_curly_token() {
|
||||
Some(it) => Position::after(it),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(it) => ted::Position::after(it),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
},
|
||||
};
|
||||
|
||||
ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
|
||||
if is_multiline {
|
||||
ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
|
||||
ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -656,7 +657,7 @@ impl ast::RecordExprField {
|
|||
ast::make::tokens::single_space().into(),
|
||||
expr.syntax().clone().into(),
|
||||
];
|
||||
ted::insert_all_raw(Position::last_child_of(self.syntax()), children);
|
||||
ted::insert_all_raw(ted::Position::last_child_of(self.syntax()), children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -679,17 +680,17 @@ impl ast::RecordPatFieldList {
|
|||
Some(last_field) => {
|
||||
let syntax = last_field.syntax();
|
||||
let comma = get_or_insert_comma_after(syntax);
|
||||
Position::after(comma)
|
||||
ted::Position::after(comma)
|
||||
}
|
||||
None => match self.l_curly_token() {
|
||||
Some(it) => Position::after(it),
|
||||
None => Position::last_child_of(self.syntax()),
|
||||
Some(it) => ted::Position::after(it),
|
||||
None => ted::Position::last_child_of(self.syntax()),
|
||||
},
|
||||
};
|
||||
|
||||
ted::insert_all(position, vec![whitespace.into(), field.syntax().clone().into()]);
|
||||
if is_multiline {
|
||||
ted::insert(Position::after(field.syntax()), ast::make::token(T![,]));
|
||||
ted::insert(ted::Position::after(field.syntax()), ast::make::token(T![,]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -703,7 +704,7 @@ fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
|
|||
Some(it) => it,
|
||||
None => {
|
||||
let comma = ast::make::token(T![,]);
|
||||
ted::insert(Position::after(syntax), &comma);
|
||||
ted::insert(ted::Position::after(syntax), &comma);
|
||||
comma
|
||||
}
|
||||
}
|
||||
|
|
@ -728,7 +729,7 @@ fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
|||
}
|
||||
}
|
||||
Some(ws) if ws.kind() == T!['}'] => {
|
||||
ted::insert(Position::after(l), make::tokens::whitespace(&format!("\n{indent}")));
|
||||
ted::insert(ted::Position::after(l), make::tokens::whitespace(&format!("\n{indent}")));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
@ -780,6 +781,56 @@ impl ast::IdentPat {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pat_with_editor(
|
||||
&self,
|
||||
pat: Option<ast::Pat>,
|
||||
syntax_editor: &mut SyntaxEditor,
|
||||
syntax_factory: &SyntaxFactory,
|
||||
) {
|
||||
match pat {
|
||||
None => {
|
||||
if let Some(at_token) = self.at_token() {
|
||||
// Remove `@ Pat`
|
||||
let start = at_token.clone().into();
|
||||
let end = self
|
||||
.pat()
|
||||
.map(|it| it.syntax().clone().into())
|
||||
.unwrap_or_else(|| at_token.into());
|
||||
syntax_editor.delete_all(start..=end);
|
||||
|
||||
// Remove any trailing ws
|
||||
if let Some(last) =
|
||||
self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
|
||||
{
|
||||
last.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(pat) => {
|
||||
if let Some(old_pat) = self.pat() {
|
||||
// Replace existing pattern
|
||||
syntax_editor.replace(old_pat.syntax(), pat.syntax())
|
||||
} else if let Some(at_token) = self.at_token() {
|
||||
// Have an `@` token but not a pattern yet
|
||||
syntax_editor.insert(Position::after(at_token), pat.syntax());
|
||||
} else {
|
||||
// Don't have an `@`, should have a name
|
||||
let name = self.name().unwrap();
|
||||
|
||||
syntax_editor.insert_all(
|
||||
Position::after(name.syntax()),
|
||||
vec![
|
||||
syntax_factory.whitespace(" ").into(),
|
||||
syntax_factory.token(T![@]).into(),
|
||||
syntax_factory.whitespace(" ").into(),
|
||||
pat.syntax().clone().into(),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HasVisibilityEdit: ast::HasVisibility {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue