Speed up resolving "Generate delegate method" assist (part 1)

Fix #19322

Sometimes there are 185 "Generate delegate" assists with the same
assist_id and asssist_kind.  This commit introduces and additional
differentiator: assist_subtype.  Therefore, when the LSP client sends
an assist resolve request, rust-analyzer only need to compute edits
for a single assist instead of 185.
This commit is contained in:
Felicián Németh 2025-03-15 08:14:20 +01:00
parent dab1329f35
commit 7aa70a86d1
5 changed files with 30 additions and 10 deletions

View File

@ -92,19 +92,18 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<'
}); });
} }
methods.sort_by(|(a, _), (b, _)| a.cmp(b)); methods.sort_by(|(a, _), (b, _)| a.cmp(b));
for (name, method) in methods { for (index, (name, method)) in methods.into_iter().enumerate() {
let adt = ast::Adt::Struct(strukt.clone()); let adt = ast::Adt::Struct(strukt.clone());
let name = name.display(ctx.db(), current_edition).to_string(); let name = name.display(ctx.db(), current_edition).to_string();
// if `find_struct_impl` returns None, that means that a function named `name` already exists. // if `find_struct_impl` returns None, that means that a function named `name` already exists.
let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else { let Some(impl_def) = find_struct_impl(ctx, &adt, std::slice::from_ref(&name)) else {
continue; continue;
}; };
let field = make::ext::field_from_idents(["self", &field_name])?; let field = make::ext::field_from_idents(["self", &field_name])?;
acc.add_group( acc.add_group(
&GroupLabel("Generate delegate methods…".to_owned()), &GroupLabel("Generate delegate methods…".to_owned()),
AssistId("generate_delegate_methods", AssistKind::Generate), AssistId("generate_delegate_methods", AssistKind::Generate, Some(index)),
format!("Generate delegate for `{field_name}.{name}()`",), format!("Generate delegate for `{field_name}.{name}()`",),
target, target,
|edit| { |edit| {

View File

@ -201,7 +201,7 @@ impl Struct {
pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) { pub(crate) fn delegate(&self, field: Field, acc: &mut Assists, ctx: &AssistContext<'_>) {
let db = ctx.db(); let db = ctx.db();
for delegee in &field.impls { for (index, delegee) in field.impls.iter().enumerate() {
let trait_ = match delegee { let trait_ = match delegee {
Delegee::Bound(b) => b, Delegee::Bound(b) => b,
Delegee::Impls(i, _) => i, Delegee::Impls(i, _) => i,
@ -229,7 +229,11 @@ impl Struct {
acc.add_group( acc.add_group(
&GroupLabel(format!("Generate delegate trait impls for field `{}`", field.name)), &GroupLabel(format!("Generate delegate trait impls for field `{}`", field.name)),
AssistId("generate_delegate_trait", ide_db::assists::AssistKind::Generate), AssistId(
"generate_delegate_trait",
ide_db::assists::AssistKind::Generate,
Some(index),
),
format!("Generate delegate trait impl `{}` for `{}`", signature, field.name), format!("Generate delegate trait impl `{}` for `{}`", signature, field.name),
field.range, field.range,
|builder| { |builder| {

View File

@ -105,7 +105,7 @@ impl FromStr for AssistKind {
/// Unique identifier of the assist, should not be shown to the user /// Unique identifier of the assist, should not be shown to the user
/// directly. /// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str, pub AssistKind); pub struct AssistId(pub &'static str, pub AssistKind, pub Option<usize>);
/// A way to control how many assist to resolve during the assist resolution. /// A way to control how many assist to resolve during the assist resolution.
/// When an assist is resolved, its edits are calculated that might be costly to always do by default. /// When an assist is resolved, its edits are calculated that might be costly to always do by default.
@ -128,6 +128,8 @@ pub struct SingleResolve {
pub assist_id: String, pub assist_id: String,
// The kind of the assist. // The kind of the assist.
pub assist_kind: AssistKind, pub assist_kind: AssistKind,
/// Subtype of the assist. When many assists have the same id, it differentiates among them.
pub assist_subtype: Option<usize>,
} }
impl AssistResolveStrategy { impl AssistResolveStrategy {
@ -136,7 +138,9 @@ impl AssistResolveStrategy {
AssistResolveStrategy::None => false, AssistResolveStrategy::None => false,
AssistResolveStrategy::All => true, AssistResolveStrategy::All => true,
AssistResolveStrategy::Single(single_resolve) => { AssistResolveStrategy::Single(single_resolve) => {
single_resolve.assist_id == id.0 && single_resolve.assist_kind == id.1 single_resolve.assist_id == id.0
&& single_resolve.assist_kind == id.1
&& single_resolve.assist_subtype == id.2
} }
} }
} }

View File

@ -1568,13 +1568,21 @@ pub(crate) fn handle_code_action_resolve(
fn parse_action_id(action_id: &str) -> anyhow::Result<(usize, SingleResolve), String> { fn parse_action_id(action_id: &str) -> anyhow::Result<(usize, SingleResolve), String> {
let id_parts = action_id.split(':').collect::<Vec<_>>(); let id_parts = action_id.split(':').collect::<Vec<_>>();
match id_parts.as_slice() { match id_parts.as_slice() {
[assist_id_string, assist_kind_string, index_string] => { [assist_id_string, assist_kind_string, index_string, subtype_str] => {
let assist_kind: AssistKind = assist_kind_string.parse()?; let assist_kind: AssistKind = assist_kind_string.parse()?;
let index: usize = match index_string.parse() { let index: usize = match index_string.parse() {
Ok(index) => index, Ok(index) => index,
Err(e) => return Err(format!("Incorrect index string: {e}")), Err(e) => return Err(format!("Incorrect index string: {e}")),
}; };
Ok((index, SingleResolve { assist_id: assist_id_string.to_string(), assist_kind })) let assist_subtype = subtype_str.parse::<usize>().ok();
Ok((
index,
SingleResolve {
assist_id: assist_id_string.to_string(),
assist_kind,
assist_subtype,
},
))
} }
_ => Err("Action id contains incorrect number of segments".to_owned()), _ => Err("Action id contains incorrect number of segments".to_owned()),
} }

View File

@ -1514,7 +1514,12 @@ pub(crate) fn code_action(
(Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?), (Some(it), _) => res.edit = Some(snippet_workspace_edit(snap, it)?),
(None, Some((index, code_action_params, version))) => { (None, Some((index, code_action_params, version))) => {
res.data = Some(lsp_ext::CodeActionData { res.data = Some(lsp_ext::CodeActionData {
id: format!("{}:{}:{index}", assist.id.0, assist.id.1.name()), id: format!(
"{}:{}:{index}:{}",
assist.id.0,
assist.id.1.name(),
assist.id.2.map(|x| x.to_string()).unwrap_or("".to_owned())
),
code_action_params, code_action_params,
version, version,
}); });