mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Merge #6964
6964: Add full pattern completions for Struct and Variant patterns r=matklad a=Veykril Just gonna call it full pattern completion as pattern completion is already implemented in a sense by showing idents in pattern position. What this does is basically complete struct and variant patterns where applicable(function params, let statements and refutable pattern locations). This does not replace just completing the corresponding idents of the structs and variants, instead two completions are shown for these, a completion for the ident itself and a completion for the pattern(if the pattern make sense to be used that is). I figured in some cases one would rather type out the pattern manually if it has a lot of fields but you only care about one since this completion would cause one more work in the end since you would have to delete all the extra matched fields again. These completions are tagged as `CompletionKind::Snippet`, not sure if that is the right one here. <details> <summary>some gifs</summary> ![dx2lxgzhj3](https://user-images.githubusercontent.com/3757771/102719967-6987ef80-42f1-11eb-8ae0-8aff53777860.gif) ![EP2E7sJLkB](https://user-images.githubusercontent.com/3757771/102785777-c7264580-439e-11eb-8a64-f142e19fb65b.gif) ![JMNHHWknr9](https://user-images.githubusercontent.com/3757771/102785796-d1e0da80-439e-11eb-934b-218ada31b51c.gif) </details> Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
4a2f60cb7b
8 changed files with 363 additions and 74 deletions
|
@ -19,9 +19,14 @@ use hir::{ModPath, ScopeDef, Type};
|
||||||
use crate::{
|
use crate::{
|
||||||
item::Builder,
|
item::Builder,
|
||||||
render::{
|
render::{
|
||||||
const_::render_const, enum_variant::render_variant, function::render_fn,
|
const_::render_const,
|
||||||
macro_::render_macro, render_field, render_resolution, render_tuple_field,
|
enum_variant::render_variant,
|
||||||
type_alias::render_type_alias, RenderContext,
|
function::render_fn,
|
||||||
|
macro_::render_macro,
|
||||||
|
pattern::{render_struct_pat, render_variant_pat},
|
||||||
|
render_field, render_resolution, render_tuple_field,
|
||||||
|
type_alias::render_type_alias,
|
||||||
|
RenderContext,
|
||||||
},
|
},
|
||||||
CompletionContext, CompletionItem,
|
CompletionContext, CompletionItem,
|
||||||
};
|
};
|
||||||
|
@ -105,6 +110,28 @@ impl Completions {
|
||||||
self.add(item)
|
self.add(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_variant_pat(
|
||||||
|
&mut self,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
variant: hir::Variant,
|
||||||
|
local_name: Option<hir::Name>,
|
||||||
|
) {
|
||||||
|
if let Some(item) = render_variant_pat(RenderContext::new(ctx), variant, local_name) {
|
||||||
|
self.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_struct_pat(
|
||||||
|
&mut self,
|
||||||
|
ctx: &CompletionContext,
|
||||||
|
strukt: hir::Struct,
|
||||||
|
local_name: Option<hir::Name>,
|
||||||
|
) {
|
||||||
|
if let Some(item) = render_struct_pat(RenderContext::new(ctx), strukt, local_name) {
|
||||||
|
self.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
|
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
|
||||||
if let Some(item) = render_const(RenderContext::new(ctx), constant) {
|
if let Some(item) = render_const(RenderContext::new(ctx), constant) {
|
||||||
self.add(item);
|
self.add(item);
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
use crate::{CompletionContext, Completions};
|
use crate::{CompletionContext, Completions};
|
||||||
|
|
||||||
/// Completes constats and paths in patterns.
|
/// Completes constants and paths in patterns.
|
||||||
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_let_pat_binding) {
|
if !(ctx.is_pat_binding_or_const || ctx.is_irrefutable_pat_binding) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ctx.record_pat_syntax.is_some() {
|
if ctx.record_pat_syntax.is_some() {
|
||||||
|
@ -15,20 +15,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
// suggest variants + auto-imports
|
// suggest variants + auto-imports
|
||||||
ctx.scope.process_all_names(&mut |name, res| {
|
ctx.scope.process_all_names(&mut |name, res| {
|
||||||
let add_resolution = match &res {
|
let add_resolution = match &res {
|
||||||
hir::ScopeDef::ModuleDef(def) => {
|
hir::ScopeDef::ModuleDef(def) => match def {
|
||||||
if ctx.is_irrefutable_let_pat_binding {
|
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
|
||||||
matches!(def, hir::ModuleDef::Adt(hir::Adt::Struct(_)))
|
acc.add_struct_pat(ctx, strukt.clone(), Some(name.clone()));
|
||||||
} else {
|
true
|
||||||
matches!(
|
|
||||||
def,
|
|
||||||
hir::ModuleDef::Adt(hir::Adt::Enum(..))
|
|
||||||
| hir::ModuleDef::Adt(hir::Adt::Struct(..))
|
|
||||||
| hir::ModuleDef::Variant(..)
|
|
||||||
| hir::ModuleDef::Const(..)
|
|
||||||
| hir::ModuleDef::Module(..)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
hir::ModuleDef::Variant(variant) if !ctx.is_irrefutable_pat_binding => {
|
||||||
|
acc.add_variant_pat(ctx, variant.clone(), Some(name.clone()));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
hir::ModuleDef::Adt(hir::Adt::Enum(..))
|
||||||
|
| hir::ModuleDef::Variant(..)
|
||||||
|
| hir::ModuleDef::Const(..)
|
||||||
|
| hir::ModuleDef::Module(..) => !ctx.is_irrefutable_pat_binding,
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
hir::ScopeDef::MacroDef(_) => true,
|
hir::ScopeDef::MacroDef(_) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
@ -42,13 +43,21 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
|
||||||
use crate::{test_utils::completion_list, CompletionKind};
|
use crate::{
|
||||||
|
test_utils::{check_edit, completion_list},
|
||||||
|
CompletionKind,
|
||||||
|
};
|
||||||
|
|
||||||
fn check(ra_fixture: &str, expect: Expect) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
let actual = completion_list(ra_fixture, CompletionKind::Reference);
|
let actual = completion_list(ra_fixture, CompletionKind::Reference);
|
||||||
expect.assert_eq(&actual)
|
expect.assert_eq(&actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_snippet(ra_fixture: &str, expect: Expect) {
|
||||||
|
let actual = completion_list(ra_fixture, CompletionKind::Snippet);
|
||||||
|
expect.assert_eq(&actual)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn completes_enum_variants_and_modules() {
|
fn completes_enum_variants_and_modules() {
|
||||||
check(
|
check(
|
||||||
|
@ -69,7 +78,7 @@ fn foo() {
|
||||||
en E
|
en E
|
||||||
ct Z
|
ct Z
|
||||||
st Bar
|
st Bar
|
||||||
ev X ()
|
ev X
|
||||||
md m
|
md m
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -114,4 +123,139 @@ fn foo() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_in_param() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
enum E { X }
|
||||||
|
|
||||||
|
static FOO: E = E::X;
|
||||||
|
struct Bar { f: u32 }
|
||||||
|
|
||||||
|
fn foo(<|>) {
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
st Bar
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_pat_in_let() {
|
||||||
|
check_snippet(
|
||||||
|
r#"
|
||||||
|
struct Bar { f: u32 }
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
let <|>
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
bn Bar Bar { f$1 }$0
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_param_pattern() {
|
||||||
|
check_snippet(
|
||||||
|
r#"
|
||||||
|
struct Foo { bar: String, baz: String }
|
||||||
|
struct Bar(String, String);
|
||||||
|
struct Baz;
|
||||||
|
fn outer(<|>) {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
bn Foo Foo { bar$1, baz$2 }: Foo$0
|
||||||
|
bn Bar Bar($1, $2): Bar$0
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_let_pattern() {
|
||||||
|
check_snippet(
|
||||||
|
r#"
|
||||||
|
struct Foo { bar: String, baz: String }
|
||||||
|
struct Bar(String, String);
|
||||||
|
struct Baz;
|
||||||
|
fn outer() {
|
||||||
|
let <|>
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
bn Foo Foo { bar$1, baz$2 }$0
|
||||||
|
bn Bar Bar($1, $2)$0
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn completes_refutable_pattern() {
|
||||||
|
check_snippet(
|
||||||
|
r#"
|
||||||
|
struct Foo { bar: i32, baz: i32 }
|
||||||
|
struct Bar(String, String);
|
||||||
|
struct Baz;
|
||||||
|
fn outer() {
|
||||||
|
match () {
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
bn Foo Foo { bar$1, baz$2 }$0
|
||||||
|
bn Bar Bar($1, $2)$0
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn omits_private_fields_pat() {
|
||||||
|
check_snippet(
|
||||||
|
r#"
|
||||||
|
mod foo {
|
||||||
|
pub struct Foo { pub bar: i32, baz: i32 }
|
||||||
|
pub struct Bar(pub String, String);
|
||||||
|
pub struct Invisible(String, String);
|
||||||
|
}
|
||||||
|
use foo::*;
|
||||||
|
|
||||||
|
fn outer() {
|
||||||
|
match () {
|
||||||
|
<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
bn Foo Foo { bar$1, .. }$0
|
||||||
|
bn Bar Bar($1, ..)$0
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn only_shows_ident_completion() {
|
||||||
|
check_edit(
|
||||||
|
"Foo",
|
||||||
|
r#"
|
||||||
|
struct Foo(i32);
|
||||||
|
fn main() {
|
||||||
|
match Foo(92) {
|
||||||
|
<|>(92) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct Foo(i32);
|
||||||
|
fn main() {
|
||||||
|
match Foo(92) {
|
||||||
|
Foo(92) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub(crate) struct CompletionContext<'a> {
|
||||||
/// If a name-binding or reference to a const in a pattern.
|
/// If a name-binding or reference to a const in a pattern.
|
||||||
/// Irrefutable patterns (like let) are excluded.
|
/// Irrefutable patterns (like let) are excluded.
|
||||||
pub(super) is_pat_binding_or_const: bool,
|
pub(super) is_pat_binding_or_const: bool,
|
||||||
pub(super) is_irrefutable_let_pat_binding: bool,
|
pub(super) is_irrefutable_pat_binding: bool,
|
||||||
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
/// A single-indent path, like `foo`. `::foo` should not be considered a trivial path.
|
||||||
pub(super) is_trivial_path: bool,
|
pub(super) is_trivial_path: bool,
|
||||||
/// If not a trivial path, the prefix (qualifier).
|
/// If not a trivial path, the prefix (qualifier).
|
||||||
|
@ -147,7 +147,7 @@ impl<'a> CompletionContext<'a> {
|
||||||
active_parameter: ActiveParameter::at(db, position),
|
active_parameter: ActiveParameter::at(db, position),
|
||||||
is_param: false,
|
is_param: false,
|
||||||
is_pat_binding_or_const: false,
|
is_pat_binding_or_const: false,
|
||||||
is_irrefutable_let_pat_binding: false,
|
is_irrefutable_pat_binding: false,
|
||||||
is_trivial_path: false,
|
is_trivial_path: false,
|
||||||
path_qual: None,
|
path_qual: None,
|
||||||
after_if: false,
|
after_if: false,
|
||||||
|
@ -327,14 +327,19 @@ impl<'a> CompletionContext<'a> {
|
||||||
if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
|
if bind_pat.syntax().parent().and_then(ast::RecordPatFieldList::cast).is_some() {
|
||||||
self.is_pat_binding_or_const = false;
|
self.is_pat_binding_or_const = false;
|
||||||
}
|
}
|
||||||
if let Some(let_stmt) = bind_pat.syntax().ancestors().find_map(ast::LetStmt::cast) {
|
if let Some(Some(pat)) = bind_pat.syntax().ancestors().find_map(|node| {
|
||||||
if let Some(pat) = let_stmt.pat() {
|
match_ast! {
|
||||||
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range())
|
match node {
|
||||||
{
|
ast::LetStmt(it) => Some(it.pat()),
|
||||||
self.is_pat_binding_or_const = false;
|
ast::Param(it) => Some(it.pat()),
|
||||||
self.is_irrefutable_let_pat_binding = true;
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}) {
|
||||||
|
if pat.syntax().text_range().contains_range(bind_pat.syntax().text_range()) {
|
||||||
|
self.is_pat_binding_or_const = false;
|
||||||
|
self.is_irrefutable_pat_binding = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if is_node::<ast::Param>(name.syntax()) {
|
if is_node::<ast::Param>(name.syntax()) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub(crate) mod macro_;
|
||||||
pub(crate) mod function;
|
pub(crate) mod function;
|
||||||
pub(crate) mod enum_variant;
|
pub(crate) mod enum_variant;
|
||||||
pub(crate) mod const_;
|
pub(crate) mod const_;
|
||||||
|
pub(crate) mod pattern;
|
||||||
pub(crate) mod type_alias;
|
pub(crate) mod type_alias;
|
||||||
|
|
||||||
mod builder_ext;
|
mod builder_ext;
|
||||||
|
@ -159,6 +160,12 @@ impl<'a> Render<'a> {
|
||||||
let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
|
let item = render_fn(self.ctx, import_to_add, Some(local_name), *func);
|
||||||
return Some(item);
|
return Some(item);
|
||||||
}
|
}
|
||||||
|
ScopeDef::ModuleDef(Variant(_))
|
||||||
|
if self.ctx.completion.is_pat_binding_or_const
|
||||||
|
| self.ctx.completion.is_irrefutable_pat_binding =>
|
||||||
|
{
|
||||||
|
CompletionItemKind::EnumVariant
|
||||||
|
}
|
||||||
ScopeDef::ModuleDef(Variant(var)) => {
|
ScopeDef::ModuleDef(Variant(var)) => {
|
||||||
let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
|
let item = render_variant(self.ctx, import_to_add, Some(local_name), *var, None);
|
||||||
return Some(item);
|
return Some(item);
|
||||||
|
|
|
@ -34,7 +34,6 @@ impl Builder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ctx.is_pattern_call {
|
if ctx.is_pattern_call {
|
||||||
mark::hit!(dont_duplicate_pattern_parens);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ctx.is_call {
|
if ctx.is_call {
|
||||||
|
|
|
@ -124,51 +124,6 @@ use Option::*;
|
||||||
fn main() -> Option<i32> {
|
fn main() -> Option<i32> {
|
||||||
Some($0)
|
Some($0)
|
||||||
}
|
}
|
||||||
"#,
|
|
||||||
);
|
|
||||||
check_edit(
|
|
||||||
"Some",
|
|
||||||
r#"
|
|
||||||
enum Option<T> { Some(T), None }
|
|
||||||
use Option::*;
|
|
||||||
fn main(value: Option<i32>) {
|
|
||||||
match value {
|
|
||||||
Som<|>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
r#"
|
|
||||||
enum Option<T> { Some(T), None }
|
|
||||||
use Option::*;
|
|
||||||
fn main(value: Option<i32>) {
|
|
||||||
match value {
|
|
||||||
Some($0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn dont_duplicate_pattern_parens() {
|
|
||||||
mark::check!(dont_duplicate_pattern_parens);
|
|
||||||
check_edit(
|
|
||||||
"Var",
|
|
||||||
r#"
|
|
||||||
enum E { Var(i32) }
|
|
||||||
fn main() {
|
|
||||||
match E::Var(92) {
|
|
||||||
E::<|>(92) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
r#"
|
|
||||||
enum E { Var(i32) }
|
|
||||||
fn main() {
|
|
||||||
match E::Var(92) {
|
|
||||||
E::Var(92) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
148
crates/completion/src/render/pattern.rs
Normal file
148
crates/completion/src/render/pattern.rs
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
//! Renderer for patterns.
|
||||||
|
|
||||||
|
use hir::{db::HirDatabase, HasAttrs, HasVisibility, Name, StructKind};
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
config::SnippetCap, item::CompletionKind, render::RenderContext, CompletionItem,
|
||||||
|
CompletionItemKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn visible_fields(
|
||||||
|
ctx: &RenderContext<'_>,
|
||||||
|
fields: &[hir::Field],
|
||||||
|
item: impl HasAttrs,
|
||||||
|
) -> Option<(Vec<hir::Field>, bool)> {
|
||||||
|
let module = ctx.completion.scope.module()?;
|
||||||
|
let n_fields = fields.len();
|
||||||
|
let fields = fields
|
||||||
|
.into_iter()
|
||||||
|
.filter(|field| field.is_visible_from(ctx.db(), module))
|
||||||
|
.copied()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let fields_omitted =
|
||||||
|
n_fields - fields.len() > 0 || item.attrs(ctx.db()).by_key("non_exhaustive").exists();
|
||||||
|
Some((fields, fields_omitted))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn render_struct_pat(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
strukt: hir::Struct,
|
||||||
|
local_name: Option<Name>,
|
||||||
|
) -> Option<CompletionItem> {
|
||||||
|
let _p = profile::span("render_struct_pat");
|
||||||
|
|
||||||
|
let fields = strukt.fields(ctx.db());
|
||||||
|
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, strukt)?;
|
||||||
|
|
||||||
|
if visible_fields.is_empty() {
|
||||||
|
// Matching a struct without matching its fields is pointless, unlike matching a Variant without its fields
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())).to_string();
|
||||||
|
let pat = render_pat(&ctx, &name, strukt.kind(ctx.db()), &visible_fields, fields_omitted)?;
|
||||||
|
|
||||||
|
Some(build_completion(ctx, name, pat, strukt))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn render_variant_pat(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
variant: hir::Variant,
|
||||||
|
local_name: Option<Name>,
|
||||||
|
) -> Option<CompletionItem> {
|
||||||
|
let _p = profile::span("render_variant_pat");
|
||||||
|
|
||||||
|
let fields = variant.fields(ctx.db());
|
||||||
|
let (visible_fields, fields_omitted) = visible_fields(&ctx, &fields, variant)?;
|
||||||
|
|
||||||
|
let name = local_name.unwrap_or_else(|| variant.name(ctx.db())).to_string();
|
||||||
|
let pat = render_pat(&ctx, &name, variant.kind(ctx.db()), &visible_fields, fields_omitted)?;
|
||||||
|
|
||||||
|
Some(build_completion(ctx, name, pat, variant))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_completion(
|
||||||
|
ctx: RenderContext<'_>,
|
||||||
|
name: String,
|
||||||
|
pat: String,
|
||||||
|
item: impl HasAttrs + Copy,
|
||||||
|
) -> CompletionItem {
|
||||||
|
let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name)
|
||||||
|
.kind(CompletionItemKind::Binding)
|
||||||
|
.set_documentation(ctx.docs(item))
|
||||||
|
.set_deprecated(ctx.is_deprecated(item))
|
||||||
|
.detail(&pat);
|
||||||
|
let completion = if let Some(snippet_cap) = ctx.snippet_cap() {
|
||||||
|
completion.insert_snippet(snippet_cap, pat)
|
||||||
|
} else {
|
||||||
|
completion.insert_text(pat)
|
||||||
|
};
|
||||||
|
completion.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_pat(
|
||||||
|
ctx: &RenderContext<'_>,
|
||||||
|
name: &str,
|
||||||
|
kind: StructKind,
|
||||||
|
fields: &[hir::Field],
|
||||||
|
fields_omitted: bool,
|
||||||
|
) -> Option<String> {
|
||||||
|
let mut pat = match kind {
|
||||||
|
StructKind::Tuple if ctx.snippet_cap().is_some() => {
|
||||||
|
render_tuple_as_pat(&fields, &name, fields_omitted)
|
||||||
|
}
|
||||||
|
StructKind::Record => {
|
||||||
|
render_record_as_pat(ctx.db(), ctx.snippet_cap(), &fields, &name, fields_omitted)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ctx.completion.is_param {
|
||||||
|
pat.push(':');
|
||||||
|
pat.push(' ');
|
||||||
|
pat.push_str(&name);
|
||||||
|
}
|
||||||
|
if ctx.snippet_cap().is_some() {
|
||||||
|
pat.push_str("$0");
|
||||||
|
}
|
||||||
|
Some(pat)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_record_as_pat(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
snippet_cap: Option<SnippetCap>,
|
||||||
|
fields: &[hir::Field],
|
||||||
|
name: &str,
|
||||||
|
fields_omitted: bool,
|
||||||
|
) -> String {
|
||||||
|
let fields = fields.iter();
|
||||||
|
if snippet_cap.is_some() {
|
||||||
|
format!(
|
||||||
|
"{name} {{ {}{} }}",
|
||||||
|
fields
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, field)| format!("{}${}", field.name(db), idx + 1))
|
||||||
|
.format(", "),
|
||||||
|
if fields_omitted { ", .." } else { "" },
|
||||||
|
name = name
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"{name} {{ {}{} }}",
|
||||||
|
fields.map(|field| field.name(db)).format(", "),
|
||||||
|
if fields_omitted { ", .." } else { "" },
|
||||||
|
name = name
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_tuple_as_pat(fields: &[hir::Field], name: &str, fields_omitted: bool) -> String {
|
||||||
|
format!(
|
||||||
|
"{name}({}{})",
|
||||||
|
fields.iter().enumerate().map(|(idx, _)| format!("${}", idx + 1)).format(", "),
|
||||||
|
if fields_omitted { ", .." } else { "" },
|
||||||
|
name = name
|
||||||
|
)
|
||||||
|
}
|
|
@ -511,6 +511,10 @@ impl Struct {
|
||||||
db.struct_data(self.id).repr.clone()
|
db.struct_data(self.id).repr.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn kind(self, db: &dyn HirDatabase) -> StructKind {
|
||||||
|
self.variant_data(db).kind()
|
||||||
|
}
|
||||||
|
|
||||||
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
||||||
db.struct_data(self.id).variant_data.clone()
|
db.struct_data(self.id).variant_data.clone()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue