Add signature help for tuple struct patterns

This commit is contained in:
Lukas Wirth 2023-03-15 10:17:30 +01:00
parent 03a6ab0b62
commit 55120b3d83
2 changed files with 213 additions and 25 deletions

View File

@ -109,7 +109,13 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
} }
return signature_help_for_record_pat(&sema, record, token); return signature_help_for_record_pat(&sema, record, token);
}, },
ast::TupleStructPat(tuple_pat) => {}, ast::TupleStructPat(tuple_pat) => {
let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
if cursor_outside {
continue;
}
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
},
_ => (), _ => (),
} }
} }
@ -367,6 +373,90 @@ fn signature_help_for_record_lit(
) )
} }
fn signature_help_for_record_pat(
sema: &Semantics<'_, RootDatabase>,
record: ast::RecordPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
signature_help_for_record_(
sema,
record.record_pat_field_list()?.syntax().children_with_tokens(),
&record.path()?,
record
.record_pat_field_list()?
.fields()
.filter_map(|field| sema.resolve_record_pat_field(&field)),
token,
)
}
fn signature_help_for_tuple_struct_pat(
sema: &Semantics<'_, RootDatabase>,
pat: ast::TupleStructPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
let is_left_of_rest_pat =
rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
let mut res = SignatureHelp {
doc: None,
signature: String::new(),
parameters: vec![],
active_parameter: None,
};
let db = sema.db;
let path_res = sema.resolve_path(&pat.path()?)?;
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
let en = variant.parent_enum(db);
res.doc = en.docs(db).map(|it| it.into());
format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
variant.fields(db)
} else {
let adt = match path_res {
PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
PathResolution::Def(ModuleDef::Adt(adt)) => adt,
_ => return None,
};
match adt {
hir::Adt::Struct(it) => {
res.doc = it.docs(db).map(|it| it.into());
format_to!(res.signature, "struct {} (", it.name(db));
it.fields(db)
}
_ => return None,
}
};
let commas = pat
.syntax()
.children_with_tokens()
.filter_map(syntax::NodeOrToken::into_token)
.filter(|t| t.kind() == syntax::T![,]);
res.active_parameter = Some(if is_left_of_rest_pat {
commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
} else {
let n_commas = commas
.collect::<Vec<_>>()
.into_iter()
.rev()
.take_while(|t| t.text_range().start() > token.text_range().start())
.count();
fields.len().saturating_sub(1).saturating_sub(n_commas)
});
let mut buf = String::new();
for ty in fields.into_iter().map(|it| it.ty(db)) {
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
res.push_call_param(&buf);
buf.clear();
}
res.signature.push_str(")");
Some(res)
}
fn signature_help_for_record_( fn signature_help_for_record_(
sema: &Semantics<'_, RootDatabase>, sema: &Semantics<'_, RootDatabase>,
field_list_children: SyntaxElementChildren, field_list_children: SyntaxElementChildren,
@ -442,23 +532,6 @@ fn signature_help_for_record_(
Some(res) Some(res)
} }
fn signature_help_for_record_pat(
sema: &Semantics<'_, RootDatabase>,
record: ast::RecordPat,
token: SyntaxToken,
) -> Option<SignatureHelp> {
signature_help_for_record_(
sema,
record.record_pat_field_list()?.syntax().children_with_tokens(),
&record.path()?,
record
.record_pat_field_list()?
.fields()
.filter_map(|field| sema.resolve_record_pat_field(&field)),
token,
)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::iter; use std::iter;
@ -480,6 +553,7 @@ pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
(database, FilePosition { file_id, offset }) (database, FilePosition { file_id, offset })
} }
#[track_caller]
fn check(ra_fixture: &str, expect: Expect) { fn check(ra_fixture: &str, expect: Expect) {
let fixture = format!( let fixture = format!(
r#" r#"
@ -931,6 +1005,119 @@ struct S(u32, i32)
); );
} }
#[test]
fn tuple_struct_pat() {
check(
r#"
/// A cool tuple struct
struct S(u32, i32);
fn main() {
let S(0, $0);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32)
--- ^^^
"#]],
);
}
#[test]
fn tuple_struct_pat_rest() {
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
let S(0, .., $0);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16)
--- --- --- ^^^
"#]],
);
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16, u8);
fn main() {
let S(0, .., $0, 0);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16, u8)
--- --- --- ^^^ --
"#]],
);
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
let S($0, .., 1);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16)
^^^ --- --- ---
"#]],
);
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16, u8);
fn main() {
let S(1, .., 1, $0, 2);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16, u8)
--- --- --- ^^^ --
"#]],
);
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
let S(1, $0.., 1);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16)
--- ^^^ --- ---
"#]],
);
check(
r#"
/// A cool tuple struct
struct S(u32, i32, f32, u16);
fn main() {
let S(1, ..$0, 1);
}
"#,
expect![[r#"
A cool tuple struct
------
struct S (u32, i32, f32, u16)
--- ^^^ --- ---
"#]],
);
}
#[test] #[test]
fn generic_struct() { fn generic_struct() {
check( check(

View File

@ -431,14 +431,15 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) { fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) {
while !p.at(EOF) && !p.at(ket) { while !p.at(EOF) && !p.at(ket) {
if !p.at_ts(PAT_TOP_FIRST) { pattern_top(p);
p.error("expected a pattern"); if !p.at(T![,]) {
if p.at_ts(PAT_TOP_FIRST) {
p.error(format!("expected {:?}, got {:?}", T![,], p.current()));
} else {
break; break;
} }
} else {
pattern_top(p); p.bump(T![,]);
if !p.at(ket) {
p.expect(T![,]);
} }
} }
} }