//! Complete fields in record literals and patterns.
-use ide_db::{helpers::FamousDefs, SymbolKind};
-use syntax::ast::Expr;
+use ide_db::SymbolKind;
+use syntax::{ast::Expr, T};
-use crate::{item::CompletionKind, CompletionContext, CompletionItem, Completions};
+use crate::{
+ patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind, Completions,
+};
pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
- let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
- (None, None) => return None,
- (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
- (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
- (_, Some(record_lit)) => {
- let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_lit.clone()));
- let default_trait = FamousDefs(&ctx.sema, ctx.krate).core_default_Default();
- let impl_default_trait = default_trait
- .zip(ty)
- .map_or(false, |(default_trait, ty)| ty.impls_trait(ctx.db, default_trait, &[]));
-
- let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
- if impl_default_trait && !missing_fields.is_empty() {
+ let missing_fields = match &ctx.completion_location {
+ Some(
+ ImmediateLocation::RecordExpr(record_expr)
+ | ImmediateLocation::RecordExprUpdate(record_expr),
+ ) => {
+ let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));
+ let default_trait = ctx.famous_defs().core_default_Default();
+ let impl_default_trait = default_trait.zip(ty).map_or(false, |(default_trait, ty)| {
+ ty.original.impls_trait(ctx.db, default_trait, &[])
+ });
+
+ let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
+ if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
let completion_text = "..Default::default()";
- let mut item = CompletionItem::new(
- CompletionKind::Snippet,
- ctx.source_range(),
- completion_text,
- );
+ let mut item =
+ CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
let completion_text =
completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
- item.insert_text(completion_text).kind(SymbolKind::Field);
+ item.insert_text(completion_text);
item.add_to(acc);
}
-
+ if ctx.previous_token_is(T![.]) {
+ let mut item =
+ CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
+ item.insert_text(".");
+ item.add_to(acc);
+ return None;
+ }
missing_fields
}
+ Some(ImmediateLocation::RecordPat(record_pat)) => {
+ ctx.sema.record_pattern_missing_fields(record_pat)
+ }
+ _ => return None,
};
for (field, ty) in missing_fields {
- acc.add_field(ctx, field, &ty);
+ acc.add_field(ctx, None, field, &ty);
}
Some(())
}
-#[cfg(test)]
-mod tests {
- use expect_test::{expect, Expect};
- use ide_db::helpers::FamousDefs;
-
- use crate::{
- test_utils::{self, completion_list},
- CompletionKind,
- };
-
- fn check(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(ra_fixture, CompletionKind::Reference);
- expect.assert_eq(&actual);
+pub(crate) fn complete_record_literal(
+ acc: &mut Completions,
+ ctx: &CompletionContext,
+) -> Option<()> {
+ if !ctx.expects_expression() {
+ return None;
}
- fn check_snippet(ra_fixture: &str, expect: Expect) {
- let actual = completion_list(
- &format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE),
- CompletionKind::Snippet,
- );
- expect.assert_eq(&actual);
- }
+ if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? {
+ let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
- fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
- test_utils::check_edit(
- what,
- &format!(
- "//- /main.rs crate:main deps:core{}\n{}",
- ra_fixture_before,
- FamousDefs::FIXTURE,
- ),
- &(ra_fixture_after.to_owned() + "\n"),
- );
- }
+ let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
- #[test]
- fn test_record_literal_field_default() {
- let test_code = r#"
-struct S { foo: u32, bar: usize }
-
-impl core::default::Default for S {
- fn default() -> Self {
- S {
- foo: 0,
- bar: 0,
- }
+ acc.add_struct_literal(ctx, strukt, path, None);
}
-}
-fn process(f: S) {
- let other = S {
- foo: 5,
- .$0
- };
+ Some(())
}
-"#;
- check(
- test_code,
- expect![[r#"
- fd bar usize
- "#]],
- );
- check_snippet(
- test_code,
- expect![[r#"
- fd ..Default::default()
- "#]],
- );
- }
+#[cfg(test)]
+mod tests {
+ use crate::tests::check_edit;
#[test]
- fn test_record_literal_field_default_completion() {
+ fn literal_struct_completion_edit() {
check_edit(
- "..Default::default()",
+ "FooDesc {…}",
r#"
-struct S { foo: u32, bar: usize }
+struct FooDesc { pub bar: bool }
-impl core::default::Default for S {
- fn default() -> Self {
- S {
- foo: 0,
- bar: 0,
- }
- }
-}
+fn create_foo(foo_desc: &FooDesc) -> () { () }
-fn process(f: S) {
- let other = S {
- foo: 5,
- .$0
- };
+fn baz() {
+ let foo = create_foo(&$0);
}
-"#,
+ "#,
r#"
-struct S { foo: u32, bar: usize }
+struct FooDesc { pub bar: bool }
-impl core::default::Default for S {
- fn default() -> Self {
- S {
- foo: 0,
- bar: 0,
- }
- }
-}
+fn create_foo(foo_desc: &FooDesc) -> () { () }
-fn process(f: S) {
- let other = S {
- foo: 5,
- ..Default::default()
- };
+fn baz() {
+ let foo = create_foo(&FooDesc { bar: ${1:()} }$0);
}
-"#,
- );
+ "#,
+ )
}
#[test]
- fn test_record_literal_field_without_default() {
- let test_code = r#"
-struct S { foo: u32, bar: usize }
-
-fn process(f: S) {
- let other = S {
- foo: 5,
- .$0
- };
-}
-"#;
- check(
- test_code,
- expect![[r#"
- fd bar usize
- "#]],
- );
-
- check_snippet(test_code, expect![[r#""#]]);
+ fn literal_struct_completion_from_sub_modules() {
+ check_edit(
+ "Struct {…}",
+ r#"
+mod submod {
+ pub struct Struct {
+ pub a: u64,
}
+}
- #[test]
- fn test_record_pattern_field() {
- check(
+fn f() -> submod::Struct {
+ Stru$0
+}
+ "#,
r#"
-struct S { foo: u32 }
-
-fn process(f: S) {
- match f {
- S { f$0: 92 } => (),
+mod submod {
+ pub struct Struct {
+ pub a: u64,
}
}
-"#,
- expect![[r#"
- fd foo u32
- "#]],
- );
+
+fn f() -> submod::Struct {
+ submod::Struct { a: ${1:()} }$0
+}
+ "#,
+ )
}
#[test]
- fn test_record_pattern_enum_variant() {
- check(
+ fn literal_struct_complexion_module() {
+ check_edit(
+ "FooDesc {…}",
r#"
-enum E { S { foo: u32, bar: () } }
+mod _69latrick {
+ pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
+ pub fn create_foo(foo_desc: &FooDesc) -> () { () }
+}
-fn process(e: E) {
- match e {
- E::S { $0 } => (),
- }
+fn baz() {
+ use _69latrick::*;
+
+ let foo = create_foo(&$0);
+}
+ "#,
+ r#"
+mod _69latrick {
+ pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
+ pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}
-"#,
- expect![[r#"
- fd foo u32
- fd bar ()
- "#]],
- );
- }
- #[test]
- fn test_record_pattern_field_in_simple_macro() {
- check(
- r"
-macro_rules! m { ($e:expr) => { $e } }
-struct S { foo: u32 }
-
-fn process(f: S) {
- m!(match f {
- S { f$0: 92 } => (),
- })
+fn baz() {
+ use _69latrick::*;
+
+ let foo = create_foo(&FooDesc { six: ${1:()}, neuf: ${2:()}, bar: ${3:()} }$0);
}
-",
- expect![[r#"
- fd foo u32
- "#]],
+ "#,
);
}
#[test]
- fn only_missing_fields_are_completed_in_destruct_pats() {
- check(
+ fn default_completion_edit() {
+ check_edit(
+ "..Default::default()",
r#"
-struct S {
- foo1: u32, foo2: u32,
- bar: u32, baz: u32,
+//- minicore: default
+struct Struct { foo: u32, bar: usize }
+
+impl Default for Struct {
+ fn default() -> Self {}
}
-fn main() {
- let s = S {
- foo1: 1, foo2: 2,
- bar: 3, baz: 4,
+fn foo() {
+ let other = Struct {
+ foo: 5,
+ .$0
};
- if let S { foo1, foo2: a, $0 } = s {}
}
"#,
- expect![[r#"
- fd bar u32
- fd baz u32
- "#]],
- );
- }
-
- #[test]
- fn test_record_literal_field() {
- check(
r#"
-struct A { the_field: u32 }
-fn foo() {
- A { the$0 }
+struct Struct { foo: u32, bar: usize }
+
+impl Default for Struct {
+ fn default() -> Self {}
}
-"#,
- expect![[r#"
- fd the_field u32
- "#]],
- );
- }
- #[test]
- fn test_record_literal_enum_variant() {
- check(
- r#"
-enum E { A { a: u32 } }
fn foo() {
- let _ = E::A { $0 }
+ let other = Struct {
+ foo: 5,
+ ..Default::default()
+ };
}
"#,
- expect![[r#"
- fd a u32
- "#]],
);
- }
-
- #[test]
- fn test_record_literal_two_structs() {
- check(
+ check_edit(
+ "..Default::default()",
r#"
-struct A { a: u32 }
-struct B { b: u32 }
+//- minicore: default
+struct Struct { foo: u32, bar: usize }
+
+impl Default for Struct {
+ fn default() -> Self {}
+}
fn foo() {
- let _: A = B { $0 }
+ let other = Struct {
+ foo: 5,
+ $0
+ };
}
"#,
- expect![[r#"
- fd b u32
- "#]],
- );
- }
-
- #[test]
- fn test_record_literal_generic_struct() {
- check(
r#"
-struct A<T> { a: T }
+struct Struct { foo: u32, bar: usize }
-fn foo() {
- let _: A<u32> = A { $0 }
+impl Default for Struct {
+ fn default() -> Self {}
}
-"#,
- expect![[r#"
- fd a u32
- "#]],
- );
- }
- #[test]
- fn test_record_literal_field_in_simple_macro() {
- check(
- r#"
-macro_rules! m { ($e:expr) => { $e } }
-struct A { the_field: u32 }
fn foo() {
- m!(A { the$0 })
+ let other = Struct {
+ foo: 5,
+ ..Default::default()
+ };
}
"#,
- expect![[r#"
- fd the_field u32
- "#]],
);
- }
-
- #[test]
- fn only_missing_fields_are_completed() {
- check(
+ check_edit(
+ "..Default::default()",
r#"
-struct S {
- foo1: u32, foo2: u32,
- bar: u32, baz: u32,
+//- minicore: default
+struct Struct { foo: u32, bar: usize }
+
+impl Default for Struct {
+ fn default() -> Self {}
}
-fn main() {
- let foo1 = 1;
- let s = S { foo1, foo2: 5, $0 }
+fn foo() {
+ let other = Struct {
+ foo: 5,
+ ..$0
+ };
}
"#,
- expect![[r#"
- fd bar u32
- fd baz u32
- "#]],
- );
- }
-
- #[test]
- fn completes_functional_update() {
- check(
r#"
-struct S { foo1: u32, foo2: u32 }
+struct Struct { foo: u32, bar: usize }
+
+impl Default for Struct {
+ fn default() -> Self {}
+}
-fn main() {
- let foo1 = 1;
- let s = S { foo1, $0 .. loop {} }
+fn foo() {
+ let other = Struct {
+ foo: 5,
+ ..Default::default()
+ };
}
"#,
- expect![[r#"
- fd foo2 u32
- "#]],
);
}
}