feat: improve dot completions in a struct literal expression
Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
parent
c5d18f570c
commit
06076f95a7
@ -95,7 +95,7 @@ pub(crate) fn completions(
|
||||
complete_trait_impl::complete_trait_impl(&mut acc, &ctx);
|
||||
|
||||
// Reorder completion items if there is a sort_option
|
||||
acc.sort();
|
||||
acc.sort(&ctx);
|
||||
|
||||
Some(acc)
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty
|
||||
let fields = receiver.fields(ctx.db);
|
||||
|
||||
// If we use this implementation we can delete call_info in the CompletionContext
|
||||
if let Some(call_info) = call_info(ctx.db, ctx.file_position) {
|
||||
if let Some(record_field) = &ctx.record_field_syntax {
|
||||
acc.with_sort_option(SortOption::RecordField(record_field.clone()));
|
||||
} else if let Some(call_info) = call_info(ctx.db, ctx.file_position) {
|
||||
acc.with_sort_option(SortOption::CallFn(call_info));
|
||||
}
|
||||
|
||||
@ -240,6 +242,143 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_completion_in_record_lit() {
|
||||
assert_debug_snapshot!(
|
||||
do_ref_completion_without_sort(
|
||||
r"
|
||||
struct A { another_field: i64, another_good_type: u32, the_field: u32 }
|
||||
struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
|
||||
fn foo(a: A) {
|
||||
let b = B {
|
||||
the_field: a.<|>
|
||||
};
|
||||
}
|
||||
",
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "the_field",
|
||||
source_range: [270; 270),
|
||||
delete: [270; 270),
|
||||
insert: "the_field",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "another_good_type",
|
||||
source_range: [270; 270),
|
||||
delete: [270; 270),
|
||||
insert: "another_good_type",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "another_field",
|
||||
source_range: [270; 270),
|
||||
delete: [270; 270),
|
||||
insert: "another_field",
|
||||
kind: Field,
|
||||
detail: "i64",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_completion_in_record_lit_and_fn_call() {
|
||||
assert_debug_snapshot!(
|
||||
do_ref_completion_without_sort(
|
||||
r"
|
||||
struct A { another_field: i64, another_good_type: u32, the_field: u32 }
|
||||
struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
|
||||
fn test(the_field: i64) -> i64 { the_field }
|
||||
fn foo(a: A) {
|
||||
let b = B {
|
||||
the_field: test(a.<|>)
|
||||
};
|
||||
}
|
||||
",
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "another_field",
|
||||
source_range: [336; 336),
|
||||
delete: [336; 336),
|
||||
insert: "another_field",
|
||||
kind: Field,
|
||||
detail: "i64",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "another_good_type",
|
||||
source_range: [336; 336),
|
||||
delete: [336; 336),
|
||||
insert: "another_good_type",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "the_field",
|
||||
source_range: [336; 336),
|
||||
delete: [336; 336),
|
||||
insert: "the_field",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_completion_in_fn_call_and_record_lit() {
|
||||
assert_debug_snapshot!(
|
||||
do_ref_completion_without_sort(
|
||||
r"
|
||||
struct A { another_field: i64, another_good_type: u32, the_field: u32 }
|
||||
struct B { my_string: String, my_vec: Vec<u32>, the_field: u32 }
|
||||
fn test(the_field: i64) -> i64 { the_field }
|
||||
fn foo(a: A) {
|
||||
test(B {
|
||||
the_field: a.<|>
|
||||
});
|
||||
}
|
||||
",
|
||||
),
|
||||
@r###"
|
||||
[
|
||||
CompletionItem {
|
||||
label: "the_field",
|
||||
source_range: [328; 328),
|
||||
delete: [328; 328),
|
||||
insert: "the_field",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "another_good_type",
|
||||
source_range: [328; 328),
|
||||
delete: [328; 328),
|
||||
insert: "another_good_type",
|
||||
kind: Field,
|
||||
detail: "u32",
|
||||
},
|
||||
CompletionItem {
|
||||
label: "another_field",
|
||||
source_range: [328; 328),
|
||||
delete: [328; 328),
|
||||
insert: "another_field",
|
||||
kind: Field,
|
||||
detail: "i64",
|
||||
},
|
||||
]
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_completion_self() {
|
||||
assert_debug_snapshot!(
|
||||
|
@ -32,6 +32,7 @@ pub(crate) struct CompletionContext<'a> {
|
||||
pub(super) use_item_syntax: Option<ast::UseItem>,
|
||||
pub(super) record_lit_syntax: Option<ast::RecordLit>,
|
||||
pub(super) record_pat_syntax: Option<ast::RecordPat>,
|
||||
pub(super) record_field_syntax: Option<ast::RecordField>,
|
||||
pub(super) impl_def: Option<ast::ImplDef>,
|
||||
pub(super) call_info: Option<CallInfo>,
|
||||
pub(super) is_param: bool,
|
||||
@ -98,6 +99,7 @@ impl<'a> CompletionContext<'a> {
|
||||
use_item_syntax: None,
|
||||
record_lit_syntax: None,
|
||||
record_pat_syntax: None,
|
||||
record_field_syntax: None,
|
||||
impl_def: None,
|
||||
is_param: false,
|
||||
is_pat_binding_or_const: false,
|
||||
@ -274,6 +276,14 @@ impl<'a> CompletionContext<'a> {
|
||||
.take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
|
||||
.find_map(ast::FnDef::cast);
|
||||
|
||||
self.record_field_syntax = self
|
||||
.sema
|
||||
.ancestors_with_macros(self.token.parent())
|
||||
.take_while(|it| {
|
||||
it.kind() != SOURCE_FILE && it.kind() != MODULE && it.kind() != CALL_EXPR
|
||||
})
|
||||
.find_map(ast::RecordField::cast);
|
||||
|
||||
let parent = match name_ref.syntax().parent() {
|
||||
Some(it) => it,
|
||||
None => return,
|
||||
|
@ -2,9 +2,10 @@
|
||||
|
||||
use std::{cmp::Ordering, fmt};
|
||||
|
||||
use super::completion_context::CompletionContext;
|
||||
use crate::CallInfo;
|
||||
use hir::Documentation;
|
||||
use ra_syntax::TextRange;
|
||||
use hir::{Documentation, HirDisplay};
|
||||
use ra_syntax::{ast::RecordField, TextRange};
|
||||
use ra_text_edit::TextEdit;
|
||||
|
||||
/// `CompletionItem` describes a single completion variant in the editor pop-up.
|
||||
@ -301,7 +302,7 @@ impl<'a> Into<CompletionItem> for Builder {
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum SortOption {
|
||||
CallFn(CallInfo),
|
||||
// LitStruct,
|
||||
RecordField(RecordField),
|
||||
}
|
||||
|
||||
/// Represents an in-progress set of completions being built.
|
||||
@ -327,40 +328,55 @@ impl Completions {
|
||||
self.sort_option = Some(sort_option);
|
||||
}
|
||||
|
||||
pub(crate) fn sort(&mut self) {
|
||||
pub(crate) fn sort(&mut self, ctx: &CompletionContext) {
|
||||
if self.sort_option.is_none() {
|
||||
return;
|
||||
}
|
||||
let sort_option = self.sort_option.as_ref().unwrap();
|
||||
|
||||
match sort_option {
|
||||
let (active_name, active_type) = match self.sort_option.as_ref().unwrap() {
|
||||
SortOption::CallFn(call_info) => {
|
||||
if let Some(active_parameter_type) = call_info.active_parameter_type() {
|
||||
let active_parameter_name = call_info.active_parameter_name().unwrap();
|
||||
|
||||
self.buf.sort_by(|a, b| {
|
||||
// For the same type
|
||||
if let Some(a_parameter_type) = &a.detail {
|
||||
if &active_parameter_type == a_parameter_type {
|
||||
// If same type + same name then go top position
|
||||
if active_parameter_name != a.label {
|
||||
if let Some(b_parameter_type) = &b.detail {
|
||||
if &active_parameter_type == b_parameter_type {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
if call_info.active_parameter_type().is_none()
|
||||
|| call_info.active_parameter_name().is_none()
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // _ => unimplemented!("sort options not already implemented"),
|
||||
}
|
||||
(
|
||||
call_info.active_parameter_name().unwrap(),
|
||||
call_info.active_parameter_type().unwrap(),
|
||||
)
|
||||
}
|
||||
SortOption::RecordField(record_field) => {
|
||||
if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) {
|
||||
(
|
||||
struct_field.name(ctx.db).to_string(),
|
||||
struct_field.signature_ty(ctx.db).display(ctx.db).to_string(),
|
||||
)
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.buf.sort_by(|a, b| {
|
||||
// For the same type
|
||||
if let Some(a_parameter_type) = &a.detail {
|
||||
if &active_type == a_parameter_type {
|
||||
// If same type + same name then go top position
|
||||
if active_name != a.label {
|
||||
if let Some(b_parameter_type) = &b.detail {
|
||||
if &active_type == b_parameter_type {
|
||||
return Ordering::Equal;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user