rust/crates/ra_ide_api/src/completion/complete_scope.rs

552 lines
15 KiB
Rust
Raw Normal View History

use ra_assists::auto_import;
use ra_syntax::{ast, AstNode, SmolStr};
use ra_text_edit::TextEditBuilder;
use rustc_hash::FxHashMap;
use crate::completion::{CompletionContext, CompletionItem, CompletionKind, Completions};
2019-01-08 22:33:36 +03:00
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) {
if ctx.is_trivial_path {
let names = ctx.analyzer.all_names(ctx.db);
names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res));
2019-01-08 22:33:36 +03:00
// auto-import
// We fetch ident from the original file, because we need to pre-filter auto-imports
if ast::NameRef::cast(ctx.token.parent()).is_some() {
let import_resolver = ImportResolver::new();
let import_names = import_resolver.all_names(ctx.token.text());
import_names.into_iter().for_each(|(name, path)| {
let edit = {
let mut builder = TextEditBuilder::default();
builder.replace(ctx.source_range(), name.to_string());
auto_import::auto_import_text_edit(
2019-07-19 12:56:47 +03:00
&ctx.token.parent(),
&ctx.token.parent(),
&path,
&mut builder,
);
builder.finish()
};
// Hack: copied this check form conv.rs beacause auto import can produce edits
// that invalidate assert in conv_with.
if edit
.as_atoms()
.iter()
.filter(|atom| !ctx.source_range().is_subrange(&atom.delete))
.all(|atom| ctx.source_range().intersection(&atom.delete).is_none())
{
CompletionItem::new(
CompletionKind::Reference,
ctx.source_range(),
build_import_label(&name, &path),
)
.text_edit(edit)
.add_to(acc);
}
});
}
}
}
2019-06-04 01:51:25 -04:00
fn build_import_label(name: &str, path: &[SmolStr]) -> String {
let mut buf = String::with_capacity(64);
buf.push_str(name);
buf.push_str(" (");
fmt_import_path(path, &mut buf);
buf.push_str(")");
buf
}
2019-06-04 01:51:25 -04:00
fn fmt_import_path(path: &[SmolStr], buf: &mut String) {
let mut segments = path.iter();
if let Some(s) = segments.next() {
buf.push_str(&s);
}
for s in segments {
buf.push_str("::");
buf.push_str(&s);
}
2019-01-08 22:33:36 +03:00
}
#[derive(Debug, Clone, Default)]
pub(crate) struct ImportResolver {
// todo: use fst crate or something like that
dummy_names: Vec<(SmolStr, Vec<SmolStr>)>,
}
impl ImportResolver {
pub(crate) fn new() -> Self {
let dummy_names = vec![
(SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]),
(SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]),
(SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]),
(SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]),
(
SmolStr::new("Debug"),
vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")],
),
(
SmolStr::new("Display"),
vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")],
),
(
SmolStr::new("Hash"),
vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")],
),
(
SmolStr::new("Hasher"),
vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")],
),
(
SmolStr::new("Iterator"),
vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")],
),
];
ImportResolver { dummy_names }
}
// Returns a map of importable items filtered by name.
// The map associates item name with its full path.
// todo: should return Resolutions
pub(crate) fn all_names(&self, name: &str) -> FxHashMap<SmolStr, Vec<SmolStr>> {
if name.len() > 1 {
self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect()
} else {
FxHashMap::default()
}
}
}
2019-01-08 22:33:36 +03:00
#[cfg(test)]
mod tests {
use crate::completion::{do_completion, CompletionItem, CompletionKind};
use insta::assert_debug_snapshot_matches;
2019-01-08 22:33:36 +03:00
fn do_reference_completion(code: &str) -> Vec<CompletionItem> {
do_completion(code, CompletionKind::Reference)
2019-01-08 22:33:36 +03:00
}
#[test]
fn completes_bindings_from_let() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
fn quux(x: i32) {
let y = 92;
1 + <|>;
let z = ();
}
"
),
@r###"
[
CompletionItem {
label: "quux",
source_range: [91; 91),
delete: [91; 91),
insert: "quux($0)",
kind: Function,
detail: "fn quux(x: i32)",
},
CompletionItem {
label: "x",
source_range: [91; 91),
delete: [91; 91),
insert: "x",
kind: Binding,
detail: "i32",
},
CompletionItem {
label: "y",
source_range: [91; 91),
delete: [91; 91),
insert: "y",
kind: Binding,
detail: "i32",
},
]
"###
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn completes_bindings_from_if_let() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
fn quux() {
if let Some(x) = foo() {
let y = 92;
};
if let Some(a) = bar() {
let b = 62;
1 + <|>
}
2019-01-08 22:33:36 +03:00
}
"
),
@r###"
[
CompletionItem {
label: "a",
source_range: [242; 242),
delete: [242; 242),
insert: "a",
kind: Binding,
},
CompletionItem {
label: "b",
source_range: [242; 242),
delete: [242; 242),
insert: "b",
kind: Binding,
detail: "i32",
},
CompletionItem {
label: "quux",
source_range: [242; 242),
delete: [242; 242),
insert: "quux()$0",
kind: Function,
detail: "fn quux()",
},
]
"###
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn completes_bindings_from_for() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
fn quux() {
for x in &[1, 2, 3] {
<|>
}
2019-01-08 22:33:36 +03:00
}
"
),
@r###"
[
CompletionItem {
label: "quux",
source_range: [95; 95),
delete: [95; 95),
insert: "quux()$0",
kind: Function,
detail: "fn quux()",
},
CompletionItem {
label: "x",
source_range: [95; 95),
delete: [95; 95),
insert: "x",
kind: Binding,
},
]
"###
2019-01-08 22:33:36 +03:00
);
}
2019-02-01 23:06:57 +01:00
#[test]
fn completes_generic_params() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
fn quux<T>() {
<|>
}
"
),
@r###"
[
CompletionItem {
label: "T",
source_range: [52; 52),
delete: [52; 52),
insert: "T",
kind: TypeParam,
},
CompletionItem {
label: "quux",
source_range: [52; 52),
delete: [52; 52),
insert: "quux()$0",
kind: Function,
detail: "fn quux<T>()",
},
]
"###
2019-02-01 23:06:57 +01:00
);
}
#[test]
fn completes_generic_params_in_struct() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
struct X<T> {
x: <|>
}
"
),
@r###"
[
CompletionItem {
label: "T",
source_range: [54; 54),
delete: [54; 54),
insert: "T",
kind: TypeParam,
},
CompletionItem {
label: "X",
source_range: [54; 54),
delete: [54; 54),
insert: "X",
kind: Struct,
},
]
"###
2019-02-01 23:06:57 +01:00
);
}
2019-01-08 22:33:36 +03:00
#[test]
fn completes_module_items() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
struct Foo;
enum Baz {}
fn quux() {
<|>
}
"
),
@r###"
[
CompletionItem {
label: "Baz",
source_range: [105; 105),
delete: [105; 105),
insert: "Baz",
kind: Enum,
},
CompletionItem {
label: "Foo",
source_range: [105; 105),
delete: [105; 105),
insert: "Foo",
kind: Struct,
},
CompletionItem {
label: "quux",
source_range: [105; 105),
delete: [105; 105),
insert: "quux()$0",
kind: Function,
detail: "fn quux()",
},
]
"###
2019-01-08 22:33:36 +03:00
);
}
#[test]
2019-02-04 22:09:56 +01:00
fn completes_extern_prelude() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
//- /lib.rs
use <|>;
2019-02-04 22:09:56 +01:00
//- /other_crate/lib.rs
// nothing here
"
),
@r#"[
CompletionItem {
label: "other_crate",
source_range: [4; 4),
delete: [4; 4),
insert: "other_crate",
kind: Module,
},
]"#
2019-02-04 22:09:56 +01:00
);
}
#[test]
2019-01-08 22:33:36 +03:00
fn completes_module_items_in_nested_modules() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
struct Foo;
mod m {
struct Bar;
fn quux() { <|> }
}
"
),
@r###"
[
CompletionItem {
label: "Bar",
source_range: [117; 117),
delete: [117; 117),
insert: "Bar",
kind: Struct,
},
CompletionItem {
label: "quux",
source_range: [117; 117),
delete: [117; 117),
insert: "quux()$0",
kind: Function,
detail: "fn quux()",
},
]
"###
2019-01-08 22:33:36 +03:00
);
}
#[test]
fn completes_return_type() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
struct Foo;
fn x() -> <|>
"
),
@r###"
[
CompletionItem {
label: "Foo",
source_range: [55; 55),
delete: [55; 55),
insert: "Foo",
kind: Struct,
},
CompletionItem {
label: "x",
source_range: [55; 55),
delete: [55; 55),
insert: "x()$0",
kind: Function,
detail: "fn x()",
},
]
"###
);
2019-01-08 22:33:36 +03:00
}
#[test]
fn dont_show_both_completions_for_shadowing() {
assert_debug_snapshot_matches!(
do_reference_completion(
r"
fn foo() {
let bar = 92;
{
let bar = 62;
<|>
}
2019-01-08 22:33:36 +03:00
}
"
),
@r###"
[
CompletionItem {
label: "bar",
source_range: [146; 146),
delete: [146; 146),
insert: "bar",
kind: Binding,
detail: "i32",
},
CompletionItem {
label: "foo",
source_range: [146; 146),
delete: [146; 146),
insert: "foo()$0",
kind: Function,
detail: "fn foo()",
},
]
"###
);
2019-01-08 22:33:36 +03:00
}
#[test]
fn completes_self_in_methods() {
assert_debug_snapshot_matches!(
do_reference_completion(r"impl S { fn foo(&self) { <|> } }"),
@r#"[
CompletionItem {
label: "Self",
source_range: [25; 25),
delete: [25; 25),
insert: "Self",
kind: TypeParam,
},
CompletionItem {
label: "self",
source_range: [25; 25),
delete: [25; 25),
insert: "self",
kind: Binding,
detail: "&{unknown}",
},
]"#
);
2019-01-08 22:33:36 +03:00
}
2019-02-13 20:53:42 +01:00
#[test]
fn completes_prelude() {
assert_debug_snapshot_matches!(
do_reference_completion(
"
//- /main.rs
fn foo() { let x: <|> }
2019-02-13 20:53:42 +01:00
//- /std/lib.rs
#[prelude_import]
use prelude::*;
2019-02-13 20:53:42 +01:00
mod prelude {
struct Option;
}
"
),
@r#"[
CompletionItem {
label: "Option",
source_range: [18; 18),
delete: [18; 18),
insert: "Option",
kind: Struct,
},
CompletionItem {
label: "foo",
source_range: [18; 18),
delete: [18; 18),
insert: "foo()$0",
kind: Function,
detail: "fn foo()",
},
CompletionItem {
label: "std",
source_range: [18; 18),
delete: [18; 18),
insert: "std",
kind: Module,
},
]"#
2019-02-13 20:53:42 +01:00
);
}
2019-01-08 22:33:36 +03:00
}