diff options
| author | Lukas Wirth <lukastw97@gmail.com> | 2023-03-15 10:17:30 +0100 |
|---|---|---|
| committer | Lukas Wirth <lukastw97@gmail.com> | 2023-03-15 10:17:30 +0100 |
| commit | 55120b3d83ff3608b392e3af80491f1eab2cd238 (patch) | |
| tree | 625928c88d2365f82a7ed7555662a0b8f3abd2c6 | |
| parent | 03a6ab0b62b2fbaefbf39bee6605b3a1050b8083 (diff) | |
| download | rust-55120b3d83ff3608b392e3af80491f1eab2cd238.tar.gz rust-55120b3d83ff3608b392e3af80491f1eab2cd238.zip | |
Add signature help for tuple struct patterns
| -rw-r--r-- | crates/ide/src/signature_help.rs | 223 | ||||
| -rw-r--r-- | crates/parser/src/grammar/patterns.rs | 15 |
2 files changed, 213 insertions, 25 deletions
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 21f92ccddb9..4b2c139f6f4 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -109,7 +109,13 @@ pub(crate) fn signature_help(db: &RootDatabase, position: FilePosition) -> Optio } 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_( sema: &Semantics<'_, RootDatabase>, field_list_children: SyntaxElementChildren, @@ -442,23 +532,6 @@ fn signature_help_for_record_( 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)] mod tests { use std::iter; @@ -480,6 +553,7 @@ mod tests { (database, FilePosition { file_id, offset }) } + #[track_caller] fn check(ra_fixture: &str, expect: Expect) { let fixture = format!( r#" @@ -932,6 +1006,119 @@ fn main() { } #[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] fn generic_struct() { check( r#" diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index abcefffa23f..5f4977886f6 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -431,14 +431,15 @@ fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker { fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) { while !p.at(EOF) && !p.at(ket) { - if !p.at_ts(PAT_TOP_FIRST) { - p.error("expected a pattern"); - break; - } - pattern_top(p); - if !p.at(ket) { - p.expect(T![,]); + if !p.at(T![,]) { + if p.at_ts(PAT_TOP_FIRST) { + p.error(format!("expected {:?}, got {:?}", T![,], p.current())); + } else { + break; + } + } else { + p.bump(T![,]); } } } |
