mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 13:18:47 +00:00
organize completion tests better
This commit is contained in:
parent
d4ef07b235
commit
45232dfa68
5 changed files with 488 additions and 393 deletions
|
@ -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""#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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"
|
||||
"##,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}};
|
||||
|
|
Loading…
Reference in a new issue