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:
Benjamin Coenen 2020-04-15 22:10:57 +02:00
parent c5d18f570c
commit 06076f95a7
4 changed files with 198 additions and 33 deletions

View file

@ -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)
}

View file

@ -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!(

View file

@ -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,

View file

@ -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
}
});
}
}