organize completion tests better

This commit is contained in:
Aleksey Kladov 2018-12-21 18:13:21 +03:00
parent d4ef07b235
commit 45232dfa68
5 changed files with 488 additions and 393 deletions

View file

@ -16,7 +16,7 @@ use hir::source_binder;
use crate::{
db,
Cancelable, FilePosition,
completion::completion_item::Completions,
completion::completion_item::{Completions, CompletionKind},
};
pub use crate::completion::completion_item::{CompletionItem, InsertText};
@ -81,7 +81,12 @@ fn param_completions(acc: &mut Completions, ctx: SyntaxNodeRef) {
Some((label, lookup))
}
})
.for_each(|(label, lookup)| CompletionItem::new(label).lookup_by(lookup).add_to(acc));
.for_each(|(label, lookup)| {
CompletionItem::new(label)
.lookup_by(lookup)
.kind(CompletionKind::Magic)
.add_to(acc)
});
fn process<'a, N: ast::FnDefOwner<'a>>(
node: N,
@ -104,342 +109,62 @@ fn is_node<'a, N: AstNode<'a>>(node: SyntaxNodeRef<'a>) -> bool {
}
}
#[cfg(test)]
fn check_completion(code: &str, expected_completions: &str, kind: CompletionKind) {
use crate::mock_analysis::{single_file_with_position, analysis_and_position};
let (analysis, position) = if code.contains("//-") {
analysis_and_position(code)
} else {
single_file_with_position(code)
};
let completions = completions(&analysis.imp.db, position).unwrap().unwrap();
completions.assert_match(expected_completions, kind);
}
#[cfg(test)]
mod tests {
use test_utils::assert_eq_dbg;
use crate::mock_analysis::single_file_with_position;
use super::*;
fn is_snippet(completion_item: &CompletionItem) -> bool {
match completion_item.insert_text() {
InsertText::Snippet { .. } => true,
_ => false,
}
}
fn check_scope_completion(code: &str, expected_completions: &str) {
let (analysis, position) = single_file_with_position(code);
let completions = completions(&analysis.imp.db, position)
.unwrap()
.unwrap()
.into_iter()
.filter(|c| !is_snippet(c))
.collect::<Vec<_>>();
assert_eq_dbg(expected_completions, &completions);
}
fn check_snippet_completion(code: &str, expected_completions: &str) {
let (analysis, position) = single_file_with_position(code);
let completions = completions(&analysis.imp.db, position)
.unwrap()
.unwrap()
.into_iter()
.filter(is_snippet)
.collect::<Vec<_>>();
assert_eq_dbg(expected_completions, &completions);
}
#[test]
fn test_completion_let_scope() {
check_scope_completion(
r"
fn quux(x: i32) {
let y = 92;
1 + <|>;
let z = ();
}
",
r#"[CompletionItem { label: "y", lookup: None, snippet: None },
CompletionItem { label: "x", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_completion_if_let_scope() {
check_scope_completion(
r"
fn quux() {
if let Some(x) = foo() {
let y = 92;
};
if let Some(a) = bar() {
let b = 62;
1 + <|>
}
}
",
r#"[CompletionItem { label: "b", lookup: None, snippet: None },
CompletionItem { label: "a", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_completion_for_scope() {
check_scope_completion(
r"
fn quux() {
for x in &[1, 2, 3] {
<|>
}
}
",
r#"[CompletionItem { label: "x", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_completion_mod_scope() {
check_scope_completion(
r"
struct Foo;
enum Baz {}
fn quux() {
<|>
}
",
r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
CompletionItem { label: "Foo", lookup: None, snippet: None },
CompletionItem { label: "Baz", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_completion_mod_scope_no_self_use() {
check_scope_completion(
r"
use foo<|>;
",
r#"[]"#,
);
}
#[test]
fn test_completion_self_path() {
check_scope_completion(
r"
use self::m::<|>;
mod m {
struct Bar;
}
",
r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_completion_mod_scope_nested() {
check_scope_completion(
r"
struct Foo;
mod m {
struct Bar;
fn quux() { <|> }
}
",
r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
);
}
#[test]
fn test_complete_type() {
check_scope_completion(
r"
struct Foo;
fn x() -> <|>
",
r#"[CompletionItem { label: "Foo", lookup: None, snippet: None },
CompletionItem { label: "x", lookup: None, snippet: None }]"#,
)
}
#[test]
fn test_complete_shadowing() {
check_scope_completion(
r"
fn foo() -> {
let bar = 92;
{
let bar = 62;
<|>
}
}
",
r#"[CompletionItem { label: "bar", lookup: None, snippet: None },
CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
)
}
#[test]
fn test_complete_self() {
check_scope_completion(
r"
impl S { fn foo(&self) { <|> } }
",
r#"[CompletionItem { label: "self", lookup: None, snippet: None }]"#,
)
}
#[test]
fn test_completion_kewords() {
check_snippet_completion(r"
fn quux() {
<|>
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
}
#[test]
fn test_completion_else() {
check_snippet_completion(r"
fn quux() {
if true {
()
} <|>
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "else", lookup: None, snippet: Some("else {$0}") },
CompletionItem { label: "else if", lookup: None, snippet: Some("else if $0 {}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
}
#[test]
fn test_completion_return_value() {
check_snippet_completion(r"
fn quux() -> i32 {
<|>
92
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return $0;") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
check_snippet_completion(r"
fn quux() {
<|>
92
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return;") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
}
#[test]
fn test_completion_return_no_stmt() {
check_snippet_completion(r"
fn quux() -> i32 {
match () {
() => <|>
}
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
}
#[test]
fn test_continue_break_completion() {
check_snippet_completion(r"
fn quux() -> i32 {
loop { <|> }
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "continue", lookup: None, snippet: Some("continue") },
CompletionItem { label: "break", lookup: None, snippet: Some("break") },
CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
check_snippet_completion(r"
fn quux() -> i32 {
loop { || { <|> } }
}
", r#"[CompletionItem { label: "if", lookup: None, snippet: Some("if $0 {}") },
CompletionItem { label: "match", lookup: None, snippet: Some("match $0 {}") },
CompletionItem { label: "while", lookup: None, snippet: Some("while $0 {}") },
CompletionItem { label: "loop", lookup: None, snippet: Some("loop {$0}") },
CompletionItem { label: "return", lookup: None, snippet: Some("return $0") },
CompletionItem { label: "pd", lookup: None, snippet: Some("eprintln!(\"$0 = {:?}\", $0);") },
CompletionItem { label: "ppd", lookup: None, snippet: Some("eprintln!(\"$0 = {:#?}\", $0);") }]"#);
fn check_magic_completion(code: &str, expected_completions: &str) {
check_completion(code, expected_completions, CompletionKind::Magic);
}
#[test]
fn test_param_completion_last_param() {
check_scope_completion(r"
check_magic_completion(
r"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file<|>) {}
", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
",
r#"file_id "file_id: FileId""#,
);
}
#[test]
fn test_param_completion_nth_param() {
check_scope_completion(r"
check_magic_completion(
r"
fn foo(file_id: FileId) {}
fn bar(file_id: FileId) {}
fn baz(file<|>, x: i32) {}
", r#"[CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
",
r#"file_id "file_id: FileId""#,
);
}
#[test]
fn test_param_completion_trait_param() {
check_scope_completion(r"
check_magic_completion(
r"
pub(crate) trait SourceRoot {
pub fn contains(&self, file_id: FileId) -> bool;
pub fn module_map(&self) -> &ModuleMap;
pub fn lines(&self, file_id: FileId) -> &LineIndex;
pub fn syntax(&self, file<|>)
}
", r#"[CompletionItem { label: "self", lookup: None, snippet: None },
CompletionItem { label: "SourceRoot", lookup: None, snippet: None },
CompletionItem { label: "file_id: FileId", lookup: Some("file_id"), snippet: None }]"#);
}
#[test]
fn test_item_snippets() {
// check_snippet_completion(r"
// <|>
// ",
// r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
// );
check_snippet_completion(r"
#[cfg(test)]
mod tests {
<|>
}
",
r##"[CompletionItem { label: "Test function", lookup: Some("tfn"), snippet: Some("#[test]\nfn ${1:feature}() {\n $0\n}") },
CompletionItem { label: "pub(crate)", lookup: None, snippet: Some("pub(crate) $0") }]"##,
r#"file_id "file_id: FileId""#,
);
}
}

View file

@ -6,6 +6,8 @@ pub struct CompletionItem {
label: String,
lookup: Option<String>,
snippet: Option<String>,
/// Used only internally in test, to check only specific kind of completion.
kind: CompletionKind,
}
pub enum InsertText {
@ -13,6 +15,18 @@ pub enum InsertText {
Snippet { text: String },
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum CompletionKind {
/// Parser-based keyword completion.
Keyword,
/// Your usual "complete all valid identifiers".
Reference,
/// "Secret sauce" completions.
Magic,
Snippet,
Unspecified,
}
impl CompletionItem {
pub(crate) fn new(label: impl Into<String>) -> Builder {
let label = label.into();
@ -20,6 +34,7 @@ impl CompletionItem {
label,
lookup: None,
snippet: None,
kind: CompletionKind::Unspecified,
}
}
/// What user sees in pop-up in the UI.
@ -50,28 +65,34 @@ pub(crate) struct Builder {
label: String,
lookup: Option<String>,
snippet: Option<String>,
kind: CompletionKind,
}
impl Builder {
pub fn add_to(self, acc: &mut Completions) {
pub(crate) fn add_to(self, acc: &mut Completions) {
acc.add(self.build())
}
pub fn build(self) -> CompletionItem {
pub(crate) fn build(self) -> CompletionItem {
CompletionItem {
label: self.label,
lookup: self.lookup,
snippet: self.snippet,
kind: self.kind,
}
}
pub fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
self.lookup = Some(lookup.into());
self
}
pub fn snippet(mut self, snippet: impl Into<String>) -> Builder {
pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> Builder {
self.snippet = Some(snippet.into());
self
}
pub(crate) fn kind(mut self, kind: CompletionKind) -> Builder {
self.kind = kind;
self
}
}
impl Into<CompletionItem> for Builder {
@ -97,6 +118,57 @@ impl Completions {
{
items.into_iter().for_each(|item| self.add(item.into()))
}
#[cfg(test)]
pub(crate) fn assert_match(&self, expected: &str, kind: CompletionKind) {
let expected = normalize(expected);
let actual = self.debug_render(kind);
test_utils::assert_eq_text!(expected.as_str(), actual.as_str(),);
/// Normalize the textual representation of `Completions`:
/// replace `;` with newlines, normalize whitespace
fn normalize(expected: &str) -> String {
use ra_syntax::{tokenize, TextUnit, TextRange, SyntaxKind::SEMI};
let mut res = String::new();
for line in expected.trim().lines() {
let line = line.trim();
let mut start_offset: TextUnit = 0.into();
// Yep, we use rust tokenize in completion tests :-)
for token in tokenize(line) {
let range = TextRange::offset_len(start_offset, token.len);
start_offset += token.len;
if token.kind == SEMI {
res.push('\n');
} else {
res.push_str(&line[range]);
}
}
res.push('\n');
}
res
}
}
#[cfg(test)]
fn debug_render(&self, kind: CompletionKind) -> String {
let mut res = String::new();
for c in self.buf.iter() {
if c.kind == kind {
if let Some(lookup) = &c.lookup {
res.push_str(lookup);
res.push_str(&format!(" {:?}", c.label));
} else {
res.push_str(&c.label);
}
if let Some(snippet) = &c.snippet {
res.push_str(&format!(" {:?}", snippet));
}
res.push('\n');
}
}
res
}
}
impl Into<Vec<CompletionItem>> for Completions {

View file

@ -13,7 +13,7 @@ use hir::{
use crate::{
db::RootDatabase,
completion::{CompletionItem, Completions},
completion::{CompletionItem, Completions, CompletionKind::*},
Cancelable
};
@ -51,7 +51,11 @@ pub(super) fn completions(
}
}
})
.for_each(|(name, _res)| CompletionItem::new(name.to_string()).add_to(acc));
.for_each(|(name, _res)| {
CompletionItem::new(name.to_string())
.kind(Reference)
.add_to(acc)
});
}
NameRefKind::Path(path) => complete_path(acc, db, module, path)?,
NameRefKind::BareIdentInMod => {
@ -123,9 +127,13 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Completions)
.scope_chain(name_ref.syntax())
.flat_map(|scope| scopes.entries(scope).iter())
.filter(|entry| shadowed.insert(entry.name()))
.for_each(|entry| CompletionItem::new(entry.name().to_string()).add_to(acc));
.for_each(|entry| {
CompletionItem::new(entry.name().to_string())
.kind(Reference)
.add_to(acc)
});
if scopes.self_param.is_some() {
CompletionItem::new("self").add_to(acc);
CompletionItem::new("self").kind(Reference).add_to(acc);
}
}
@ -148,9 +156,11 @@ fn complete_path(
_ => return Ok(()),
};
let module_scope = target_module.scope(db)?;
module_scope
.entries()
.for_each(|(name, _res)| CompletionItem::new(name.to_string()).add_to(acc));
module_scope.entries().for_each(|(name, _res)| {
CompletionItem::new(name.to_string())
.kind(Reference)
.add_to(acc)
});
Ok(())
}
@ -164,9 +174,11 @@ fn ${1:feature}() {
$0
}",
)
.kind(Snippet)
.add_to(acc);
CompletionItem::new("pub(crate)")
.snippet("pub(crate) $0")
.kind(Snippet)
.add_to(acc);
}
@ -249,14 +261,362 @@ fn complete_return(fn_def: ast::FnDef, name_ref: ast::NameRef) -> Option<Complet
}
fn keyword(kw: &str, snippet: &str) -> CompletionItem {
CompletionItem::new(kw).snippet(snippet).build()
CompletionItem::new(kw)
.kind(Keyword)
.snippet(snippet)
.build()
}
fn complete_expr_snippets(acc: &mut Completions) {
CompletionItem::new("pd")
.snippet("eprintln!(\"$0 = {:?}\", $0);")
.kind(Snippet)
.add_to(acc);
CompletionItem::new("ppd")
.snippet("eprintln!(\"$0 = {:#?}\", $0);")
.kind(Snippet)
.add_to(acc);
}
#[cfg(test)]
mod tests {
use crate::completion::{CompletionKind, check_completion};
fn check_reference_completion(code: &str, expected_completions: &str) {
check_completion(code, expected_completions, CompletionKind::Reference);
}
fn check_keyword_completion(code: &str, expected_completions: &str) {
check_completion(code, expected_completions, CompletionKind::Keyword);
}
fn check_snippet_completion(code: &str, expected_completions: &str) {
check_completion(code, expected_completions, CompletionKind::Snippet);
}
#[test]
fn test_completion_let_scope() {
check_reference_completion(
r"
fn quux(x: i32) {
let y = 92;
1 + <|>;
let z = ();
}
",
"y;x;quux",
);
}
#[test]
fn test_completion_if_let_scope() {
check_reference_completion(
r"
fn quux() {
if let Some(x) = foo() {
let y = 92;
};
if let Some(a) = bar() {
let b = 62;
1 + <|>
}
}
",
"b;a;quux",
);
}
#[test]
fn test_completion_for_scope() {
check_reference_completion(
r"
fn quux() {
for x in &[1, 2, 3] {
<|>
}
}
",
"x;quux",
);
}
#[test]
fn test_completion_mod_scope() {
check_reference_completion(
r"
struct Foo;
enum Baz {}
fn quux() {
<|>
}
",
"quux;Foo;Baz",
);
}
#[test]
fn test_completion_mod_scope_no_self_use() {
check_reference_completion(
r"
use foo<|>;
",
"",
);
}
#[test]
fn test_completion_self_path() {
check_reference_completion(
r"
use self::m::<|>;
mod m {
struct Bar;
}
",
"Bar",
);
}
#[test]
fn test_completion_mod_scope_nested() {
check_reference_completion(
r"
struct Foo;
mod m {
struct Bar;
fn quux() { <|> }
}
",
"quux;Bar",
);
}
#[test]
fn test_complete_type() {
check_reference_completion(
r"
struct Foo;
fn x() -> <|>
",
"Foo;x",
)
}
#[test]
fn test_complete_shadowing() {
check_reference_completion(
r"
fn foo() -> {
let bar = 92;
{
let bar = 62;
<|>
}
}
",
"bar;foo",
)
}
#[test]
fn test_complete_self() {
check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
}
#[test]
fn test_complete_crate_path() {
check_reference_completion(
"
//- /lib.rs
mod foo;
struct Spam;
//- /foo.rs
use crate::Sp<|>
",
"Spam;foo",
);
}
#[test]
fn test_complete_crate_path_with_braces() {
check_reference_completion(
"
//- /lib.rs
mod foo;
struct Spam;
//- /foo.rs
use crate::{Sp<|>};
",
"Spam;foo",
);
}
#[test]
fn test_complete_crate_path_in_nested_tree() {
check_reference_completion(
"
//- /lib.rs
mod foo;
pub mod bar {
pub mod baz {
pub struct Spam;
}
}
//- /foo.rs
use crate::{bar::{baz::Sp<|>}};
",
"Spam",
);
}
#[test]
fn test_completion_kewords() {
check_keyword_completion(
r"
fn quux() {
<|>
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
return "return"
"#,
);
}
#[test]
fn test_completion_else() {
check_keyword_completion(
r"
fn quux() {
if true {
()
} <|>
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
else "else {$0}"
else if "else if $0 {}"
return "return"
"#,
);
}
#[test]
fn test_completion_return_value() {
check_keyword_completion(
r"
fn quux() -> i32 {
<|>
92
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
return "return $0;"
"#,
);
check_keyword_completion(
r"
fn quux() {
<|>
92
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
return "return;"
"#,
);
}
#[test]
fn test_completion_return_no_stmt() {
check_keyword_completion(
r"
fn quux() -> i32 {
match () {
() => <|>
}
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
return "return $0"
"#,
);
}
#[test]
fn test_continue_break_completion() {
check_keyword_completion(
r"
fn quux() -> i32 {
loop { <|> }
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
continue "continue"
break "break"
return "return $0"
"#,
);
check_keyword_completion(
r"
fn quux() -> i32 {
loop { || { <|> } }
}
",
r#"
if "if $0 {}"
match "match $0 {}"
while "while $0 {}"
loop "loop {$0}"
return "return $0"
"#,
);
}
#[test]
fn test_item_snippets() {
// check_snippet_completion(r"
// <|>
// ",
// r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##,
// );
check_snippet_completion(
r"
#[cfg(test)]
mod tests {
<|>
}
",
r##"
tfn "Test function" "#[test]\nfn ${1:feature}() {\n $0\n}"
pub(crate) "pub(crate) $0"
"##,
);
}
}

View file

@ -452,63 +452,3 @@ fn test_find_all_refs_for_fn_param() {
let refs = get_all_refs(code);
assert_eq!(refs.len(), 2);
}
#[test]
fn test_complete_crate_path() {
let (analysis, position) = analysis_and_position(
"
//- /lib.rs
mod foo;
struct Spam;
//- /foo.rs
use crate::Sp<|>
",
);
let completions = analysis.completions(position).unwrap().unwrap();
assert_eq_dbg(
r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
&completions,
);
}
#[test]
fn test_complete_crate_path_with_braces() {
let (analysis, position) = analysis_and_position(
"
//- /lib.rs
mod foo;
struct Spam;
//- /foo.rs
use crate::{Sp<|>};
",
);
let completions = analysis.completions(position).unwrap().unwrap();
assert_eq_dbg(
r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
&completions,
);
}
#[test]
fn test_complete_crate_path_in_nested_tree() {
let (analysis, position) = analysis_and_position(
"
//- /lib.rs
mod foo;
pub mod bar {
pub mod baz {
pub struct Spam;
}
}
//- /foo.rs
use crate::{bar::{baz::Sp<|>}};
",
);
let completions = analysis.completions(position).unwrap().unwrap();
assert_eq_dbg(
r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
&completions,
);
}

View file

@ -10,22 +10,20 @@ pub const CURSOR_MARKER: &str = "<|>";
#[macro_export]
macro_rules! assert_eq_text {
($expected:expr, $actual:expr) => {{
let expected = $expected;
let actual = $actual;
if expected != actual {
let changeset = $crate::__Changeset::new(actual, expected, "\n");
println!("Expected:\n{}\n\nActual:\n{}\nDiff:{}\n", expected, actual, changeset);
panic!("text differs");
}
}};
($expected:expr, $actual:expr) => {
assert_eq_text!($expected, $actual,)
};
($expected:expr, $actual:expr, $($tt:tt)*) => {{
let expected = $expected;
let actual = $actual;
if expected != actual {
let changeset = $crate::__Changeset::new(actual, expected, "\n");
println!("Expected:\n{}\n\nActual:\n{}\n\nDiff:\n{}\n", expected, actual, changeset);
println!($($tt)*);
if expected.trim() == actual.trim() {
eprintln!("Expected:\n{:?}\n\nActual:\n{:?}\n\nWhitespace difference\n", expected, actual);
} else {
let changeset = $crate::__Changeset::new(actual, expected, "\n");
eprintln!("Expected:\n{}\n\nActual:\n{}\n\nDiff:\n{}\n", expected, actual, changeset);
}
eprintln!($($tt)*);
panic!("text differs");
}
}};