Skip to content

Commit 55120b3

Browse files
committed
Add signature help for tuple struct patterns
1 parent 03a6ab0 commit 55120b3

File tree

2 files changed

+213
-25
lines changed

2 files changed

+213
-25
lines changed

crates/ide/src/signature_help.rs

Lines changed: 205 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,13 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio
109109
}
110110
return signature_help_for_record_pat(&sema, record, token);
111111
},
112-
ast::TupleStructPat(tuple_pat) => {},
112+
ast::TupleStructPat(tuple_pat) => {
113+
let cursor_outside = tuple_pat.r_paren_token().as_ref() == Some(&token);
114+
if cursor_outside {
115+
continue;
116+
}
117+
return signature_help_for_tuple_struct_pat(&sema, tuple_pat, token);
118+
},
113119
_ => (),
114120
}
115121
}
@@ -367,6 +373,90 @@ fn signature_help_for_record_lit(
367373
)
368374
}
369375

376+
fn signature_help_for_record_pat(
377+
sema: &Semantics<'_, RootDatabase>,
378+
record: ast::RecordPat,
379+
token: SyntaxToken,
380+
) -> Option<SignatureHelp> {
381+
signature_help_for_record_(
382+
sema,
383+
record.record_pat_field_list()?.syntax().children_with_tokens(),
384+
&record.path()?,
385+
record
386+
.record_pat_field_list()?
387+
.fields()
388+
.filter_map(|field| sema.resolve_record_pat_field(&field)),
389+
token,
390+
)
391+
}
392+
393+
fn signature_help_for_tuple_struct_pat(
394+
sema: &Semantics<'_, RootDatabase>,
395+
pat: ast::TupleStructPat,
396+
token: SyntaxToken,
397+
) -> Option<SignatureHelp> {
398+
let rest_pat = pat.fields().find(|it| matches!(it, ast::Pat::RestPat(_)));
399+
let is_left_of_rest_pat =
400+
rest_pat.map_or(true, |it| token.text_range().start() < it.syntax().text_range().end());
401+
402+
let mut res = SignatureHelp {
403+
doc: None,
404+
signature: String::new(),
405+
parameters: vec![],
406+
active_parameter: None,
407+
};
408+
409+
let db = sema.db;
410+
let path_res = sema.resolve_path(&pat.path()?)?;
411+
let fields: Vec<_> = if let PathResolution::Def(ModuleDef::Variant(variant)) = path_res {
412+
let en = variant.parent_enum(db);
413+
414+
res.doc = en.docs(db).map(|it| it.into());
415+
format_to!(res.signature, "enum {}::{} (", en.name(db), variant.name(db));
416+
variant.fields(db)
417+
} else {
418+
let adt = match path_res {
419+
PathResolution::SelfType(imp) => imp.self_ty(db).as_adt()?,
420+
PathResolution::Def(ModuleDef::Adt(adt)) => adt,
421+
_ => return None,
422+
};
423+
424+
match adt {
425+
hir::Adt::Struct(it) => {
426+
res.doc = it.docs(db).map(|it| it.into());
427+
format_to!(res.signature, "struct {} (", it.name(db));
428+
it.fields(db)
429+
}
430+
_ => return None,
431+
}
432+
};
433+
let commas = pat
434+
.syntax()
435+
.children_with_tokens()
436+
.filter_map(syntax::NodeOrToken::into_token)
437+
.filter(|t| t.kind() == syntax::T![,]);
438+
res.active_parameter = Some(if is_left_of_rest_pat {
439+
commas.take_while(|t| t.text_range().start() <= token.text_range().start()).count()
440+
} else {
441+
let n_commas = commas
442+
.collect::<Vec<_>>()
443+
.into_iter()
444+
.rev()
445+
.take_while(|t| t.text_range().start() > token.text_range().start())
446+
.count();
447+
fields.len().saturating_sub(1).saturating_sub(n_commas)
448+
});
449+
450+
let mut buf = String::new();
451+
for ty in fields.into_iter().map(|it| it.ty(db)) {
452+
format_to!(buf, "{}", ty.display_truncated(db, Some(20)));
453+
res.push_call_param(&buf);
454+
buf.clear();
455+
}
456+
res.signature.push_str(")");
457+
Some(res)
458+
}
459+
370460
fn signature_help_for_record_(
371461
sema: &Semantics<'_, RootDatabase>,
372462
field_list_children: SyntaxElementChildren,
@@ -442,23 +532,6 @@ fn signature_help_for_record_(
442532
Some(res)
443533
}
444534

445-
fn signature_help_for_record_pat(
446-
sema: &Semantics<'_, RootDatabase>,
447-
record: ast::RecordPat,
448-
token: SyntaxToken,
449-
) -> Option<SignatureHelp> {
450-
signature_help_for_record_(
451-
sema,
452-
record.record_pat_field_list()?.syntax().children_with_tokens(),
453-
&record.path()?,
454-
record
455-
.record_pat_field_list()?
456-
.fields()
457-
.filter_map(|field| sema.resolve_record_pat_field(&field)),
458-
token,
459-
)
460-
}
461-
462535
#[cfg(test)]
463536
mod tests {
464537
use std::iter;
@@ -480,6 +553,7 @@ mod tests {
480553
(database, FilePosition { file_id, offset })
481554
}
482555

556+
#[track_caller]
483557
fn check(ra_fixture: &str, expect: Expect) {
484558
let fixture = format!(
485559
r#"
@@ -931,6 +1005,119 @@ fn main() {
9311005
);
9321006
}
9331007

1008+
#[test]
1009+
fn tuple_struct_pat() {
1010+
check(
1011+
r#"
1012+
/// A cool tuple struct
1013+
struct S(u32, i32);
1014+
fn main() {
1015+
let S(0, $0);
1016+
}
1017+
"#,
1018+
expect![[r#"
1019+
A cool tuple struct
1020+
------
1021+
struct S (u32, i32)
1022+
--- ^^^
1023+
"#]],
1024+
);
1025+
}
1026+
1027+
#[test]
1028+
fn tuple_struct_pat_rest() {
1029+
check(
1030+
r#"
1031+
/// A cool tuple struct
1032+
struct S(u32, i32, f32, u16);
1033+
fn main() {
1034+
let S(0, .., $0);
1035+
}
1036+
"#,
1037+
expect![[r#"
1038+
A cool tuple struct
1039+
------
1040+
struct S (u32, i32, f32, u16)
1041+
--- --- --- ^^^
1042+
"#]],
1043+
);
1044+
check(
1045+
r#"
1046+
/// A cool tuple struct
1047+
struct S(u32, i32, f32, u16, u8);
1048+
fn main() {
1049+
let S(0, .., $0, 0);
1050+
}
1051+
"#,
1052+
expect![[r#"
1053+
A cool tuple struct
1054+
------
1055+
struct S (u32, i32, f32, u16, u8)
1056+
--- --- --- ^^^ --
1057+
"#]],
1058+
);
1059+
check(
1060+
r#"
1061+
/// A cool tuple struct
1062+
struct S(u32, i32, f32, u16);
1063+
fn main() {
1064+
let S($0, .., 1);
1065+
}
1066+
"#,
1067+
expect![[r#"
1068+
A cool tuple struct
1069+
------
1070+
struct S (u32, i32, f32, u16)
1071+
^^^ --- --- ---
1072+
"#]],
1073+
);
1074+
check(
1075+
r#"
1076+
/// A cool tuple struct
1077+
struct S(u32, i32, f32, u16, u8);
1078+
fn main() {
1079+
let S(1, .., 1, $0, 2);
1080+
}
1081+
"#,
1082+
expect![[r#"
1083+
A cool tuple struct
1084+
------
1085+
struct S (u32, i32, f32, u16, u8)
1086+
--- --- --- ^^^ --
1087+
"#]],
1088+
);
1089+
check(
1090+
r#"
1091+
/// A cool tuple struct
1092+
struct S(u32, i32, f32, u16);
1093+
fn main() {
1094+
let S(1, $0.., 1);
1095+
}
1096+
"#,
1097+
expect![[r#"
1098+
A cool tuple struct
1099+
------
1100+
struct S (u32, i32, f32, u16)
1101+
--- ^^^ --- ---
1102+
"#]],
1103+
);
1104+
check(
1105+
r#"
1106+
/// A cool tuple struct
1107+
struct S(u32, i32, f32, u16);
1108+
fn main() {
1109+
let S(1, ..$0, 1);
1110+
}
1111+
"#,
1112+
expect![[r#"
1113+
A cool tuple struct
1114+
------
1115+
struct S (u32, i32, f32, u16)
1116+
--- ^^^ --- ---
1117+
"#]],
1118+
);
1119+
}
1120+
9341121
#[test]
9351122
fn generic_struct() {
9361123
check(

crates/parser/src/grammar/patterns.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,15 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
431431

432432
fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) {
433433
while !p.at(EOF) && !p.at(ket) {
434-
if !p.at_ts(PAT_TOP_FIRST) {
435-
p.error("expected a pattern");
436-
break;
437-
}
438-
439434
pattern_top(p);
440-
if !p.at(ket) {
441-
p.expect(T![,]);
435+
if !p.at(T![,]) {
436+
if p.at_ts(PAT_TOP_FIRST) {
437+
p.error(format!("expected {:?}, got {:?}", T![,], p.current()));
438+
} else {
439+
break;
440+
}
441+
} else {
442+
p.bump(T![,]);
442443
}
443444
}
444445
}

0 commit comments

Comments
 (0)