feat: enhance signature help to display generic parameters for callables and default values for generic args

This commit is contained in:
roifewu 2025-04-16 02:34:48 +08:00
parent f766d1403e
commit 727d4b184d

View File

@ -5,13 +5,15 @@ use std::collections::BTreeSet;
use either::Either; use either::Either;
use hir::{ use hir::{
AssocItem, DisplayTarget, GenericParam, HirDisplay, ModuleDef, PathResolution, Semantics, Trait, AssocItem, DisplayTarget, GenericDef, GenericParam, HirDisplay, ModuleDef, PathResolution,
Semantics, Trait,
}; };
use ide_db::{ use ide_db::{
FilePosition, FxIndexMap, FilePosition, FxIndexMap,
active_parameter::{callable_for_arg_list, generic_def_for_node}, active_parameter::{callable_for_arg_list, generic_def_for_node},
documentation::{Documentation, HasDocs}, documentation::{Documentation, HasDocs},
}; };
use itertools::Itertools;
use span::Edition; use span::Edition;
use stdx::format_to; use stdx::format_to;
use syntax::{ use syntax::{
@ -175,6 +177,20 @@ fn signature_help_for_call(
hir::CallableKind::Function(func) => { hir::CallableKind::Function(func) => {
res.doc = func.docs(db); res.doc = func.docs(db);
format_to!(res.signature, "fn {}", func.name(db).display(db, edition)); format_to!(res.signature, "fn {}", func.name(db).display(db, edition));
let generic_params = GenericDef::Function(func)
.params(db)
.iter()
.filter(|param| match param {
GenericParam::TypeParam(type_param) => !type_param.is_implicit(db),
GenericParam::ConstParam(_) | GenericParam::LifetimeParam(_) => true,
})
.map(|param| param.display(db, display_target))
.join(", ");
if !generic_params.is_empty() {
format_to!(res.signature, "<{}>", generic_params);
}
fn_params = Some(match callable.receiver_param(db) { fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db), Some(_self) => func.params_without_self(db),
None => func.assoc_fn_params(db), None => func.assoc_fn_params(db),
@ -183,15 +199,34 @@ fn signature_help_for_call(
hir::CallableKind::TupleStruct(strukt) => { hir::CallableKind::TupleStruct(strukt) => {
res.doc = strukt.docs(db); res.doc = strukt.docs(db);
format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition)); format_to!(res.signature, "struct {}", strukt.name(db).display(db, edition));
let generic_params = GenericDef::Adt(strukt.into())
.params(db)
.iter()
.map(|param| param.display(db, display_target))
.join(", ");
if !generic_params.is_empty() {
format_to!(res.signature, "<{}>", generic_params);
}
} }
hir::CallableKind::TupleEnumVariant(variant) => { hir::CallableKind::TupleEnumVariant(variant) => {
res.doc = variant.docs(db); res.doc = variant.docs(db);
format_to!( format_to!(
res.signature, res.signature,
"enum {}::{}", "enum {}",
variant.parent_enum(db).name(db).display(db, edition), variant.parent_enum(db).name(db).display(db, edition),
variant.name(db).display(db, edition)
); );
let generic_params = GenericDef::Adt(variant.parent_enum(db).into())
.params(db)
.iter()
.map(|param| param.display(db, display_target))
.join(", ");
if !generic_params.is_empty() {
format_to!(res.signature, "<{}>", generic_params);
}
format_to!(res.signature, "::{}", variant.name(db).display(db, edition))
} }
hir::CallableKind::Closure(closure) => { hir::CallableKind::Closure(closure) => {
let fn_trait = closure.fn_trait(db); let fn_trait = closure.fn_trait(db);
@ -339,6 +374,20 @@ fn signature_help_for_generics(
buf.clear(); buf.clear();
format_to!(buf, "{}", param.display(db, display_target)); format_to!(buf, "{}", param.display(db, display_target));
match param {
GenericParam::TypeParam(param) => {
if let Some(ty) = param.default(db) {
format_to!(buf, " = {}", ty.display(db, display_target));
}
}
GenericParam::ConstParam(param) => {
if let Some(expr) = param.default(db, display_target).and_then(|konst| konst.expr())
{
format_to!(buf, " = {}", expr);
}
}
_ => {}
}
res.push_generic_param(&buf); res.push_generic_param(&buf);
} }
if let hir::GenericDef::Trait(tr) = generics_def { if let hir::GenericDef::Trait(tr) = generics_def {
@ -815,7 +864,7 @@ fn foo<T, U: Copy + Display>(x: T, y: U) -> u32
fn bar() { foo($03, ); } fn bar() { foo($03, ); }
"#, "#,
expect![[r#" expect![[r#"
fn foo(x: i32, y: U) -> u32 fn foo<T, U>(x: i32, y: U) -> u32
^^^^^^ ---- ^^^^^^ ----
"#]], "#]],
); );
@ -829,7 +878,7 @@ fn foo<T>() -> T where T: Copy + Display {}
fn bar() { foo($0); } fn bar() { foo($0); }
"#, "#,
expect![[r#" expect![[r#"
fn foo() -> T fn foo<T>() -> T
"#]], "#]],
); );
} }
@ -1279,7 +1328,7 @@ fn main() {
} }
"#, "#,
expect![[r#" expect![[r#"
struct S({unknown}) struct S<T>({unknown})
^^^^^^^^^ ^^^^^^^^^
"#]], "#]],
); );
@ -1375,7 +1424,7 @@ id! {
fn test() { S.foo($0); } fn test() { S.foo($0); }
"#, "#,
expect![[r#" expect![[r#"
fn foo(&'a mut self) fn foo<'a>(&'a mut self)
"#]], "#]],
); );
} }
@ -1724,7 +1773,7 @@ fn sup() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn test(&mut self, val: V) fn test<V>(&mut self, val: V)
^^^^^^ ^^^^^^
"#]], "#]],
); );
@ -1901,7 +1950,7 @@ fn f() {
} }
"#, "#,
expect![[r#" expect![[r#"
fn foo(x: Wrap<impl Trait<U>>) fn foo<U>(x: Wrap<impl Trait<U>>)
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
"#]], "#]],
); );
@ -2394,4 +2443,96 @@ fn main() {
"#]], "#]],
); );
} }
#[test]
fn test_tuple_generic_param() {
check(
r#"
struct S<T>(T);
fn main() {
let s: S<$0
}
"#,
expect![[r#"
struct S<T>
^
"#]],
);
}
#[test]
fn test_enum_generic_param() {
check(
r#"
enum Option<T> {
Some(T),
None,
}
fn main() {
let opt: Option<$0
}
"#,
expect![[r#"
enum Option<T>
^
"#]],
);
}
#[test]
fn test_enum_variant_generic_param() {
check(
r#"
enum Option<T> {
Some(T),
None,
}
fn main() {
let opt = Option::Some($0);
}
"#,
expect![[r#"
enum Option<T>::Some({unknown})
^^^^^^^^^
"#]],
);
}
#[test]
fn test_generic_arg_with_default() {
check(
r#"
struct S<T = u8> {
field: T,
}
fn main() {
let s: S<$0
}
"#,
expect![[r#"
struct S<T = u8>
^^^^^^
"#]],
);
check(
r#"
struct S<const C: u8 = 5> {
field: C,
}
fn main() {
let s: S<$0
}
"#,
expect![[r#"
struct S<const C: u8 = 5>
^^^^^^^^^^^^^^^
"#]],
);
}
} }