From 1335608dae47cf8baa1dd2e8809264b030e85c3e Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Wed, 1 Apr 2020 00:50:45 +0300 Subject: Unite record completion logic into a single module --- crates/ra_ide/src/completion.rs | 66 +--- crates/ra_ide/src/completion/complete_record.rs | 411 +++++++++++++++++++++ .../src/completion/complete_record_literal.rs | 224 ----------- .../src/completion/complete_record_pattern.rs | 162 -------- 4 files changed, 413 insertions(+), 450 deletions(-) create mode 100644 crates/ra_ide/src/completion/complete_record.rs delete mode 100644 crates/ra_ide/src/completion/complete_record_literal.rs delete mode 100644 crates/ra_ide/src/completion/complete_record_pattern.rs (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 67dfd6f2e..93157bbba 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -5,8 +5,7 @@ mod completion_context; mod presentation; mod complete_dot; -mod complete_record_literal; -mod complete_record_pattern; +mod complete_record; mod complete_pattern; mod complete_fn_param; mod complete_keyword; @@ -32,12 +31,6 @@ use crate::{ pub use crate::completion::completion_item::{ CompletionItem, CompletionItemKind, InsertTextFormat, }; -use either::Either; -use hir::{StructField, Type}; -use ra_syntax::{ - ast::{self, NameOwner}, - SmolStr, -}; #[derive(Clone, Debug, PartialEq, Eq)] pub struct CompletionConfig { @@ -95,8 +88,7 @@ pub(crate) fn completions( complete_path::complete_path(&mut acc, &ctx); complete_scope::complete_scope(&mut acc, &ctx); complete_dot::complete_dot(&mut acc, &ctx); - complete_record_literal::complete_record_literal(&mut acc, &ctx); - complete_record_pattern::complete_record_pattern(&mut acc, &ctx); + complete_record::complete_record(&mut acc, &ctx); complete_pattern::complete_pattern(&mut acc, &ctx); complete_postfix::complete_postfix(&mut acc, &ctx); complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); @@ -104,57 +96,3 @@ pub(crate) fn completions( Some(acc) } - -pub(crate) fn get_missing_fields( - ctx: &CompletionContext, - record: Either<&ast::RecordLit, &ast::RecordPat>, -) -> Option> { - let (ty, variant) = match record { - Either::Left(record_lit) => ( - ctx.sema.type_of_expr(&record_lit.clone().into())?, - ctx.sema.resolve_record_literal(record_lit)?, - ), - Either::Right(record_pat) => ( - ctx.sema.type_of_pat(&record_pat.clone().into())?, - ctx.sema.resolve_record_pattern(record_pat)?, - ), - }; - - let already_present_names = get_already_present_names(record); - Some( - ty.variant_fields(ctx.db, variant) - .into_iter() - .filter(|(field, _)| { - !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string())) - }) - .collect(), - ) -} - -fn get_already_present_names(record: Either<&ast::RecordLit, &ast::RecordPat>) -> Vec { - // TODO kb have a single match - match record { - Either::Left(record_lit) => record_lit - .record_field_list() - .map(|field_list| field_list.fields()) - .map(|fields| { - fields - .into_iter() - .filter_map(|field| field.name_ref()) - .map(|name_ref| name_ref.text().clone()) - .collect() - }) - .unwrap_or_default(), - Either::Right(record_pat) => record_pat - .record_field_pat_list() - .map(|pat_list| pat_list.bind_pats()) - .map(|bind_pats| { - bind_pats - .into_iter() - .filter_map(|pat| pat.name()) - .map(|name| name.text().clone()) - .collect() - }) - .unwrap_or_default(), - } -} diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs new file mode 100644 index 000000000..01dd8c6db --- /dev/null +++ b/crates/ra_ide/src/completion/complete_record.rs @@ -0,0 +1,411 @@ +//! Complete fields in record literals and patterns. +use crate::completion::{CompletionContext, Completions}; +use ra_syntax::{ast, ast::NameOwner, SmolStr}; + +pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { + let (ty, variant, already_present_fields) = + match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { + (None, None) => return None, + (Some(_), Some(_)) => panic!("A record cannot be both a literal and a pattern"), + (Some(record_pat), _) => ( + ctx.sema.type_of_pat(&record_pat.clone().into())?, + ctx.sema.resolve_record_pattern(record_pat)?, + pattern_ascribed_fields(record_pat), + ), + (_, Some(record_lit)) => ( + ctx.sema.type_of_expr(&record_lit.clone().into())?, + ctx.sema.resolve_record_literal(record_lit)?, + literal_ascribed_fields(record_lit), + ), + }; + + for (field, field_ty) in ty.variant_fields(ctx.db, variant).into_iter().filter(|(field, _)| { + // FIXME: already_present_names better be `Vec` + !already_present_fields.contains(&SmolStr::from(field.name(ctx.db).to_string())) + }) { + acc.add_field(ctx, field, &field_ty); + } + Some(()) +} + +fn literal_ascribed_fields(record_lit: &ast::RecordLit) -> Vec { + record_lit + .record_field_list() + .map(|field_list| field_list.fields()) + .map(|fields| { + fields + .into_iter() + .filter_map(|field| field.name_ref()) + .map(|name_ref| name_ref.text().clone()) + .collect() + }) + .unwrap_or_default() +} + +fn pattern_ascribed_fields(record_pat: &ast::RecordPat) -> Vec { + record_pat + .record_field_pat_list() + .map(|pat_list| { + pat_list + .record_field_pats() + .filter_map(|fild_pat| fild_pat.name()) + .chain(pat_list.bind_pats().filter_map(|bind_pat| bind_pat.name())) + .map(|name| name.text().clone()) + .collect() + }) + .unwrap_or_default() +} + +#[cfg(test)] +mod tests { + mod record_lit_tests { + use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + use insta::assert_debug_snapshot; + + fn complete(code: &str) -> Vec { + do_completion(code, CompletionKind::Reference) + } + + #[test] + fn test_record_pattern_field() { + let completions = complete( + r" + struct S { foo: u32 } + + fn process(f: S) { + match f { + S { f<|>: 92 } => (), + } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "foo", + source_range: [117; 118), + delete: [117; 118), + insert: "foo", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_pattern_enum_variant() { + let completions = complete( + r" + enum E { + S { foo: u32, bar: () } + } + + fn process(e: E) { + match e { + E::S { <|> } => (), + } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "bar", + source_range: [161; 161), + delete: [161; 161), + insert: "bar", + kind: Field, + detail: "()", + }, + CompletionItem { + label: "foo", + source_range: [161; 161), + delete: [161; 161), + insert: "foo", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_pattern_field_in_simple_macro() { + let completions = complete( + r" + macro_rules! m { ($e:expr) => { $e } } + struct S { foo: u32 } + + fn process(f: S) { + m!(match f { + S { f<|>: 92 } => (), + }) + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "foo", + source_range: [171; 172), + delete: [171; 172), + insert: "foo", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn only_missing_fields_are_completed_in_destruct_pats() { + let completions = complete( + r" + struct S { + foo1: u32, + foo2: u32, + bar: u32, + baz: u32, + } + + fn main() { + let s = S { + foo1: 1, + foo2: 2, + bar: 3, + baz: 4, + }; + if let S { foo1, foo2: a, <|> } = s {} + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "bar", + source_range: [372; 372), + delete: [372; 372), + insert: "bar", + kind: Field, + detail: "u32", + }, + CompletionItem { + label: "baz", + source_range: [372; 372), + delete: [372; 372), + insert: "baz", + kind: Field, + detail: "u32", + }, + ] + "###); + } + } + + mod record_pat_tests { + use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + use insta::assert_debug_snapshot; + + fn complete(code: &str) -> Vec { + do_completion(code, CompletionKind::Reference) + } + + #[test] + fn test_record_literal_deprecated_field() { + let completions = complete( + r" + struct A { + #[deprecated] + the_field: u32, + } + fn foo() { + A { the<|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "the_field", + source_range: [142; 145), + delete: [142; 145), + insert: "the_field", + kind: Field, + detail: "u32", + deprecated: true, + }, + ] + "###); + } + + #[test] + fn test_record_literal_field() { + let completions = complete( + r" + struct A { the_field: u32 } + fn foo() { + A { the<|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "the_field", + source_range: [83; 86), + delete: [83; 86), + insert: "the_field", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_literal_enum_variant() { + let completions = complete( + r" + enum E { + A { a: u32 } + } + fn foo() { + let _ = E::A { <|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "a", + source_range: [119; 119), + delete: [119; 119), + insert: "a", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_literal_two_structs() { + let completions = complete( + r" + struct A { a: u32 } + struct B { b: u32 } + + fn foo() { + let _: A = B { <|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "b", + source_range: [119; 119), + delete: [119; 119), + insert: "b", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_literal_generic_struct() { + let completions = complete( + r" + struct A { a: T } + + fn foo() { + let _: A = A { <|> } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "a", + source_range: [93; 93), + delete: [93; 93), + insert: "a", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn test_record_literal_field_in_simple_macro() { + let completions = complete( + r" + macro_rules! m { ($e:expr) => { $e } } + struct A { the_field: u32 } + fn foo() { + m!(A { the<|> }) + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "the_field", + source_range: [137; 140), + delete: [137; 140), + insert: "the_field", + kind: Field, + detail: "u32", + }, + ] + "###); + } + + #[test] + fn only_missing_fields_are_completed() { + let completions = complete( + r" + struct S { + foo1: u32, + foo2: u32, + bar: u32, + baz: u32, + } + + fn main() { + let foo1 = 1; + let s = S { + foo1, + foo2: 5, + <|> + } + } + ", + ); + assert_debug_snapshot!(completions, @r###" + [ + CompletionItem { + label: "bar", + source_range: [302; 302), + delete: [302; 302), + insert: "bar", + kind: Field, + detail: "u32", + }, + CompletionItem { + label: "baz", + source_range: [302; 302), + delete: [302; 302), + insert: "baz", + kind: Field, + detail: "u32", + }, + ] + "###); + } + } +} diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs deleted file mode 100644 index 8b67d3ba2..000000000 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ /dev/null @@ -1,224 +0,0 @@ -//! FIXME: write short doc here - -use super::get_missing_fields; -use crate::completion::{CompletionContext, Completions}; -use either::Either; - -/// Complete fields in fields literals. -pub(super) fn complete_record_literal( - acc: &mut Completions, - ctx: &CompletionContext, -) -> Option<()> { - let record_lit = ctx.record_lit_syntax.as_ref()?; - for (field, field_ty) in get_missing_fields(ctx, Either::Left(record_lit))? { - acc.add_field(ctx, field, &field_ty); - } - Some(()) -} - -#[cfg(test)] -mod tests { - use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; - use insta::assert_debug_snapshot; - - fn complete(code: &str) -> Vec { - do_completion(code, CompletionKind::Reference) - } - - #[test] - fn test_record_literal_deprecated_field() { - let completions = complete( - r" - struct A { - #[deprecated] - the_field: u32, - } - fn foo() { - A { the<|> } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "the_field", - source_range: [142; 145), - delete: [142; 145), - insert: "the_field", - kind: Field, - detail: "u32", - deprecated: true, - }, - ] - "###); - } - - #[test] - fn test_record_literal_field() { - let completions = complete( - r" - struct A { the_field: u32 } - fn foo() { - A { the<|> } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "the_field", - source_range: [83; 86), - delete: [83; 86), - insert: "the_field", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_literal_enum_variant() { - let completions = complete( - r" - enum E { - A { a: u32 } - } - fn foo() { - let _ = E::A { <|> } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "a", - source_range: [119; 119), - delete: [119; 119), - insert: "a", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_literal_two_structs() { - let completions = complete( - r" - struct A { a: u32 } - struct B { b: u32 } - - fn foo() { - let _: A = B { <|> } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "b", - source_range: [119; 119), - delete: [119; 119), - insert: "b", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_literal_generic_struct() { - let completions = complete( - r" - struct A { a: T } - - fn foo() { - let _: A = A { <|> } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "a", - source_range: [93; 93), - delete: [93; 93), - insert: "a", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_literal_field_in_simple_macro() { - let completions = complete( - r" - macro_rules! m { ($e:expr) => { $e } } - struct A { the_field: u32 } - fn foo() { - m!(A { the<|> }) - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "the_field", - source_range: [137; 140), - delete: [137; 140), - insert: "the_field", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn only_missing_fields_are_completed() { - let completions = complete( - r" - struct S { - foo1: u32, - foo2: u32, - bar: u32, - baz: u32, - } - - fn main() { - let foo1 = 1; - let s = S { - foo1, - foo2: 5, - <|> - } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "bar", - source_range: [302; 302), - delete: [302; 302), - insert: "bar", - kind: Field, - detail: "u32", - }, - CompletionItem { - label: "baz", - source_range: [302; 302), - delete: [302; 302), - insert: "baz", - kind: Field, - detail: "u32", - }, - ] - "###); - } -} diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs deleted file mode 100644 index f94dced04..000000000 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ /dev/null @@ -1,162 +0,0 @@ -//! FIXME: write short doc here - -use super::get_missing_fields; -use crate::completion::{CompletionContext, Completions}; -use either::Either; - -pub(super) fn complete_record_pattern( - acc: &mut Completions, - ctx: &CompletionContext, -) -> Option<()> { - let record_pat = ctx.record_lit_pat.as_ref()?; - for (field, field_ty) in get_missing_fields(ctx, Either::Right(record_pat))? { - acc.add_field(ctx, field, &field_ty); - } - Some(()) -} - -#[cfg(test)] -mod tests { - use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; - use insta::assert_debug_snapshot; - - fn complete(code: &str) -> Vec { - do_completion(code, CompletionKind::Reference) - } - - #[test] - fn test_record_pattern_field() { - let completions = complete( - r" - struct S { foo: u32 } - - fn process(f: S) { - match f { - S { f<|>: 92 } => (), - } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "foo", - source_range: [117; 118), - delete: [117; 118), - insert: "foo", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_pattern_enum_variant() { - let completions = complete( - r" - enum E { - S { foo: u32, bar: () } - } - - fn process(e: E) { - match e { - E::S { <|> } => (), - } - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "bar", - source_range: [161; 161), - delete: [161; 161), - insert: "bar", - kind: Field, - detail: "()", - }, - CompletionItem { - label: "foo", - source_range: [161; 161), - delete: [161; 161), - insert: "foo", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn test_record_pattern_field_in_simple_macro() { - let completions = complete( - r" - macro_rules! m { ($e:expr) => { $e } } - struct S { foo: u32 } - - fn process(f: S) { - m!(match f { - S { f<|>: 92 } => (), - }) - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "foo", - source_range: [171; 172), - delete: [171; 172), - insert: "foo", - kind: Field, - detail: "u32", - }, - ] - "###); - } - - #[test] - fn only_missing_fields_are_completed_in_destruct_pats() { - let completions = complete( - r" - struct S { - foo1: u32, - foo2: u32, - bar: u32, - baz: u32, - } - - fn main() { - let s = S { - foo1: 1, - foo2: 2, - bar: 3, - baz: 4, - }; - if let S { foo1, foo2: a, <|> } = s {} - } - ", - ); - assert_debug_snapshot!(completions, @r###" - [ - CompletionItem { - label: "bar", - source_range: [372; 372), - delete: [372; 372), - insert: "bar", - kind: Field, - detail: "u32", - }, - CompletionItem { - label: "baz", - source_range: [372; 372), - delete: [372; 372), - insert: "baz", - kind: Field, - detail: "u32", - }, - ] - "###); - } -} -- cgit v1.2.3