Start refactoring ide_completion tests

This commit is contained in:
Lukas Wirth 2021-06-16 17:37:23 +02:00
parent f38770cd26
commit d338a80394
9 changed files with 306 additions and 251 deletions

View File

@ -41,9 +41,9 @@ pub struct Completions {
buf: Vec<CompletionItem>,
}
impl Into<Vec<CompletionItem>> for Completions {
fn into(self) -> Vec<CompletionItem> {
self.buf
impl From<Completions> for Vec<CompletionItem> {
fn from(val: Completions) -> Self {
val.buf
}
}
@ -74,35 +74,6 @@ impl Completions {
items.into_iter().for_each(|item| self.add(item.into()))
}
pub(crate) fn add_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: hir::Field,
ty: &hir::Type,
) {
let item = render_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_tuple_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: usize,
ty: &hir::Type,
) {
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
let mut item =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
self.add(item.build());
}
pub(crate) fn add_resolution(
&mut self,
ctx: &CompletionContext,
@ -144,33 +115,6 @@ impl Completions {
self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
}
pub(crate) fn add_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
}
pub(crate) fn add_qualified_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
path: hir::ModPath,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
}
pub(crate) fn add_struct_pat(
&mut self,
ctx: &CompletionContext,
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
}
pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
self.add_opt(render_const(RenderContext::new(ctx), constant));
}
@ -206,10 +150,67 @@ impl Completions {
let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
self.add(item);
}
pub(crate) fn add_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: hir::Field,
ty: &hir::Type,
) {
let item = render_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_tuple_field(
&mut self,
ctx: &CompletionContext,
receiver: Option<hir::Name>,
field: usize,
ty: &hir::Type,
) {
let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
self.add(item);
}
pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
let mut item =
CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
self.add(item.build());
}
pub(crate) fn add_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
local_name: Option<hir::Name>,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
}
pub(crate) fn add_qualified_variant_pat(
&mut self,
ctx: &CompletionContext,
variant: hir::Variant,
path: hir::ModPath,
) {
self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
}
pub(crate) fn add_struct_pat(
&mut self,
ctx: &CompletionContext,
strukt: hir::Struct,
local_name: Option<hir::Name>,
) {
self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
}
}
/// Calls the callback for each variant of the provided enum with the path to the variant.
fn complete_enum_variants(
/// Skips variants that are visible with single segment paths.
fn enum_variants_with_paths(
acc: &mut Completions,
ctx: &CompletionContext,
enum_: hir::Enum,

View File

@ -230,30 +230,6 @@ mod tests {
);
}
#[test]
fn test_keywords_at_source_file_level() {
check(
r"m$0",
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
"#]],
);
}
#[test]
fn test_keywords_in_function() {
check(
@ -442,18 +418,6 @@ fn quux() -> i32 {
);
}
#[test]
fn test_keywords_after_unsafe_in_item_list() {
check(
r"unsafe $0",
expect![[r#"
kw fn
kw trait
kw impl
"#]],
);
}
#[test]
fn test_keywords_after_unsafe_in_block_expr() {
check(

View File

@ -13,7 +13,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
if let Some(hir::Adt::Enum(e)) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
{
super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
acc.add_qualified_variant_pat(ctx, variant, path.clone());
acc.add_qualified_enum_variant(ctx, variant, path);
});

View File

@ -713,24 +713,6 @@ impl MyStruct {
);
}
#[test]
fn completes_in_item_list() {
check(
r#"
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
crate::$0
"#,
expect![[r#"
md bar
ma foo!() #[macro_export] macro_rules! foo
"#]],
)
}
#[test]
fn test_super_super_completion() {
check(

View File

@ -105,21 +105,4 @@ mod tests {
check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
}
#[test]
fn completes_snippets_in_items() {
check(
r#"
#[cfg(test)]
mod tests {
$0
}
"#,
expect![[r#"
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
"#]],
)
}
}

View File

@ -40,7 +40,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
if let Some(hir::Adt::Enum(e)) =
ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
{
super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| {
super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
acc.add_qualified_enum_variant(ctx, variant, path)
});
}

View File

@ -1,14 +1,16 @@
//! `completions` crate provides utilities for generating completions of user input.
mod completions;
mod config;
mod item;
mod context;
mod item;
mod patterns;
#[cfg(test)]
mod test_utils;
mod render;
mod completions;
#[cfg(test)]
mod tests;
#[cfg(test)]
mod test_utils;
use completions::flyimport::position_for_import;
use ide_db::{
@ -141,6 +143,7 @@ pub fn completions(
let ctx = CompletionContext::new(db, position, config)?;
if ctx.no_completion_required() {
cov_mark::hit!(no_completion_required);
// No work required here.
return None;
}
@ -200,117 +203,3 @@ pub fn resolve_completion_edits(
ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
}
#[cfg(test)]
mod tests {
use crate::test_utils::{self, TEST_CONFIG};
struct DetailAndDocumentation<'a> {
detail: &'a str,
documentation: &'a str,
}
fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
let (db, position) = test_utils::position(ra_fixture);
let config = TEST_CONFIG;
let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
for item in completions {
if item.detail() == Some(expected.detail) {
let opt = item.documentation();
let doc = opt.as_ref().map(|it| it.as_str());
assert_eq!(doc, Some(expected.documentation));
return;
}
}
panic!("completion detail not found: {}", expected.detail)
}
fn check_no_completion(ra_fixture: &str) {
let (db, position) = test_utils::position(ra_fixture);
let config = TEST_CONFIG;
let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
.and_then(|completions| {
let completions: Vec<_> = completions.into();
if completions.is_empty() {
None
} else {
Some(completions)
}
})
.map(|completions| {
completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
});
// `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
assert_eq!(completions, None, "Completions were generated, but weren't expected");
}
#[test]
fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
check_detail_and_documentation(
r#"
macro_rules! bar {
() => {
struct Bar;
impl Bar {
#[doc = "Do the foo"]
fn foo(&self) {}
}
}
}
bar!();
fn foo() {
let bar = Bar;
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
);
}
#[test]
fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
check_detail_and_documentation(
r#"
macro_rules! bar {
() => {
struct Bar;
impl Bar {
/// Do the foo
fn foo(&self) {}
}
}
}
bar!();
fn foo() {
let bar = Bar;
bar.fo$0;
}
"#,
DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
);
}
#[test]
fn test_no_completions_required() {
// There must be no hint for 'in' keyword.
check_no_completion(r#"fn foo() { for i i$0 }"#);
// After 'in' keyword hints may be spawned.
check_detail_and_documentation(
r#"
/// Do the foo
fn foo() -> &'static str { "foo" }
fn bar() {
for c in fo$0
}
"#,
DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" },
);
}
}

View File

@ -0,0 +1,64 @@
mod item_list;
use expect_test::Expect;
use stdx::format_to;
use crate::{
test_utils::{self, get_all_items, TEST_CONFIG},
CompletionConfig, CompletionItem,
};
fn completion_list(code: &str) -> String {
completion_list_with_config(TEST_CONFIG, code)
}
fn completion_list_with_config(config: CompletionConfig, code: &str) -> String {
fn monospace_width(s: &str) -> usize {
s.chars().count()
}
let kind_completions: Vec<CompletionItem> = get_all_items(config, code).into_iter().collect();
let label_width = kind_completions
.iter()
.map(|it| monospace_width(it.label()))
.max()
.unwrap_or_default()
.min(16);
kind_completions
.into_iter()
.map(|it| {
let tag = it.kind().unwrap().tag();
let var_name = format!("{} {}", tag, it.label());
let mut buf = var_name;
if let Some(detail) = it.detail() {
let width = label_width.saturating_sub(monospace_width(it.label()));
format_to!(buf, "{:width$} {}", "", detail, width = width);
}
if it.deprecated() {
format_to!(buf, " DEPRECATED");
}
format_to!(buf, "\n");
buf
})
.collect()
}
fn check(ra_fixture: &str, expect: Expect) {
let actual = completion_list(ra_fixture);
expect.assert_eq(&actual)
}
fn check_no_completion(ra_fixture: &str) {
let (db, position) = test_utils::position(ra_fixture);
assert!(
crate::completions(&db, &TEST_CONFIG, position).is_none(),
"Completions were generated, but weren't expected"
);
}
#[test]
fn test_no_completions_required() {
cov_mark::check!(no_completion_required);
check_no_completion(r#"fn foo() { for i i$0 }"#);
}

View File

@ -0,0 +1,172 @@
use expect_test::expect;
use crate::tests::check;
#[test]
fn in_mod_item_list() {
check(
r#"mod tests {
$0
}
"#,
expect![[r#"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
"#]],
)
}
#[test]
fn in_source_file_item_list() {
check(
r#"
enum Enum { Variant }
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
const CONST: () = ();
$0"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn in_qualified_path() {
check(
r#"
enum Enum { Variant }
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
const CONST: () = ();
crate::$0"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
md bar
ma foo!() #[macro_export] macro_rules! foo
"##]],
)
}
#[test]
fn after_unsafe_token() {
check(
r#"
enum Enum { Variant }
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
const CONST: () = ();
unsafe $0"#,
expect![[r##"
kw fn
kw trait
kw impl
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
);
}
#[test]
fn after_visibility() {
check(
r#"
enum Enum { Variant }
struct MyStruct {}
#[macro_export]
macro_rules! foo {}
mod bar {}
const CONST: () = ();
pub $0"#,
expect![[r##"
kw pub(crate)
kw pub
kw unsafe
kw fn
kw const
kw type
kw use
kw impl
kw trait
kw static
kw extern
kw mod
kw enum
kw struct
kw union
sn tmod (Test module)
sn tfn (Test function)
sn macro_rules
md bar
ma foo!() #[macro_export] macro_rules! foo
ma foo!() #[macro_export] macro_rules! foo
"##]],
);
}