From d42346fed61f706d68fe888631a41ea5f2752d7f Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sat, 11 Apr 2020 22:54:18 +0200 Subject: Improve autocompletion by looking on the type and name Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion/complete_dot.rs | 125 ++++++++++++++++++++- crates/ra_ide/src/completion/completion_context.rs | 14 ++- crates/ra_ide/src/completion/presentation.rs | 2 +- crates/ra_ide/src/completion/test_utils.rs | 11 +- crates/ra_ide/src/display/function_signature.rs | 57 ++++++---- crates/ra_ide/src/lib.rs | 15 +++ 6 files changed, 194 insertions(+), 30 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index f433faef3..358b041aa 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -use hir::{HasVisibility, Type}; +use hir::{HasVisibility, HirDisplay, Type}; use crate::completion::completion_item::CompletionKind; use crate::{ @@ -8,6 +8,7 @@ use crate::{ CompletionItem, }; use rustc_hash::FxHashSet; +use std::cmp::Ordering; /// Complete dot accesses, i.e. fields or methods (and .await syntax). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { @@ -37,7 +38,31 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { for receiver in receiver.autoderef(ctx.db) { - for (field, ty) in receiver.fields(ctx.db) { + let mut fields = receiver.fields(ctx.db); + if let Some(call_info) = &ctx.call_info { + if let Some(active_parameter_type) = call_info.active_parameter_type() { + let active_parameter_name = call_info.active_parameter_name().unwrap(); + fields.sort_by(|a, b| { + // For the same type + if active_parameter_type == a.1.display(ctx.db).to_string() { + // If same type + same name then go top position + if active_parameter_name == a.0.name(ctx.db).to_string() { + Ordering::Less + } else { + if active_parameter_type == b.1.display(ctx.db).to_string() { + Ordering::Equal + } else { + Ordering::Less + } + } + } else { + Ordering::Greater + } + }); + } + } + + for (field, ty) in fields { if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { // Skip private field. FIXME: If the definition location of the // field is editable, we should show the completion @@ -47,6 +72,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty } for (i, ty) in receiver.tuple_fields(ctx.db).into_iter().enumerate() { // FIXME: Handle visibility + // TODO: add the same behavior with type ? acc.add_tuple_field(ctx, i, &ty); } } @@ -70,13 +96,20 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T #[cfg(test)] mod tests { - use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + use crate::completion::{ + test_utils::{do_completion, do_completion_without_sort}, + CompletionItem, CompletionKind, + }; use insta::assert_debug_snapshot; fn do_ref_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Reference) } + fn do_ref_completion_without_sort(code: &str) -> Vec { + do_completion_without_sort(code, CompletionKind::Reference) + } + #[test] fn test_struct_field_completion() { assert_debug_snapshot!( @@ -103,6 +136,92 @@ mod tests { ); } + #[test] + fn test_struct_field_completion_in_func_call() { + assert_debug_snapshot!( + do_ref_completion_without_sort( + r" + struct A { another_field: i64, the_field: u32, my_string: String } + fn test(my_param: u32) -> u32 { my_param } + fn foo(a: A) { + test(a.<|>) + } + ", + ), + @r###" + [ + CompletionItem { + label: "the_field", + source_range: [201; 201), + delete: [201; 201), + insert: "the_field", + kind: Field, + detail: "u32", + }, + CompletionItem { + label: "another_field", + source_range: [201; 201), + delete: [201; 201), + insert: "another_field", + kind: Field, + detail: "i64", + }, + CompletionItem { + label: "my_string", + source_range: [201; 201), + delete: [201; 201), + insert: "my_string", + kind: Field, + detail: "{unknown}", + }, + ] + "### + ); + } + + #[test] + fn test_struct_field_completion_in_func_call_with_type_and_name() { + assert_debug_snapshot!( + do_ref_completion_without_sort( + r" + struct A { another_field: i64, another_good_type: u32, the_field: u32 } + fn test(the_field: u32) -> u32 { the_field } + fn foo(a: A) { + test(a.<|>) + } + ", + ), + @r###" + [ + CompletionItem { + label: "the_field", + source_range: [208; 208), + delete: [208; 208), + insert: "the_field", + kind: Field, + detail: "u32", + }, + CompletionItem { + label: "another_good_type", + source_range: [208; 208), + delete: [208; 208), + insert: "another_good_type", + kind: Field, + detail: "u32", + }, + CompletionItem { + label: "another_field", + source_range: [208; 208), + delete: [208; 208), + insert: "another_field", + kind: Field, + detail: "i64", + }, + ] + "### + ); + } + #[test] fn test_struct_field_completion_self() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index f833d2a9a..fddaf21e4 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -1,17 +1,19 @@ //! FIXME: write short doc here -use hir::{Semantics, SemanticsScope}; +use hir::{db::HirDatabase, Semantics, SemanticsScope}; use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{find_covering_element, find_node_at_offset}, - ast, AstNode, + ast, + ast::ArgListOwner, + AstNode, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextUnit, }; use ra_text_edit::AtomTextEdit; -use crate::{completion::CompletionConfig, FilePosition}; +use crate::{call_info::call_info, completion::CompletionConfig, CallInfo, FilePosition}; /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. @@ -21,6 +23,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) db: &'a RootDatabase, pub(super) config: &'a CompletionConfig, pub(super) offset: TextUnit, + pub(super) file_position: FilePosition, /// The token before the cursor, in the original file. pub(super) original_token: SyntaxToken, /// The token before the cursor, in the macro-expanded file. @@ -32,6 +35,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) record_lit_syntax: Option, pub(super) record_lit_pat: Option, pub(super) impl_def: Option, + pub(super) call_info: Option, pub(super) is_param: bool, /// If a name-binding or reference to a const in a pattern. /// Irrefutable patterns (like let) are excluded. @@ -88,9 +92,11 @@ impl<'a> CompletionContext<'a> { original_token, token, offset: position.offset, + file_position: position, krate, name_ref_syntax: None, function_syntax: None, + call_info: None, use_item_syntax: None, record_lit_syntax: None, record_lit_pat: None, @@ -253,6 +259,8 @@ impl<'a> CompletionContext<'a> { self.use_item_syntax = self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); + self.call_info = call_info(self.db, self.file_position); + self.function_syntax = self .sema .ancestors_with_macros(self.token.parent()) diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 55f75b15a..8be2d02d0 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -367,7 +367,7 @@ mod tests { ra_fixture: &str, options: CompletionConfig, ) -> Vec { - do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) + do_completion_with_options(ra_fixture, CompletionKind::Reference, &options, true) } #[test] diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index eb90b5279..f54d15a90 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -7,13 +7,18 @@ use crate::{ }; pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec { - do_completion_with_options(code, kind, &CompletionConfig::default()) + do_completion_with_options(code, kind, &CompletionConfig::default(), true) +} + +pub(crate) fn do_completion_without_sort(code: &str, kind: CompletionKind) -> Vec { + do_completion_with_options(code, kind, &CompletionConfig::default(), false) } pub(crate) fn do_completion_with_options( code: &str, kind: CompletionKind, options: &CompletionConfig, + sort_by_key: bool, ) -> Vec { let (analysis, position) = if code.contains("//-") { analysis_and_position(code) @@ -24,6 +29,8 @@ pub(crate) fn do_completion_with_options( let completion_items: Vec = completions.into(); let mut kind_completions: Vec = completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); - kind_completions.sort_by_key(|c| c.label().to_owned()); + if sort_by_key { + kind_completions.sort_by_key(|c| c.label().to_owned()); + } kind_completions } diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index b967a6816..e58a78271 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -36,6 +36,8 @@ pub struct FunctionSignature { pub parameters: Vec, /// Parameter names of the function pub parameter_names: Vec, + /// Parameter types of the function + pub parameter_types: Vec, /// Optional return type pub ret_type: Option, /// Where predicates @@ -62,14 +64,14 @@ impl FunctionSignature { return None; }; - let params = st - .fields(db) - .into_iter() - .map(|field: hir::StructField| { - let ty = field.signature_ty(db); - format!("{}", ty.display(db)) - }) - .collect(); + let mut params = vec![]; + let mut parameter_types = vec![]; + for field in st.fields(db).into_iter() { + let ty = field.signature_ty(db); + let raw_param = format!("{}", ty.display(db)); + parameter_types.push(raw_param.split(':').nth(1).unwrap()[1..].to_string()); + params.push(raw_param); + } Some( FunctionSignature { @@ -79,6 +81,7 @@ impl FunctionSignature { ret_type: node.name().map(|n| n.text().to_string()), parameters: params, parameter_names: vec![], + parameter_types, generic_parameters: generic_parameters(&node), where_predicates: where_predicates(&node), doc: None, @@ -99,15 +102,14 @@ impl FunctionSignature { let name = format!("{}::{}", parent_name, variant.name(db)); - let params = variant - .fields(db) - .into_iter() - .map(|field: hir::StructField| { - let name = field.name(db); - let ty = field.signature_ty(db); - format!("{}: {}", name, ty.display(db)) - }) - .collect(); + let mut params = vec![]; + let mut parameter_types = vec![]; + for field in variant.fields(db).into_iter() { + let ty = field.signature_ty(db); + let raw_param = format!("{}", ty.display(db)); + parameter_types.push(raw_param.split(':').nth(1).unwrap()[1..].to_string()); + params.push(raw_param); + } Some( FunctionSignature { @@ -117,6 +119,7 @@ impl FunctionSignature { ret_type: None, parameters: params, parameter_names: vec![], + parameter_types, generic_parameters: vec![], where_predicates: vec![], doc: None, @@ -139,6 +142,7 @@ impl FunctionSignature { ret_type: None, parameters: params, parameter_names: vec![], + parameter_types: vec![], generic_parameters: vec![], where_predicates: vec![], doc: None, @@ -151,18 +155,28 @@ impl FunctionSignature { impl From<&'_ ast::FnDef> for FunctionSignature { fn from(node: &ast::FnDef) -> FunctionSignature { - fn param_list(node: &ast::FnDef) -> (bool, Vec) { + fn param_list(node: &ast::FnDef) -> (bool, Vec, Vec) { let mut res = vec![]; + let mut res_types = vec![]; let mut has_self_param = false; if let Some(param_list) = node.param_list() { if let Some(self_param) = param_list.self_param() { has_self_param = true; - res.push(self_param.syntax().text().to_string()) + let raw_param = self_param.syntax().text().to_string(); + + // TODO: better solution ? + res_types.push( + raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), + ); + res.push(raw_param); } res.extend(param_list.params().map(|param| param.syntax().text().to_string())); + res_types.extend(param_list.params().map(|param| { + param.syntax().text().to_string().split(':').nth(1).unwrap()[1..].to_string() + })); } - (has_self_param, res) + (has_self_param, res, res_types) } fn param_name_list(node: &ast::FnDef) -> Vec { @@ -192,7 +206,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { res } - let (has_self_param, parameters) = param_list(node); + let (has_self_param, parameters, parameter_types) = param_list(node); FunctionSignature { kind: CallableKind::Function, @@ -204,6 +218,7 @@ impl From<&'_ ast::FnDef> for FunctionSignature { .map(|n| n.syntax().text().to_string()), parameters, parameter_names: param_name_list(node), + parameter_types, generic_parameters: generic_parameters(node), where_predicates: where_predicates(node), // docs are processed separately diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5599f143f..357c01cdf 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -127,6 +127,21 @@ pub struct CallInfo { pub active_parameter: Option, } +impl CallInfo { + pub fn active_parameter_type(&self) -> Option { + if let Some(id) = self.active_parameter { + return self.signature.parameter_types.get(id).map(|param_ty| param_ty.clone()); + } + None + } + pub fn active_parameter_name(&self) -> Option { + if let Some(id) = self.active_parameter { + return self.signature.parameter_names.get(id).map(|param_ty| param_ty.clone()); + } + None + } +} + /// `AnalysisHost` stores the current state of the world. #[derive(Debug)] pub struct AnalysisHost { -- cgit v1.2.3 From 064095742980d4c825391f643e437520599f51d8 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Sun, 12 Apr 2020 17:45:07 +0200 Subject: Improve autocompletion by looking on the type and name, change implementation, include sort in Completions struct Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion.rs | 3 ++ crates/ra_ide/src/completion/complete_dot.rs | 69 ++++++++++++++++--------- crates/ra_ide/src/completion/completion_item.rs | 50 +++++++++++++++++- 3 files changed, 96 insertions(+), 26 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 4a1a2a04a..3be8b1903 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -94,5 +94,8 @@ pub(crate) fn completions( complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); complete_trait_impl::complete_trait_impl(&mut acc, &ctx); + // Reorder completion items if there is a sort_option + acc.sort(); + Some(acc) } diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index b5448af5c..cb899d8ff 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -1,14 +1,23 @@ //! FIXME: write short doc here -use hir::{HasVisibility, HirDisplay, Type}; +use hir::{ + HasVisibility, + // HirDisplay, + Type, +}; use crate::completion::completion_item::CompletionKind; use crate::{ - completion::{completion_context::CompletionContext, completion_item::Completions}, + call_info::call_info, + completion::{ + completion_context::CompletionContext, + completion_item::{Completions, SortOption}, + }, + // CallInfo, CompletionItem, }; use rustc_hash::FxHashSet; -use std::cmp::Ordering; +// use std::cmp::Ordering; /// Complete dot accesses, i.e. fields or methods (and .await syntax). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { @@ -38,30 +47,40 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { for receiver in receiver.autoderef(ctx.db) { - let mut fields = receiver.fields(ctx.db); - if let Some(call_info) = &ctx.call_info { - if let Some(active_parameter_type) = call_info.active_parameter_type() { - let active_parameter_name = call_info.active_parameter_name().unwrap(); - fields.sort_by(|a, b| { - // For the same type - if active_parameter_type == a.1.display(ctx.db).to_string() { - // If same type + same name then go top position - if active_parameter_name == a.0.name(ctx.db).to_string() { - Ordering::Less - } else { - if active_parameter_type == b.1.display(ctx.db).to_string() { - Ordering::Equal - } else { - Ordering::Less - } - } - } else { - Ordering::Greater - } - }); - } + 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) { + acc.with_sort_option(SortOption::CallFn(call_info)); } + // // For Call Fn + // if let Some(call_info) = &ctx.call_info { + // if let Some(active_parameter_type) = call_info.active_parameter_type() { + // let active_parameter_name = call_info.active_parameter_name().unwrap(); + // fields.sort_by(|a, b| { + // // For the same type + // if active_parameter_type == a.1.display(ctx.db).to_string() { + // // If same type + same name then go top position + // if active_parameter_name == a.0.name(ctx.db).to_string() { + // Ordering::Less + // } else { + // if active_parameter_type == b.1.display(ctx.db).to_string() { + // Ordering::Equal + // } else { + // Ordering::Less + // } + // } + // } else { + // Ordering::Greater + // } + // }); + // } + // } + + // For Lit struct fields + // --- + for (field, ty) in fields { if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { // Skip private field. FIXME: If the definition location of the diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index bc0f1aff5..f8e6e53f1 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -1,7 +1,8 @@ //! FIXME: write short doc here -use std::fmt; +use std::{cmp::Ordering, fmt}; +use crate::CallInfo; use hir::Documentation; use ra_syntax::TextRange; use ra_text_edit::TextEdit; @@ -297,10 +298,17 @@ impl<'a> Into for Builder { } } +#[derive(Debug)] +pub(crate) enum SortOption { + CallFn(CallInfo), + // LitStruct, +} + /// Represents an in-progress set of completions being built. #[derive(Debug, Default)] pub(crate) struct Completions { buf: Vec, + sort_option: Option, } impl Completions { @@ -314,6 +322,46 @@ impl Completions { { items.into_iter().for_each(|item| self.add(item.into())) } + + pub(crate) fn with_sort_option(&mut self, sort_option: SortOption) { + self.sort_option = Some(sort_option); + } + + pub(crate) fn sort(&mut self) { + if self.sort_option.is_none() { + return; + } + let sort_option = self.sort_option.as_ref().unwrap(); + + match sort_option { + 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 + } + }); + } + } // _ => unimplemented!("sort options not already implemented"), + } + } } impl Into> for Completions { -- cgit v1.2.3 From 06076f95a7ca764696b055eb754e163f884eefaa Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Wed, 15 Apr 2020 22:10:57 +0200 Subject: feat: improve dot completions in a struct literal expression Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion.rs | 2 +- crates/ra_ide/src/completion/complete_dot.rs | 141 ++++++++++++++++++++- crates/ra_ide/src/completion/completion_context.rs | 10 ++ crates/ra_ide/src/completion/completion_item.rs | 74 ++++++----- 4 files changed, 196 insertions(+), 31 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 3be8b1903..185450508 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -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) } diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index cb899d8ff..2e228b638 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -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, 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, 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, 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!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index da054f7a2..dd9cc7daa 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -32,6 +32,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) use_item_syntax: Option, pub(super) record_lit_syntax: Option, pub(super) record_pat_syntax: Option, + pub(super) record_field_syntax: Option, pub(super) impl_def: Option, pub(super) call_info: Option, 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, diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index f8e6e53f1..c9c3fdc0e 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -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 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 + if call_info.active_parameter_type().is_none() + || call_info.active_parameter_name().is_none() + { + return; + } + ( + 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; } - } else { - Ordering::Greater } - }); + } + Ordering::Less + } else { + Ordering::Greater } - } // _ => unimplemented!("sort options not already implemented"), - } + } else { + Ordering::Greater + } + }); } } -- cgit v1.2.3 From 6ebc8bbeb005f5d3f2b00d1ae1f1804116e3a8f5 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Thu, 16 Apr 2020 18:30:08 +0200 Subject: feat: improve dot completions with scoring Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion.rs | 6 +- crates/ra_ide/src/completion/complete_dot.rs | 114 +++++++++--------------- crates/ra_ide/src/completion/completion_item.rs | 62 ++++++++----- crates/ra_ide/src/completion/presentation.rs | 2 +- crates/ra_ide/src/completion/test_utils.rs | 11 +-- crates/ra_ide/src/lib.rs | 4 +- 6 files changed, 91 insertions(+), 108 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 185450508..38c8aed8d 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -29,7 +29,7 @@ use crate::{ }; pub use crate::completion::completion_item::{ - CompletionItem, CompletionItemKind, InsertTextFormat, + CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -94,8 +94,8 @@ pub(crate) fn completions( complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); complete_trait_impl::complete_trait_impl(&mut acc, &ctx); - // Reorder completion items if there is a sort_option - acc.sort(&ctx); + // Compute score for completion items + acc.compute_score(&ctx); Some(acc) } diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 2e228b638..174b39964 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -6,12 +6,11 @@ use hir::{ Type, }; -use crate::completion::completion_item::CompletionKind; use crate::{ call_info::call_info, completion::{ completion_context::CompletionContext, - completion_item::{Completions, SortOption}, + completion_item::{CompletionKind, Completions, ScoreOption}, }, // CallInfo, CompletionItem, @@ -49,40 +48,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty for receiver in receiver.autoderef(ctx.db) { let fields = receiver.fields(ctx.db); - // If we use this implementation we can delete call_info in the CompletionContext if let Some(record_field) = &ctx.record_field_syntax { - acc.with_sort_option(SortOption::RecordField(record_field.clone())); + acc.with_score_option(ScoreOption::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)); + acc.with_score_option(ScoreOption::CallFn(call_info)); } - // // For Call Fn - // if let Some(call_info) = &ctx.call_info { - // if let Some(active_parameter_type) = call_info.active_parameter_type() { - // let active_parameter_name = call_info.active_parameter_name().unwrap(); - // fields.sort_by(|a, b| { - // // For the same type - // if active_parameter_type == a.1.display(ctx.db).to_string() { - // // If same type + same name then go top position - // if active_parameter_name == a.0.name(ctx.db).to_string() { - // Ordering::Less - // } else { - // if active_parameter_type == b.1.display(ctx.db).to_string() { - // Ordering::Equal - // } else { - // Ordering::Less - // } - // } - // } else { - // Ordering::Greater - // } - // }); - // } - // } - - // For Lit struct fields - // --- - for (field, ty) in fields { if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { // Skip private field. FIXME: If the definition location of the @@ -116,20 +87,13 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T #[cfg(test)] mod tests { - use crate::completion::{ - test_utils::{do_completion, do_completion_without_sort}, - CompletionItem, CompletionKind, - }; + use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; use insta::assert_debug_snapshot; fn do_ref_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Reference) } - fn do_ref_completion_without_sort(code: &str) -> Vec { - do_completion_without_sort(code, CompletionKind::Reference) - } - #[test] fn test_struct_field_completion() { assert_debug_snapshot!( @@ -159,7 +123,7 @@ mod tests { #[test] fn test_struct_field_completion_in_func_call() { assert_debug_snapshot!( - do_ref_completion_without_sort( + do_ref_completion( r" struct A { another_field: i64, the_field: u32, my_string: String } fn test(my_param: u32) -> u32 { my_param } @@ -170,14 +134,6 @@ mod tests { ), @r###" [ - CompletionItem { - label: "the_field", - source_range: [201; 201), - delete: [201; 201), - insert: "the_field", - kind: Field, - detail: "u32", - }, CompletionItem { label: "another_field", source_range: [201; 201), @@ -194,6 +150,15 @@ mod tests { kind: Field, detail: "{unknown}", }, + CompletionItem { + label: "the_field", + source_range: [201; 201), + delete: [201; 201), + insert: "the_field", + kind: Field, + detail: "u32", + score: TypeMatch, + }, ] "### ); @@ -202,7 +167,7 @@ mod tests { #[test] fn test_struct_field_completion_in_func_call_with_type_and_name() { assert_debug_snapshot!( - do_ref_completion_without_sort( + do_ref_completion( r" struct A { another_field: i64, another_good_type: u32, the_field: u32 } fn test(the_field: u32) -> u32 { the_field } @@ -214,12 +179,12 @@ mod tests { @r###" [ CompletionItem { - label: "the_field", + label: "another_field", source_range: [208; 208), delete: [208; 208), - insert: "the_field", + insert: "another_field", kind: Field, - detail: "u32", + detail: "i64", }, CompletionItem { label: "another_good_type", @@ -228,14 +193,16 @@ mod tests { insert: "another_good_type", kind: Field, detail: "u32", + score: TypeMatch, }, CompletionItem { - label: "another_field", + label: "the_field", source_range: [208; 208), delete: [208; 208), - insert: "another_field", + insert: "the_field", kind: Field, - detail: "i64", + detail: "u32", + score: TypeAndNameMatch, }, ] "### @@ -245,7 +212,7 @@ mod tests { #[test] fn test_struct_field_completion_in_record_lit() { assert_debug_snapshot!( - do_ref_completion_without_sort( + do_ref_completion( r" struct A { another_field: i64, another_good_type: u32, the_field: u32 } struct B { my_string: String, my_vec: Vec, the_field: u32 } @@ -259,12 +226,12 @@ mod tests { @r###" [ CompletionItem { - label: "the_field", + label: "another_field", source_range: [270; 270), delete: [270; 270), - insert: "the_field", + insert: "another_field", kind: Field, - detail: "u32", + detail: "i64", }, CompletionItem { label: "another_good_type", @@ -273,14 +240,16 @@ mod tests { insert: "another_good_type", kind: Field, detail: "u32", + score: TypeMatch, }, CompletionItem { - label: "another_field", + label: "the_field", source_range: [270; 270), delete: [270; 270), - insert: "another_field", + insert: "the_field", kind: Field, - detail: "i64", + detail: "u32", + score: TypeAndNameMatch, }, ] "### @@ -290,7 +259,7 @@ mod tests { #[test] fn test_struct_field_completion_in_record_lit_and_fn_call() { assert_debug_snapshot!( - do_ref_completion_without_sort( + do_ref_completion( r" struct A { another_field: i64, another_good_type: u32, the_field: u32 } struct B { my_string: String, my_vec: Vec, the_field: u32 } @@ -311,6 +280,7 @@ mod tests { insert: "another_field", kind: Field, detail: "i64", + score: TypeMatch, }, CompletionItem { label: "another_good_type", @@ -336,7 +306,7 @@ mod tests { #[test] fn test_struct_field_completion_in_fn_call_and_record_lit() { assert_debug_snapshot!( - do_ref_completion_without_sort( + do_ref_completion( r" struct A { another_field: i64, another_good_type: u32, the_field: u32 } struct B { my_string: String, my_vec: Vec, the_field: u32 } @@ -351,12 +321,12 @@ mod tests { @r###" [ CompletionItem { - label: "the_field", + label: "another_field", source_range: [328; 328), delete: [328; 328), - insert: "the_field", + insert: "another_field", kind: Field, - detail: "u32", + detail: "i64", }, CompletionItem { label: "another_good_type", @@ -365,14 +335,16 @@ mod tests { insert: "another_good_type", kind: Field, detail: "u32", + score: TypeMatch, }, CompletionItem { - label: "another_field", + label: "the_field", source_range: [328; 328), delete: [328; 328), - insert: "another_field", + insert: "the_field", kind: Field, - detail: "i64", + detail: "u32", + score: TypeAndNameMatch, }, ] "### diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index c9c3fdc0e..84d51bafe 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -53,6 +53,9 @@ pub struct CompletionItem { /// If completing a function call, ask the editor to show parameter popup /// after completion. trigger_call_info: bool, + + /// Score is usefull to pre select or display in better order completion items + score: Option, } // We use custom debug for CompletionItem to make `insta`'s diffs more readable. @@ -82,6 +85,9 @@ impl fmt::Debug for CompletionItem { if self.deprecated { s.field("deprecated", &true); } + if let Some(score) = &self.score { + s.field("score", score); + } if self.trigger_call_info { s.field("trigger_call_info", &true); } @@ -149,6 +155,7 @@ impl CompletionItem { text_edit: None, deprecated: None, trigger_call_info: None, + score: None, } } /// What user sees in pop-up in the UI. @@ -188,6 +195,10 @@ impl CompletionItem { self.deprecated } + pub fn score(&self) -> Option { + self.score.clone() + } + pub fn trigger_call_info(&self) -> bool { self.trigger_call_info } @@ -208,6 +219,7 @@ pub(crate) struct Builder { text_edit: Option, deprecated: Option, trigger_call_info: Option, + score: Option, } impl Builder { @@ -237,6 +249,7 @@ impl Builder { completion_kind: self.completion_kind, deprecated: self.deprecated.unwrap_or(false), trigger_call_info: self.trigger_call_info.unwrap_or(false), + score: self.score, } } pub(crate) fn lookup_by(mut self, lookup: impl Into) -> Builder { @@ -287,6 +300,10 @@ impl Builder { self.deprecated = Some(deprecated); self } + pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { + self.score = Some(score); + self + } pub(crate) fn trigger_call_info(mut self) -> Builder { self.trigger_call_info = Some(true); self @@ -300,16 +317,22 @@ impl<'a> Into for Builder { } #[derive(Debug)] -pub(crate) enum SortOption { +pub(crate) enum ScoreOption { CallFn(CallInfo), RecordField(RecordField), } +#[derive(Debug, Clone)] +pub enum CompletionScore { + TypeMatch, + TypeAndNameMatch, +} + /// Represents an in-progress set of completions being built. #[derive(Debug, Default)] pub(crate) struct Completions { buf: Vec, - sort_option: Option, + score_option: Option, } impl Completions { @@ -324,17 +347,17 @@ impl Completions { items.into_iter().for_each(|item| self.add(item.into())) } - pub(crate) fn with_sort_option(&mut self, sort_option: SortOption) { - self.sort_option = Some(sort_option); + pub(crate) fn with_score_option(&mut self, score_option: ScoreOption) { + self.score_option = Some(score_option); } - pub(crate) fn sort(&mut self, ctx: &CompletionContext) { - if self.sort_option.is_none() { + pub(crate) fn compute_score(&mut self, ctx: &CompletionContext) { + if self.score_option.is_none() { return; } - let (active_name, active_type) = match self.sort_option.as_ref().unwrap() { - SortOption::CallFn(call_info) => { + let (active_name, active_type) = match self.score_option.as_ref().unwrap() { + ScoreOption::CallFn(call_info) => { if call_info.active_parameter_type().is_none() || call_info.active_parameter_name().is_none() { @@ -345,7 +368,7 @@ impl Completions { call_info.active_parameter_type().unwrap(), ) } - SortOption::RecordField(record_field) => { + ScoreOption::RecordField(record_field) => { if let Some((struct_field, _)) = ctx.sema.resolve_record_field(record_field) { ( struct_field.name(ctx.db).to_string(), @@ -357,26 +380,19 @@ impl Completions { } }; - self.buf.sort_by(|a, b| { + for completion_item in &mut self.buf { // For the same type - if let Some(a_parameter_type) = &a.detail { + if let Some(a_parameter_type) = &completion_item.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; - } - } + if active_name == completion_item.label { + completion_item.score = Some(CompletionScore::TypeAndNameMatch); + } else { + completion_item.score = Some(CompletionScore::TypeMatch); } - Ordering::Less - } else { - Ordering::Greater } - } else { - Ordering::Greater } - }); + } } } diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 8be2d02d0..55f75b15a 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -367,7 +367,7 @@ mod tests { ra_fixture: &str, options: CompletionConfig, ) -> Vec { - do_completion_with_options(ra_fixture, CompletionKind::Reference, &options, true) + do_completion_with_options(ra_fixture, CompletionKind::Reference, &options) } #[test] diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index f54d15a90..eb90b5279 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -7,18 +7,13 @@ use crate::{ }; pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec { - do_completion_with_options(code, kind, &CompletionConfig::default(), true) -} - -pub(crate) fn do_completion_without_sort(code: &str, kind: CompletionKind) -> Vec { - do_completion_with_options(code, kind, &CompletionConfig::default(), false) + do_completion_with_options(code, kind, &CompletionConfig::default()) } pub(crate) fn do_completion_with_options( code: &str, kind: CompletionKind, options: &CompletionConfig, - sort_by_key: bool, ) -> Vec { let (analysis, position) = if code.contains("//-") { analysis_and_position(code) @@ -29,8 +24,6 @@ pub(crate) fn do_completion_with_options( let completion_items: Vec = completions.into(); let mut kind_completions: Vec = completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); - if sort_by_key { - kind_completions.sort_by_key(|c| c.label().to_owned()); - } + kind_completions.sort_by_key(|c| c.label().to_owned()); kind_completions } diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 357c01cdf..ddaa30a16 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -67,7 +67,9 @@ use crate::display::ToNav; pub use crate::{ assists::{Assist, AssistId}, call_hierarchy::CallItem, - completion::{CompletionConfig, CompletionItem, CompletionItemKind, InsertTextFormat}, + completion::{ + CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, InsertTextFormat, + }, diagnostics::Severity, display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, expand_macro::ExpandedMacro, -- cgit v1.2.3 From 071ef268b5c8fb9afec1db912ebcc5d6577f5e73 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 17 Apr 2020 10:29:32 +0200 Subject: feat: improve dot completions with scoring Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion.rs | 3 - crates/ra_ide/src/completion/complete_dot.rs | 19 +---- crates/ra_ide/src/completion/completion_item.rs | 106 +++++++++++------------- crates/ra_ide/src/completion/presentation.rs | 1 + 4 files changed, 52 insertions(+), 77 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index 38c8aed8d..19bc4321c 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -94,8 +94,5 @@ pub(crate) fn completions( complete_macro_in_item_position::complete_macro_in_item_position(&mut acc, &ctx); complete_trait_impl::complete_trait_impl(&mut acc, &ctx); - // Compute score for completion items - acc.compute_score(&ctx); - Some(acc) } diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 174b39964..c16357a7e 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -1,16 +1,11 @@ //! FIXME: write short doc here -use hir::{ - HasVisibility, - // HirDisplay, - Type, -}; +use hir::{HasVisibility, Type}; use crate::{ - call_info::call_info, completion::{ completion_context::CompletionContext, - completion_item::{CompletionKind, Completions, ScoreOption}, + completion_item::{CompletionKind, Completions}, }, // CallInfo, CompletionItem, @@ -46,15 +41,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { for receiver in receiver.autoderef(ctx.db) { - let fields = receiver.fields(ctx.db); - - if let Some(record_field) = &ctx.record_field_syntax { - acc.with_score_option(ScoreOption::RecordField(record_field.clone())); - } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { - acc.with_score_option(ScoreOption::CallFn(call_info)); - } - - for (field, ty) in fields { + for (field, ty) in receiver.fields(ctx.db) { if ctx.scope().module().map_or(false, |m| !field.is_visible_from(ctx.db, m)) { // Skip private field. FIXME: If the definition location of the // field is editable, we should show the completion diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 84d51bafe..a3ae9c86b 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -1,11 +1,11 @@ //! FIXME: write short doc here -use std::{cmp::Ordering, fmt}; +use std::fmt; use super::completion_context::CompletionContext; -use crate::CallInfo; +use crate::call_info::call_info; use hir::{Documentation, HirDisplay}; -use ra_syntax::{ast::RecordField, TextRange}; +use ra_syntax::TextRange; use ra_text_edit::TextEdit; /// `CompletionItem` describes a single completion variant in the editor pop-up. @@ -199,6 +199,10 @@ impl CompletionItem { self.score.clone() } + pub fn set_score(&mut self, score: CompletionScore) { + self.score = Some(score); + } + pub fn trigger_call_info(&self) -> bool { self.trigger_call_info } @@ -300,6 +304,47 @@ impl Builder { self.deprecated = Some(deprecated); self } + #[allow(unused)] + pub(crate) fn compute_score(mut self, ctx: &CompletionContext) -> Builder { + let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { + 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; + } + } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { + if call_info.active_parameter_type().is_some() + && call_info.active_parameter_name().is_some() + { + ( + call_info.active_parameter_name().unwrap(), + call_info.active_parameter_type().unwrap(), + ) + } else { + return self; + } + } else { + return self; + }; + + // Compute score + // For the same type + if let Some(a_parameter_type) = &self.detail { + if &active_type == a_parameter_type { + // If same type + same name then go top position + if active_name == self.label { + return self.set_score(CompletionScore::TypeAndNameMatch); + } else { + return self.set_score(CompletionScore::TypeMatch); + } + } + } + + self + } pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { self.score = Some(score); self @@ -316,12 +361,6 @@ impl<'a> Into for Builder { } } -#[derive(Debug)] -pub(crate) enum ScoreOption { - CallFn(CallInfo), - RecordField(RecordField), -} - #[derive(Debug, Clone)] pub enum CompletionScore { TypeMatch, @@ -332,7 +371,6 @@ pub enum CompletionScore { #[derive(Debug, Default)] pub(crate) struct Completions { buf: Vec, - score_option: Option, } impl Completions { @@ -346,54 +384,6 @@ impl Completions { { items.into_iter().for_each(|item| self.add(item.into())) } - - pub(crate) fn with_score_option(&mut self, score_option: ScoreOption) { - self.score_option = Some(score_option); - } - - pub(crate) fn compute_score(&mut self, ctx: &CompletionContext) { - if self.score_option.is_none() { - return; - } - - let (active_name, active_type) = match self.score_option.as_ref().unwrap() { - ScoreOption::CallFn(call_info) => { - if call_info.active_parameter_type().is_none() - || call_info.active_parameter_name().is_none() - { - return; - } - ( - call_info.active_parameter_name().unwrap(), - call_info.active_parameter_type().unwrap(), - ) - } - ScoreOption::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; - } - } - }; - - for completion_item in &mut self.buf { - // For the same type - if let Some(a_parameter_type) = &completion_item.detail { - if &active_type == a_parameter_type { - // If same type + same name then go top position - if active_name == completion_item.label { - completion_item.score = Some(CompletionScore::TypeAndNameMatch); - } else { - completion_item.score = Some(CompletionScore::TypeMatch); - } - } - } - } - } } impl Into> for Completions { diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 55f75b15a..5c3360ce4 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -31,6 +31,7 @@ impl Completions { .detail(ty.display(ctx.db).to_string()) .set_documentation(field.docs(ctx.db)) .set_deprecated(is_deprecated) + .compute_score(ctx) .add_to(self); } -- cgit v1.2.3 From 379787858bbfb2691e134942d94fcbc70c5d1d7f Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Fri, 17 Apr 2020 10:59:04 +0200 Subject: feat: improve dot completions with scoring Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion/completion_context.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index dd9cc7daa..46e243192 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -11,7 +11,7 @@ use ra_syntax::{ }; use ra_text_edit::AtomTextEdit; -use crate::{call_info::call_info, completion::CompletionConfig, CallInfo, FilePosition}; +use crate::{completion::CompletionConfig, FilePosition}; /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. @@ -34,7 +34,6 @@ pub(crate) struct CompletionContext<'a> { pub(super) record_pat_syntax: Option, pub(super) record_field_syntax: Option, pub(super) impl_def: Option, - pub(super) call_info: Option, pub(super) is_param: bool, /// If a name-binding or reference to a const in a pattern. /// Irrefutable patterns (like let) are excluded. @@ -95,7 +94,6 @@ impl<'a> CompletionContext<'a> { krate, name_ref_syntax: None, function_syntax: None, - call_info: None, use_item_syntax: None, record_lit_syntax: None, record_pat_syntax: None, @@ -268,8 +266,6 @@ impl<'a> CompletionContext<'a> { self.use_item_syntax = self.sema.ancestors_with_macros(self.token.parent()).find_map(ast::UseItem::cast); - self.call_info = call_info(self.db, self.file_position); - self.function_syntax = self .sema .ancestors_with_macros(self.token.parent()) -- cgit v1.2.3 From 1c3a1385a587f0713908c0ae888ffad31f13de11 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 21 Apr 2020 14:31:57 +0200 Subject: Improve autocompletion by looking on the type and name Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion/complete_dot.rs | 2 -- crates/ra_ide/src/completion/completion_item.rs | 42 ++-------------------- crates/ra_ide/src/completion/presentation.rs | 48 ++++++++++++++++++++++--- crates/ra_ide/src/display/function_signature.rs | 3 +- 4 files changed, 47 insertions(+), 48 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index c16357a7e..44288f92e 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -7,11 +7,9 @@ use crate::{ completion_context::CompletionContext, completion_item::{CompletionKind, Completions}, }, - // CallInfo, CompletionItem, }; use rustc_hash::FxHashSet; -// use std::cmp::Ordering; /// Complete dot accesses, i.e. fields or methods (and .await syntax). pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index a3ae9c86b..8b96b81db 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -305,46 +305,6 @@ impl Builder { self } #[allow(unused)] - pub(crate) fn compute_score(mut self, ctx: &CompletionContext) -> Builder { - let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { - 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; - } - } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { - if call_info.active_parameter_type().is_some() - && call_info.active_parameter_name().is_some() - { - ( - call_info.active_parameter_name().unwrap(), - call_info.active_parameter_type().unwrap(), - ) - } else { - return self; - } - } else { - return self; - }; - - // Compute score - // For the same type - if let Some(a_parameter_type) = &self.detail { - if &active_type == a_parameter_type { - // If same type + same name then go top position - if active_name == self.label { - return self.set_score(CompletionScore::TypeAndNameMatch); - } else { - return self.set_score(CompletionScore::TypeMatch); - } - } - } - - self - } pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { self.score = Some(score); self @@ -363,7 +323,9 @@ impl<'a> Into for Builder { #[derive(Debug, Clone)] pub enum CompletionScore { + /// If only type match TypeMatch, + /// If type and name match TypeAndNameMatch, } diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 5c3360ce4..bb12a1bdc 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -6,12 +6,13 @@ use stdx::SepBy; use test_utils::tested_by; use crate::{ + call_info::call_info, completion::{ completion_item::Builder, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, }, display::{const_label, macro_label, type_label, FunctionSignature}, - RootDatabase, + CompletionScore, RootDatabase, }; impl Completions { @@ -22,7 +23,7 @@ impl Completions { ty: &Type, ) { let is_deprecated = is_deprecated(field, ctx.db); - CompletionItem::new( + let mut completion_item = CompletionItem::new( CompletionKind::Reference, ctx.source_range(), field.name(ctx.db).to_string(), @@ -31,8 +32,11 @@ impl Completions { .detail(ty.display(ctx.db).to_string()) .set_documentation(field.docs(ctx.db)) .set_deprecated(is_deprecated) - .compute_score(ctx) - .add_to(self); + .build(); + + compute_score(&mut completion_item, ctx); + + self.add(completion_item); } pub(crate) fn add_tuple_field(&mut self, ctx: &CompletionContext, field: usize, ty: &Type) { @@ -295,6 +299,42 @@ impl Completions { } } +pub(crate) fn compute_score(completion_item: &mut CompletionItem, ctx: &CompletionContext) { + let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { + 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; + } + } else if let Some(call_info) = call_info(ctx.db, ctx.file_position) { + if call_info.active_parameter_type().is_some() + && call_info.active_parameter_name().is_some() + { + (call_info.active_parameter_name().unwrap(), call_info.active_parameter_type().unwrap()) + } else { + return; + } + } else { + return; + }; + + // Compute score + // For the same type + if let Some(a_parameter_type) = completion_item.detail() { + if &active_type == a_parameter_type { + // If same type + same name then go top position + if active_name == completion_item.label() { + completion_item.set_score(CompletionScore::TypeAndNameMatch); + } else { + completion_item.set_score(CompletionScore::TypeMatch); + } + } + } +} + enum Params { Named(Vec), Anonymous(usize), diff --git a/crates/ra_ide/src/display/function_signature.rs b/crates/ra_ide/src/display/function_signature.rs index 2d175882b..b5e2785fe 100644 --- a/crates/ra_ide/src/display/function_signature.rs +++ b/crates/ra_ide/src/display/function_signature.rs @@ -73,7 +73,7 @@ impl FunctionSignature { if let Some(param_type) = raw_param.split(':').nth(1) { parameter_types.push(param_type[1..].to_string()); } else { - // The unwrap_or_else is useful when you have tuple struct + // useful when you have tuple struct parameter_types.push(raw_param.clone()); } params.push(raw_param); @@ -177,7 +177,6 @@ impl From<&'_ ast::FnDef> for FunctionSignature { has_self_param = true; let raw_param = self_param.syntax().text().to_string(); - // FIXME: better solution ? res_types.push( raw_param.split(':').nth(1).unwrap_or_else(|| " Self")[1..].to_string(), ); -- cgit v1.2.3 From b6a7be19d9df53e39fa9bc1779cc19b7b708e495 Mon Sep 17 00:00:00 2001 From: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> Date: Tue, 21 Apr 2020 14:36:56 +0200 Subject: Improve autocompletion by looking on the type and name Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com> --- crates/ra_ide/src/completion/completion_item.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'crates/ra_ide') diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index 8b96b81db..e17586aa5 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -2,9 +2,7 @@ use std::fmt; -use super::completion_context::CompletionContext; -use crate::call_info::call_info; -use hir::{Documentation, HirDisplay}; +use hir::Documentation; use ra_syntax::TextRange; use ra_text_edit::TextEdit; -- cgit v1.2.3