add <> when completing generic types

This commit is contained in:
Aleksey Kladov 2019-10-08 21:14:52 +03:00
parent f4fa98b1bf
commit e0b1c17dcb
4 changed files with 198 additions and 78 deletions

View file

@ -290,22 +290,24 @@ mod tests {
} }
" "
), ),
@r###"[ @r###"
CompletionItem { [
label: "T", CompletionItem {
source_range: [54; 54), label: "T",
delete: [54; 54), source_range: [54; 54),
insert: "T", delete: [54; 54),
kind: TypeParam, insert: "T",
}, kind: TypeParam,
CompletionItem { },
label: "X", CompletionItem {
source_range: [54; 54), label: "X",
delete: [54; 54), source_range: [54; 54),
insert: "X", delete: [54; 54),
kind: Struct, insert: "X<$0>",
}, kind: Struct,
]"### },
]
"###
); );
} }
@ -319,22 +321,24 @@ mod tests {
} }
" "
), ),
@r###"[ @r###"
CompletionItem { [
label: "Self", CompletionItem {
source_range: [48; 48), label: "Self",
delete: [48; 48), source_range: [48; 48),
insert: "Self", delete: [48; 48),
kind: TypeParam, insert: "Self",
}, kind: TypeParam,
CompletionItem { },
label: "X", CompletionItem {
source_range: [48; 48), label: "X",
delete: [48; 48), source_range: [48; 48),
insert: "X", delete: [48; 48),
kind: Enum, insert: "X",
}, kind: Enum,
]"### },
]
"###
); );
} }
@ -442,23 +446,25 @@ mod tests {
fn x() -> <|> fn x() -> <|>
" "
), ),
@r###"[ @r###"
CompletionItem { [
label: "Foo", CompletionItem {
source_range: [55; 55), label: "Foo",
delete: [55; 55), source_range: [55; 55),
insert: "Foo", delete: [55; 55),
kind: Struct, insert: "Foo",
}, kind: Struct,
CompletionItem { },
label: "x", CompletionItem {
source_range: [55; 55), label: "x",
delete: [55; 55), source_range: [55; 55),
insert: "x()$0", delete: [55; 55),
kind: Function, insert: "x()$0",
detail: "fn x()", kind: Function,
}, detail: "fn x()",
]"### },
]
"###
); );
} }
@ -538,30 +544,32 @@ mod tests {
} }
" "
), ),
@r#"[ @r###"
CompletionItem { [
label: "Option", CompletionItem {
source_range: [18; 18), label: "Option",
delete: [18; 18), source_range: [18; 18),
insert: "Option", delete: [18; 18),
kind: Struct, insert: "Option",
}, kind: Struct,
CompletionItem { },
label: "foo", CompletionItem {
source_range: [18; 18), label: "foo",
delete: [18; 18), source_range: [18; 18),
insert: "foo()$0", delete: [18; 18),
kind: Function, insert: "foo()$0",
detail: "fn foo()", kind: Function,
}, detail: "fn foo()",
CompletionItem { },
label: "std", CompletionItem {
source_range: [18; 18), label: "std",
delete: [18; 18), source_range: [18; 18),
insert: "std", delete: [18; 18),
kind: Module, insert: "std",
}, kind: Module,
]"# },
]
"###
); );
} }

View file

@ -40,6 +40,8 @@ pub(crate) struct CompletionContext<'a> {
pub(super) dot_receiver: Option<ast::Expr>, pub(super) dot_receiver: Option<ast::Expr>,
/// If this is a call (method or function) in particular, i.e. the () are already there. /// If this is a call (method or function) in particular, i.e. the () are already there.
pub(super) is_call: bool, pub(super) is_call: bool,
pub(super) is_path_type: bool,
pub(super) has_type_args: bool,
} }
impl<'a> CompletionContext<'a> { impl<'a> CompletionContext<'a> {
@ -76,6 +78,8 @@ impl<'a> CompletionContext<'a> {
is_new_item: false, is_new_item: false,
dot_receiver: None, dot_receiver: None,
is_call: false, is_call: false,
is_path_type: false,
has_type_args: false,
}; };
ctx.fill(&original_parse, position.offset); ctx.fill(&original_parse, position.offset);
Some(ctx) Some(ctx)
@ -176,6 +180,9 @@ impl<'a> CompletionContext<'a> {
.and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast)) .and_then(|it| it.syntax().parent().and_then(ast::CallExpr::cast))
.is_some(); .is_some();
self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
self.has_type_args = segment.type_arg_list().is_some();
if let Some(mut path) = hir::Path::from_ast(path.clone()) { if let Some(mut path) = hir::Path::from_ast(path.clone()) {
if !path.is_ident() { if !path.is_ident() {
path.segments.pop().unwrap(); path.segments.pop().unwrap();

View file

@ -1,12 +1,12 @@
//! This modules takes care of rendering various definitions as completion items. //! This modules takes care of rendering various definitions as completion items.
use hir::{Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk}; use hir::{db::HirDatabase, Docs, HasSource, HirDisplay, ScopeDef, Ty, TypeWalk};
use join_to_string::join; use join_to_string::join;
use ra_syntax::ast::NameOwner; use ra_syntax::ast::NameOwner;
use test_utils::tested_by; use test_utils::tested_by;
use crate::completion::{ use crate::completion::{
CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, db, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
}; };
use crate::display::{const_label, function_label, macro_label, type_label}; use crate::display::{const_label, function_label, macro_label, type_label};
@ -150,7 +150,8 @@ impl Completions {
}) })
.set_documentation(func.docs(ctx.db)) .set_documentation(func.docs(ctx.db))
.detail(detail); .detail(detail);
// If not an import, add parenthesis automatically.
// Add `<>` for generic types
if ctx.use_item_syntax.is_none() if ctx.use_item_syntax.is_none()
&& !ctx.is_call && !ctx.is_call
&& ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis") && ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
@ -164,11 +165,13 @@ impl Completions {
}; };
builder = builder.insert_snippet(snippet); builder = builder.insert_snippet(snippet);
} }
self.add(builder) self.add(builder)
} }
fn add_adt_with_name(&mut self, ctx: &CompletionContext, name: String, adt: hir::Adt) { fn add_adt_with_name(&mut self, ctx: &CompletionContext, name: String, adt: hir::Adt) {
let builder = CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name); let mut builder =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.clone());
let kind = match adt { let kind = match adt {
hir::Adt::Struct(_) => CompletionItemKind::Struct, hir::Adt::Struct(_) => CompletionItemKind::Struct,
@ -178,6 +181,17 @@ impl Completions {
}; };
let docs = adt.docs(ctx.db); let docs = adt.docs(ctx.db);
// If not an import, add parenthesis automatically.
if ctx.is_path_type
&& !ctx.has_type_args
&& ctx.db.feature_flags.get("completion.insertion.add-call-parenthesis")
{
if has_non_default_type_params(adt, ctx.db) {
tested_by!(inserts_angle_brackets_for_generics);
builder = builder.insert_snippet(format!("{}<$0>", name));
}
}
builder.kind(kind).set_documentation(docs).add_to(self) builder.kind(kind).set_documentation(docs).add_to(self)
} }
@ -221,7 +235,6 @@ impl Completions {
.separator(", ") .separator(", ")
.surround_with("(", ")") .surround_with("(", ")")
.to_string(); .to_string();
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
.kind(CompletionItemKind::EnumVariant) .kind(CompletionItemKind::EnumVariant)
.set_documentation(variant.docs(ctx.db)) .set_documentation(variant.docs(ctx.db))
@ -230,6 +243,11 @@ impl Completions {
} }
} }
fn has_non_default_type_params(adt: hir::Adt, db: &db::RootDatabase) -> bool {
let subst = db.generic_defaults(adt.into());
subst.iter().any(|ty| ty == &Ty::Unknown)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::completion::{do_completion, CompletionItem, CompletionKind}; use crate::completion::{do_completion, CompletionItem, CompletionKind};
@ -397,4 +415,90 @@ mod tests {
]"# ]"#
); );
} }
#[test]
fn inserts_angle_brackets_for_generics() {
covers!(inserts_angle_brackets_for_generics);
assert_debug_snapshot!(
do_reference_completion(
r"
struct Vec<T> {}
fn foo(xs: Ve<|>)
"
),
@r###"
[
CompletionItem {
label: "Vec",
source_range: [61; 63),
delete: [61; 63),
insert: "Vec<$0>",
kind: Struct,
},
CompletionItem {
label: "foo",
source_range: [61; 63),
delete: [61; 63),
insert: "foo($0)",
kind: Function,
detail: "fn foo(xs: Ve)",
},
]
"###
);
assert_debug_snapshot!(
do_reference_completion(
r"
struct Vec<T = i128> {}
fn foo(xs: Ve<|>)
"
),
@r###"
[
CompletionItem {
label: "Vec",
source_range: [68; 70),
delete: [68; 70),
insert: "Vec",
kind: Struct,
},
CompletionItem {
label: "foo",
source_range: [68; 70),
delete: [68; 70),
insert: "foo($0)",
kind: Function,
detail: "fn foo(xs: Ve)",
},
]
"###
);
assert_debug_snapshot!(
do_reference_completion(
r"
struct Vec<T> {}
fn foo(xs: Ve<|><i128>)
"
),
@r###"
[
CompletionItem {
label: "Vec",
source_range: [61; 63),
delete: [61; 63),
insert: "Vec",
kind: Struct,
},
CompletionItem {
label: "foo",
source_range: [61; 63),
delete: [61; 63),
insert: "foo($0)",
kind: Function,
detail: "fn foo(xs: Ve<i128>)",
},
]
"###
);
}
} }

View file

@ -1,6 +1,7 @@
//! See test_utils/src/marks.rs //! See test_utils/src/marks.rs
test_utils::marks!( test_utils::marks!(
inserts_angle_brackets_for_generics
inserts_parens_for_function_calls inserts_parens_for_function_calls
goto_definition_works_for_macros goto_definition_works_for_macros
goto_definition_works_for_methods goto_definition_works_for_methods