mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
migrate generate new
This commit is contained in:
parent
ed193af369
commit
9cc03e01c5
@ -94,7 +94,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
|
|||||||
})?;
|
})?;
|
||||||
let _ = process_ref_mut(&fn_);
|
let _ = process_ref_mut(&fn_);
|
||||||
|
|
||||||
let assoc_list = make::assoc_item_list().clone_for_update();
|
let assoc_list = make::assoc_item_list(None).clone_for_update();
|
||||||
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
|
ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
|
||||||
impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_));
|
impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_));
|
||||||
|
|
||||||
|
@ -4,12 +4,12 @@ use ide_db::{
|
|||||||
};
|
};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
|
ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
|
||||||
ted,
|
syntax_editor::Position,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
AssistContext, AssistId, Assists,
|
AssistContext, AssistId, Assists,
|
||||||
utils::{find_struct_impl, generate_impl},
|
utils::{find_struct_impl, generate_impl_with_item},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Assist: generate_new
|
// Assist: generate_new
|
||||||
@ -149,7 +149,53 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
|||||||
.clone_for_update();
|
.clone_for_update();
|
||||||
fn_.indent(1.into());
|
fn_.indent(1.into());
|
||||||
|
|
||||||
if let Some(cap) = ctx.config.snippet_cap {
|
let mut editor = builder.make_editor(strukt.syntax());
|
||||||
|
|
||||||
|
// Get the node for set annotation
|
||||||
|
let contain_fn = if let Some(impl_def) = impl_def {
|
||||||
|
fn_.indent(impl_def.indent_level());
|
||||||
|
|
||||||
|
if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token())
|
||||||
|
{
|
||||||
|
editor.insert_all(
|
||||||
|
Position::after(l_curly),
|
||||||
|
vec![
|
||||||
|
make::tokens::whitespace(&format!("\n{}", impl_def.indent_level() + 1))
|
||||||
|
.into(),
|
||||||
|
fn_.syntax().clone().into(),
|
||||||
|
make::tokens::whitespace("\n").into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
fn_.syntax().clone()
|
||||||
|
} else {
|
||||||
|
let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
|
||||||
|
let list = make::assoc_item_list(Some(items));
|
||||||
|
editor.insert(Position::after(impl_def.syntax()), list.syntax());
|
||||||
|
list.syntax().clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Generate a new impl to add the method to
|
||||||
|
let indent_level = strukt.indent_level();
|
||||||
|
let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
|
||||||
|
let list = make::assoc_item_list(Some(body));
|
||||||
|
let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
|
||||||
|
|
||||||
|
impl_def.indent(strukt.indent_level());
|
||||||
|
|
||||||
|
// Insert it after the adt
|
||||||
|
editor.insert_all(
|
||||||
|
Position::after(strukt.syntax()),
|
||||||
|
vec![
|
||||||
|
make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
|
||||||
|
impl_def.syntax().clone().into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
impl_def.syntax().clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(fn_) = contain_fn.descendants().find_map(ast::Fn::cast)
|
||||||
|
&& let Some(cap) = ctx.config.snippet_cap
|
||||||
|
{
|
||||||
match strukt.kind() {
|
match strukt.kind() {
|
||||||
StructKind::Tuple(_) => {
|
StructKind::Tuple(_) => {
|
||||||
let struct_args = fn_
|
let struct_args = fn_
|
||||||
@ -168,8 +214,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
|||||||
for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
|
for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
|
||||||
if let Some(fn_pat) = fn_param.pat() {
|
if let Some(fn_pat) = fn_param.pat() {
|
||||||
let fn_pat = fn_pat.syntax().clone();
|
let fn_pat = fn_pat.syntax().clone();
|
||||||
builder
|
let placeholder = builder.make_placeholder_snippet(cap);
|
||||||
.add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]);
|
editor.add_annotation_all(vec![struct_arg, fn_pat], placeholder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,36 +225,12 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
|
|||||||
|
|
||||||
// Add a tabstop before the name
|
// Add a tabstop before the name
|
||||||
if let Some(name) = fn_.name() {
|
if let Some(name) = fn_.name() {
|
||||||
builder.add_tabstop_before(cap, name);
|
let tabstop_before = builder.make_tabstop_before(cap);
|
||||||
|
editor.add_annotation(name.syntax(), tabstop_before);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the mutable version of the impl to modify
|
builder.add_file_edits(ctx.vfs_file_id(), editor);
|
||||||
let impl_def = if let Some(impl_def) = impl_def {
|
|
||||||
fn_.indent(impl_def.indent_level());
|
|
||||||
builder.make_mut(impl_def)
|
|
||||||
} else {
|
|
||||||
// Generate a new impl to add the method to
|
|
||||||
let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone()));
|
|
||||||
let indent_level = strukt.indent_level();
|
|
||||||
fn_.indent(indent_level);
|
|
||||||
|
|
||||||
// Insert it after the adt
|
|
||||||
let strukt = builder.make_mut(strukt.clone());
|
|
||||||
|
|
||||||
ted::insert_all_raw(
|
|
||||||
ted::Position::after(strukt.syntax()),
|
|
||||||
vec![
|
|
||||||
make::tokens::whitespace(&format!("\n\n{indent_level}")).into(),
|
|
||||||
impl_def.syntax().clone().into(),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl_def
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add the `new` method at the start of the impl
|
|
||||||
impl_def.get_or_create_assoc_item_list().add_item_at_start(fn_.into());
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,8 +663,15 @@ fn generate_impl_text_inner(
|
|||||||
|
|
||||||
/// Generates the corresponding `impl Type {}` including type and lifetime
|
/// Generates the corresponding `impl Type {}` including type and lifetime
|
||||||
/// parameters.
|
/// parameters.
|
||||||
|
pub(crate) fn generate_impl_with_item(
|
||||||
|
adt: &ast::Adt,
|
||||||
|
body: Option<ast::AssocItemList>,
|
||||||
|
) -> ast::Impl {
|
||||||
|
generate_impl_inner(adt, None, true, body)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
|
pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
|
||||||
generate_impl_inner(adt, None, true)
|
generate_impl_inner(adt, None, true, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the corresponding `impl <trait> for Type {}` including type
|
/// Generates the corresponding `impl <trait> for Type {}` including type
|
||||||
@ -672,7 +679,7 @@ pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl {
|
|||||||
///
|
///
|
||||||
/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
|
/// This is useful for traits like `PartialEq`, since `impl<T> PartialEq for U<T>` often requires `T: PartialEq`.
|
||||||
pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
|
pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
|
||||||
generate_impl_inner(adt, Some(trait_), true)
|
generate_impl_inner(adt, Some(trait_), true, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generates the corresponding `impl <trait> for Type {}` including type
|
/// Generates the corresponding `impl <trait> for Type {}` including type
|
||||||
@ -680,13 +687,14 @@ pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Imp
|
|||||||
///
|
///
|
||||||
/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
|
/// This is useful for traits like `From<T>`, since `impl<T> From<T> for U<T>` doesn't require `T: From<T>`.
|
||||||
pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
|
pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl {
|
||||||
generate_impl_inner(adt, Some(trait_), false)
|
generate_impl_inner(adt, Some(trait_), false, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_impl_inner(
|
fn generate_impl_inner(
|
||||||
adt: &ast::Adt,
|
adt: &ast::Adt,
|
||||||
trait_: Option<ast::Type>,
|
trait_: Option<ast::Type>,
|
||||||
trait_is_transitive: bool,
|
trait_is_transitive: bool,
|
||||||
|
body: Option<ast::AssocItemList>,
|
||||||
) -> ast::Impl {
|
) -> ast::Impl {
|
||||||
// Ensure lifetime params are before type & const params
|
// Ensure lifetime params are before type & const params
|
||||||
let generic_params = adt.generic_param_list().map(|generic_params| {
|
let generic_params = adt.generic_param_list().map(|generic_params| {
|
||||||
@ -736,9 +744,9 @@ fn generate_impl_inner(
|
|||||||
ty,
|
ty,
|
||||||
None,
|
None,
|
||||||
adt.where_clause(),
|
adt.where_clause(),
|
||||||
None,
|
body,
|
||||||
),
|
),
|
||||||
None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), None),
|
None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body),
|
||||||
}
|
}
|
||||||
.clone_for_update();
|
.clone_for_update();
|
||||||
|
|
||||||
|
@ -644,7 +644,7 @@ impl Removable for ast::Use {
|
|||||||
impl ast::Impl {
|
impl ast::Impl {
|
||||||
pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
|
pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
|
||||||
if self.assoc_item_list().is_none() {
|
if self.assoc_item_list().is_none() {
|
||||||
let assoc_item_list = make::assoc_item_list().clone_for_update();
|
let assoc_item_list = make::assoc_item_list(None).clone_for_update();
|
||||||
ted::append_child(self.syntax(), assoc_item_list.syntax());
|
ted::append_child(self.syntax(), assoc_item_list.syntax());
|
||||||
}
|
}
|
||||||
self.assoc_item_list().unwrap()
|
self.assoc_item_list().unwrap()
|
||||||
|
@ -229,8 +229,18 @@ pub fn ty_fn_ptr<I: Iterator<Item = Param>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn assoc_item_list() -> ast::AssocItemList {
|
pub fn assoc_item_list(
|
||||||
ast_from_text("impl C for D {}")
|
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
|
||||||
|
) -> ast::AssocItemList {
|
||||||
|
let is_break_braces = body.is_some();
|
||||||
|
let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() };
|
||||||
|
let body_indent = if is_break_braces { " ".to_owned() } else { String::new() };
|
||||||
|
|
||||||
|
let body = match body {
|
||||||
|
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_gen_params(
|
fn merge_gen_params(
|
||||||
@ -273,7 +283,7 @@ pub fn impl_(
|
|||||||
generic_args: Option<ast::GenericArgList>,
|
generic_args: Option<ast::GenericArgList>,
|
||||||
path_type: ast::Type,
|
path_type: ast::Type,
|
||||||
where_clause: Option<ast::WhereClause>,
|
where_clause: Option<ast::WhereClause>,
|
||||||
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
|
body: Option<ast::AssocItemList>,
|
||||||
) -> ast::Impl {
|
) -> ast::Impl {
|
||||||
let gen_args = generic_args.map_or_else(String::new, |it| it.to_string());
|
let gen_args = generic_args.map_or_else(String::new, |it| it.to_string());
|
||||||
|
|
||||||
@ -281,20 +291,13 @@ pub fn impl_(
|
|||||||
|
|
||||||
let body_newline =
|
let body_newline =
|
||||||
if where_clause.is_some() && body.is_none() { "\n".to_owned() } else { String::new() };
|
if where_clause.is_some() && body.is_none() { "\n".to_owned() } else { String::new() };
|
||||||
|
|
||||||
let where_clause = match where_clause {
|
let where_clause = match where_clause {
|
||||||
Some(pr) => format!("\n{pr}\n"),
|
Some(pr) => format!("\n{pr}\n"),
|
||||||
None => " ".to_owned(),
|
None => " ".to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = match body {
|
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
|
||||||
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
|
ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}"))
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ast_from_text(&format!(
|
|
||||||
"impl{gen_params} {path_type}{gen_args}{where_clause}{{{body_newline}{body}}}"
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn impl_trait(
|
pub fn impl_trait(
|
||||||
@ -308,7 +311,7 @@ pub fn impl_trait(
|
|||||||
ty: ast::Type,
|
ty: ast::Type,
|
||||||
trait_where_clause: Option<ast::WhereClause>,
|
trait_where_clause: Option<ast::WhereClause>,
|
||||||
ty_where_clause: Option<ast::WhereClause>,
|
ty_where_clause: Option<ast::WhereClause>,
|
||||||
body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
|
body: Option<ast::AssocItemList>,
|
||||||
) -> ast::Impl {
|
) -> ast::Impl {
|
||||||
let is_unsafe = if is_unsafe { "unsafe " } else { "" };
|
let is_unsafe = if is_unsafe { "unsafe " } else { "" };
|
||||||
|
|
||||||
@ -330,13 +333,10 @@ pub fn impl_trait(
|
|||||||
let where_clause = merge_where_clause(ty_where_clause, trait_where_clause)
|
let where_clause = merge_where_clause(ty_where_clause, trait_where_clause)
|
||||||
.map_or_else(|| " ".to_owned(), |wc| format!("\n{wc}\n"));
|
.map_or_else(|| " ".to_owned(), |wc| format!("\n{wc}\n"));
|
||||||
|
|
||||||
let body = match body {
|
let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string());
|
||||||
Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""),
|
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
ast_from_text(&format!(
|
ast_from_text(&format!(
|
||||||
"{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{{{body_newline}{body}}}"
|
"{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}"
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
|
//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt, iter,
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
ops::RangeInclusive,
|
ops::RangeInclusive,
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
@ -41,6 +41,15 @@ impl SyntaxEditor {
|
|||||||
self.annotations.push((element.syntax_element(), annotation))
|
self.annotations.push((element.syntax_element(), annotation))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_annotation_all(
|
||||||
|
&mut self,
|
||||||
|
elements: Vec<impl Element>,
|
||||||
|
annotation: SyntaxAnnotation,
|
||||||
|
) {
|
||||||
|
self.annotations
|
||||||
|
.extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation)));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn merge(&mut self, mut other: SyntaxEditor) {
|
pub fn merge(&mut self, mut other: SyntaxEditor) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
self.root == other.root || other.root.ancestors().any(|node| node == self.root),
|
self.root == other.root || other.root.ancestors().any(|node| node == self.root),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user