[[package]]
name = "cmake"
-version = "0.1.44"
+version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb"
+checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
dependencies = [
"cc",
]
dependencies = [
"assists",
"base_db",
+ "either",
"expect-test",
"hir",
"ide_db",
[[package]]
name = "once_cell"
-version = "1.5.1"
+version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f53cef67919d7d247eb9a2f128ca9e522789967ef1eb4ccd8c71a95a8aedf596"
+checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "oorandom"
[[package]]
name = "tinyvec"
-version = "0.3.4"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
+checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "toolchain"
[[package]]
name = "unicode-normalization"
-version = "0.1.13"
+version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
+checksum = "b7f98e67a4d84f730d343392f9bfff7d21e3fca562b9cb7a43b768350beeddc6"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-segmentation"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+checksum = "db8716a166f290ff49dabc18b44aa407cb7c6dbe1aa0971b44b8a24b0ca35aae"
[[package]]
name = "unicode-xid"
let range = ctx.sema.original_range(import_assets.syntax_under_caret()).range;
let group = import_group_message(import_assets.import_candidate());
- let scope = ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), ctx)?;
+ let scope =
+ ImportScope::find_insert_use_container(import_assets.syntax_under_caret(), &ctx.sema)?;
for (import, _) in proposed_imports {
acc.add_group(
&group,
if let Some(mut mod_path) = mod_path {
mod_path.segments.pop();
mod_path.segments.push(variant_hir_name.clone());
- let scope = ImportScope::find_insert_use_container(scope_node, ctx)?;
-
+ let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?;
*rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge);
}
Some(())
let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
let current_crate = current_module.krate();
- let found_traits = imports_locator::find_imports(&ctx.sema, current_crate, trait_token.text())
- .into_iter()
- .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
- either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
- _ => None,
- })
- .flat_map(|trait_| {
- current_module
- .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
- .as_ref()
- .map(mod_path_to_ast)
- .zip(Some(trait_))
- });
+ let found_traits =
+ imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text())
+ .filter_map(
+ |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
+ either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
+ _ => None,
+ },
+ )
+ .flat_map(|trait_| {
+ current_module
+ .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
+ .as_ref()
+ .map(mod_path_to_ast)
+ .zip(Some(trait_))
+ });
let mut no_traits_found = true;
for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
}
let target = path.syntax().text_range();
- let scope = ImportScope::find_insert_use_container(path.syntax(), ctx)?;
+ let scope = ImportScope::find_insert_use_container(path.syntax(), &ctx.sema)?;
let syntax = scope.as_syntax_node();
acc.add(
AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
};
-pub use insert_use::MergeBehaviour;
-pub(crate) use insert_use::{insert_use, ImportScope};
+pub use insert_use::{insert_use, ImportScope, MergeBehaviour};
pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
let mut segments = Vec::new();
}
};
- let mut res = imports_locator::find_imports(sema, current_crate, &self.get_search_query())
- .into_iter()
- .filter_map(filter)
- .filter_map(|candidate| {
- let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
- if let Some(prefix_kind) = prefixed {
- self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
- } else {
- self.module_with_name_to_import.find_use_path(db, item)
- }
- .map(|path| (path, item))
- })
- .filter(|(use_path, _)| !use_path.segments.is_empty())
- .take(20)
- .collect::<Vec<_>>();
+ let mut res =
+ imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query())
+ .filter_map(filter)
+ .filter_map(|candidate| {
+ let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
+ if let Some(prefix_kind) = prefixed {
+ self.module_with_name_to_import.find_use_path_prefixed(
+ db,
+ item,
+ prefix_kind,
+ )
+ } else {
+ self.module_with_name_to_import.find_use_path(db, item)
+ }
+ .map(|path| (path, item))
+ })
+ .filter(|(use_path, _)| use_path.len() > 1)
+ .take(20)
+ .collect::<Vec<_>>();
res.sort_by_key(|(path, _)| path.clone());
res
}
//! Handle syntactic aspects of inserting a new `use`.
use std::{cmp::Ordering, iter::successors};
+use hir::Semantics;
+use ide_db::RootDatabase;
use itertools::{EitherOrBoth, Itertools};
use syntax::{
algo::SyntaxRewriter,
};
use test_utils::mark;
-#[derive(Debug)]
-pub(crate) enum ImportScope {
+#[derive(Debug, Clone)]
+pub enum ImportScope {
File(ast::SourceFile),
Module(ast::ItemList),
}
}
/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
- pub(crate) fn find_insert_use_container(
+ pub fn find_insert_use_container(
position: &SyntaxNode,
- ctx: &crate::assist_context::AssistContext,
+ sema: &Semantics<'_, RootDatabase>,
) -> Option<Self> {
- ctx.sema.ancestors_with_macros(position.clone()).find_map(Self::from)
+ sema.ancestors_with_macros(position.clone()).find_map(Self::from)
}
- pub(crate) fn as_syntax_node(&self) -> &SyntaxNode {
+ pub fn as_syntax_node(&self) -> &SyntaxNode {
match self {
ImportScope::File(file) => file.syntax(),
ImportScope::Module(item_list) => item_list.syntax(),
}
/// Insert an import path into the given file/node. A `merge` value of none indicates that no import merging is allowed to occur.
-pub(crate) fn insert_use<'a>(
+pub fn insert_use<'a>(
scope: &ImportScope,
path: ast::Path,
merge: Option<MergeBehaviour>,
itertools = "0.9.0"
log = "0.4.8"
rustc-hash = "1.1.0"
+either = "1.6.1"
assists = { path = "../assists", version = "0.0.0" }
stdx = { path = "../stdx", version = "0.0.0" }
Some(it) => it,
None => return,
};
- if let Some(item) = render_macro(RenderContext::new(ctx), name, macro_) {
+ if let Some(item) = render_macro(RenderContext::new(ctx), None, name, macro_) {
self.add(item);
}
}
func: hir::Function,
local_name: Option<String>,
) {
- let item = render_fn(RenderContext::new(ctx), local_name, func);
+ let item = render_fn(RenderContext::new(ctx), None, local_name, func);
self.add(item)
}
variant: hir::EnumVariant,
path: ModPath,
) {
- let item = render_enum_variant(RenderContext::new(ctx), None, variant, Some(path));
+ let item = render_enum_variant(RenderContext::new(ctx), None, None, variant, Some(path));
self.add(item);
}
variant: hir::EnumVariant,
local_name: Option<String>,
) {
- let item = render_enum_variant(RenderContext::new(ctx), local_name, variant, None);
+ let item = render_enum_variant(RenderContext::new(ctx), None, local_name, variant, None);
self.add(item);
}
}
//! Completion of names from the current scope, e.g. locals and imported items.
+use assists::utils::ImportScope;
+use either::Either;
use hir::{Adt, ModuleDef, ScopeDef, Type};
+use ide_db::imports_locator;
use syntax::AstNode;
use test_utils::mark;
-use crate::{CompletionContext, Completions};
+use crate::{
+ render::{render_resolution_with_import, RenderContext},
+ CompletionContext, Completions,
+};
pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
if !(ctx.is_trivial_path || ctx.is_pat_binding_or_const) {
}
acc.add_resolution(ctx, name.to_string(), &res)
});
+
+ fuzzy_completion(acc, ctx).unwrap_or_default()
}
fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
}
}
+fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
+ let _p = profile::span("fuzzy_completion");
+ let current_module = ctx.scope.module()?;
+ let anchor = ctx.name_ref_syntax.as_ref()?;
+ let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
+
+ let potential_import_name = ctx.token.to_string();
+
+ let possible_imports =
+ imports_locator::find_similar_imports(&ctx.sema, ctx.krate?, &potential_import_name, 400)
+ .filter_map(|import_candidate| match import_candidate {
+ // when completing outside the use declaration, modules are pretty useless
+ // and tend to bloat the completion suggestions a lot
+ Either::Left(ModuleDef::Module(_)) => None,
+ Either::Left(module_def) => Some((
+ current_module.find_use_path(ctx.db, module_def)?,
+ ScopeDef::ModuleDef(module_def),
+ )),
+ Either::Right(macro_def) => Some((
+ current_module.find_use_path(ctx.db, macro_def)?,
+ ScopeDef::MacroDef(macro_def),
+ )),
+ })
+ .filter(|(mod_path, _)| mod_path.len() > 1)
+ .filter_map(|(import_path, definition)| {
+ render_resolution_with_import(
+ RenderContext::new(ctx),
+ import_path.clone(),
+ import_scope.clone(),
+ ctx.config.merge,
+ &definition,
+ )
+ })
+ .take(20);
+
+ acc.add_all(possible_imports);
+ Some(())
+}
+
#[cfg(test)]
mod tests {
use expect_test::{expect, Expect};
"#]],
)
}
+
+ #[test]
+ fn function_fuzzy_completion() {
+ check_edit(
+ "stdin",
+ r#"
+//- /lib.rs crate:dep
+pub mod io {
+ pub fn stdin() {}
+};
+
+//- /main.rs crate:main deps:dep
+fn main() {
+ stdi<|>
+}
+"#,
+ r#"
+use dep::io::stdin;
+
+fn main() {
+ stdin()$0
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn macro_fuzzy_completion() {
+ check_edit(
+ "macro_with_curlies!",
+ r#"
+//- /lib.rs crate:dep
+/// Please call me as macro_with_curlies! {}
+#[macro_export]
+macro_rules! macro_with_curlies {
+ () => {}
+}
+
+//- /main.rs crate:main deps:dep
+fn main() {
+ curli<|>
+}
+"#,
+ r#"
+use dep::macro_with_curlies;
+
+fn main() {
+ macro_with_curlies! {$0}
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn struct_fuzzy_completion() {
+ check_edit(
+ "ThirdStruct",
+ r#"
+//- /lib.rs crate:dep
+pub struct FirstStruct;
+pub mod some_module {
+ pub struct SecondStruct;
+ pub struct ThirdStruct;
+}
+
+//- /main.rs crate:main deps:dep
+use dep::{FirstStruct, some_module::SecondStruct};
+
+fn main() {
+ this<|>
+}
+"#,
+ r#"
+use dep::{FirstStruct, some_module::{SecondStruct, ThirdStruct}};
+
+fn main() {
+ ThirdStruct
+}
+"#,
+ );
+ }
}
//! module, and we use to statically check that we only produce snippet
//! completions if we are allowed to.
+use assists::utils::MergeBehaviour;
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CompletionConfig {
pub enable_postfix_completions: bool,
pub add_call_parenthesis: bool,
pub add_call_argument_snippets: bool,
pub snippet_cap: Option<SnippetCap>,
+ pub merge: Option<MergeBehaviour>,
}
impl CompletionConfig {
add_call_parenthesis: true,
add_call_argument_snippets: true,
snippet_cap: Some(SnippetCap { _private: () }),
+ merge: Some(MergeBehaviour::Full),
}
}
}
use std::fmt;
-use hir::{Documentation, Mutability};
-use syntax::TextRange;
+use assists::utils::{insert_use, mod_path_to_ast, ImportScope, MergeBehaviour};
+use hir::{Documentation, ModPath, Mutability};
+use syntax::{algo, TextRange};
use text_edit::TextEdit;
use crate::config::SnippetCap;
///
/// Typically, replaces `source_range` with new identifier.
text_edit: TextEdit,
+
insert_text_format: InsertTextFormat,
/// What item (struct, function, etc) are we completing.
trigger_call_info: None,
score: None,
ref_match: None,
+ import_data: None,
}
}
+
/// What user sees in pop-up in the UI.
pub fn label(&self) -> &str {
&self.label
pub(crate) struct Builder {
source_range: TextRange,
completion_kind: CompletionKind,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
label: String,
insert_text: Option<String>,
insert_text_format: InsertTextFormat,
impl Builder {
pub(crate) fn build(self) -> CompletionItem {
- let label = self.label;
- let text_edit = match self.text_edit {
+ let mut label = self.label;
+ let mut lookup = self.lookup;
+ let mut insert_text = self.insert_text;
+ let mut text_edits = TextEdit::builder();
+
+ if let Some((import_path, import_scope, merge_behaviour)) = self.import_data {
+ let import = mod_path_to_ast(&import_path);
+ let mut import_path_without_last_segment = import_path;
+ let _ = import_path_without_last_segment.segments.pop();
+
+ if !import_path_without_last_segment.segments.is_empty() {
+ if lookup.is_none() {
+ lookup = Some(label.clone());
+ }
+ if insert_text.is_none() {
+ insert_text = Some(label.clone());
+ }
+ label = format!("{}::{}", import_path_without_last_segment, label);
+ }
+
+ let rewriter = insert_use(&import_scope, import, merge_behaviour);
+ if let Some(old_ast) = rewriter.rewrite_root() {
+ algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut text_edits);
+ }
+ }
+
+ let original_edit = match self.text_edit {
Some(it) => it,
- None => TextEdit::replace(
- self.source_range,
- self.insert_text.unwrap_or_else(|| label.clone()),
- ),
+ None => {
+ TextEdit::replace(self.source_range, insert_text.unwrap_or_else(|| label.clone()))
+ }
};
+ let mut resulting_edit = text_edits.finish();
+ resulting_edit.union(original_edit).expect("Failed to unite text edits");
+
CompletionItem {
source_range: self.source_range,
label,
insert_text_format: self.insert_text_format,
- text_edit,
+ text_edit: resulting_edit,
detail: self.detail,
documentation: self.documentation,
- lookup: self.lookup,
+ lookup,
kind: self.kind,
completion_kind: self.completion_kind,
deprecated: self.deprecated.unwrap_or(false),
self.trigger_call_info = Some(true);
self
}
+ pub(crate) fn import_data(
+ mut self,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
+ ) -> Builder {
+ self.import_data = import_data;
+ self
+ }
pub(crate) fn set_ref_match(
mut self,
ref_match: Option<(Mutability, CompletionScore)>,
mod builder_ext;
-use hir::{Documentation, HasAttrs, HirDisplay, Mutability, ScopeDef, Type};
+use assists::utils::{ImportScope, MergeBehaviour};
+use hir::{Documentation, HasAttrs, HirDisplay, ModPath, Mutability, ScopeDef, Type};
use ide_db::RootDatabase;
use syntax::TextRange;
use test_utils::mark;
local_name: String,
resolution: &ScopeDef,
) -> Option<CompletionItem> {
- Render::new(ctx).render_resolution(local_name, resolution)
+ Render::new(ctx).render_resolution(local_name, None, resolution)
+}
+
+pub(crate) fn render_resolution_with_import<'a>(
+ ctx: RenderContext<'a>,
+ import: ModPath,
+ import_scope: ImportScope,
+ merge_behaviour: Option<MergeBehaviour>,
+ resolution: &ScopeDef,
+) -> Option<CompletionItem> {
+ let local_name = import.segments.last()?.to_string();
+ Render::new(ctx).render_resolution(
+ local_name,
+ Some((import, import_scope, merge_behaviour)),
+ resolution,
+ )
}
/// Interface for data and methods required for items rendering.
fn render_resolution(
self,
local_name: String,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
resolution: &ScopeDef,
) -> Option<CompletionItem> {
use hir::ModuleDef::*;
let kind = match resolution {
ScopeDef::ModuleDef(Function(func)) => {
- let item = render_fn(self.ctx, Some(local_name), *func);
+ let item = render_fn(self.ctx, import_data, Some(local_name), *func);
return Some(item);
}
ScopeDef::ModuleDef(EnumVariant(var)) => {
- let item = render_enum_variant(self.ctx, Some(local_name), *var, None);
+ let item = render_enum_variant(self.ctx, import_data, Some(local_name), *var, None);
return Some(item);
}
ScopeDef::MacroDef(mac) => {
- let item = render_macro(self.ctx, local_name, *mac);
+ let item = render_macro(self.ctx, import_data, local_name, *mac);
return item;
}
local_name,
)
.kind(CompletionItemKind::UnresolvedReference)
+ .import_data(import_data)
.build();
return Some(item);
}
}
}
- let item = item.kind(kind).set_documentation(docs).set_ref_match(ref_match).build();
+ let item = item
+ .kind(kind)
+ .import_data(import_data)
+ .set_documentation(docs)
+ .set_ref_match(ref_match)
+ .build();
Some(item)
}
insert: "m",
kind: Module,
},
+ CompletionItem {
+ label: "m::Spam",
+ source_range: 75..76,
+ text_edit: TextEdit {
+ indels: [
+ Indel {
+ insert: "use m::Spam;",
+ delete: 0..0,
+ },
+ Indel {
+ insert: "\n\n",
+ delete: 0..0,
+ },
+ Indel {
+ insert: "Spam",
+ delete: 75..76,
+ },
+ ],
+ },
+ kind: Enum,
+ lookup: "Spam",
+ },
CompletionItem {
label: "m::Spam::Foo",
source_range: 75..76,
//! Renderer for `enum` variants.
+use assists::utils::{ImportScope, MergeBehaviour};
use hir::{HasAttrs, HirDisplay, ModPath, StructKind};
use itertools::Itertools;
use test_utils::mark;
pub(crate) fn render_enum_variant<'a>(
ctx: RenderContext<'a>,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
local_name: Option<String>,
variant: hir::EnumVariant,
path: Option<ModPath>,
) -> CompletionItem {
- EnumVariantRender::new(ctx, local_name, variant, path).render()
+ EnumVariantRender::new(ctx, local_name, variant, path).render(import_data)
}
#[derive(Debug)]
}
}
- fn render(self) -> CompletionItem {
+ fn render(
+ self,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
+ ) -> CompletionItem {
let mut builder = CompletionItem::new(
CompletionKind::Reference,
self.ctx.source_range(),
.kind(CompletionItemKind::EnumVariant)
.set_documentation(self.variant.docs(self.ctx.db()))
.set_deprecated(self.ctx.is_deprecated(self.variant))
+ .import_data(import_data)
.detail(self.detail());
if self.variant_kind == StructKind::Tuple {
//! Renderer for function calls.
-use hir::{HasSource, Type};
+use assists::utils::{ImportScope, MergeBehaviour};
+use hir::{HasSource, ModPath, Type};
use syntax::{ast::Fn, display::function_declaration};
use crate::{
pub(crate) fn render_fn<'a>(
ctx: RenderContext<'a>,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
local_name: Option<String>,
fn_: hir::Function,
) -> CompletionItem {
- FunctionRender::new(ctx, local_name, fn_).render()
+ FunctionRender::new(ctx, local_name, fn_).render(import_data)
}
#[derive(Debug)]
FunctionRender { ctx, name, fn_, ast_node }
}
- fn render(self) -> CompletionItem {
+ fn render(
+ self,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
+ ) -> CompletionItem {
let params = self.params();
CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
.kind(self.kind())
.set_deprecated(self.ctx.is_deprecated(self.fn_))
.detail(self.detail())
.add_call_parens(self.ctx.completion, self.name, params)
+ .import_data(import_data)
.build()
}
//! Renderer for macro invocations.
-use hir::{Documentation, HasSource};
+use assists::utils::{ImportScope, MergeBehaviour};
+use hir::{Documentation, HasSource, ModPath};
use syntax::display::macro_label;
use test_utils::mark;
pub(crate) fn render_macro<'a>(
ctx: RenderContext<'a>,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
name: String,
macro_: hir::MacroDef,
) -> Option<CompletionItem> {
- MacroRender::new(ctx, name, macro_).render()
+ MacroRender::new(ctx, name, macro_).render(import_data)
}
#[derive(Debug)]
MacroRender { ctx, name, macro_, docs, bra, ket }
}
- fn render(&self) -> Option<CompletionItem> {
+ fn render(
+ &self,
+ import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
+ ) -> Option<CompletionItem> {
// FIXME: Currently proc-macro do not have ast-node,
// such that it does not have source
if self.macro_.is_proc_macro() {
.kind(CompletionItemKind::Macro)
.set_documentation(self.docs.clone())
.set_deprecated(self.ctx.is_deprecated(self.macro_))
+ .import_data(import_data)
.detail(self.detail());
let needs_bang = self.needs_bang();
pub fn query_external_importables(
self,
db: &dyn DefDatabase,
- query: &str,
+ query: import_map::Query,
) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
- import_map::search_dependencies(
- db,
- self.into(),
- import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
- )
- .into_iter()
- .map(|item| match item {
+ import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item {
ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
})
//! FIXME: write short doc here
pub use hir_def::diagnostics::{InactiveCode, UnresolvedModule};
-pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder};
+pub use hir_expand::diagnostics::{
+ Diagnostic, DiagnosticCode, DiagnosticSink, DiagnosticSinkBuilder,
+};
pub use hir_ty::diagnostics::{
IncorrectCase, MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr,
NoSuchField,
builtin_type::BuiltinType,
docs::Documentation,
find_path::PrefixKind,
+ import_map,
item_scope::ItemInNs,
nameres::ModuleSource,
path::{ModPath, PathKind},
use crate::InFile;
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
pub struct DiagnosticCode(pub &'static str);
impl DiagnosticCode {
use std::cell::RefCell;
use hir::{
- diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
+ diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
Semantics,
};
use ide_db::base_db::SourceDatabase;
pub severity: Severity,
pub fix: Option<Fix>,
pub unused: bool,
+ pub code: Option<DiagnosticCode>,
}
impl Diagnostic {
fn error(range: TextRange, message: String) -> Self {
- Self { message, range, severity: Severity::Error, fix: None, unused: false }
+ Self { message, range, severity: Severity::Error, fix: None, unused: false, code: None }
}
fn hint(range: TextRange, message: String) -> Self {
- Self { message, range, severity: Severity::WeakWarning, fix: None, unused: false }
+ Self {
+ message,
+ range,
+ severity: Severity::WeakWarning,
+ fix: None,
+ unused: false,
+ code: None,
+ }
}
fn with_fix(self, fix: Option<Fix>) -> Self {
fn with_unused(self, unused: bool) -> Self {
Self { unused, ..self }
}
+
+ fn with_code(self, code: Option<DiagnosticCode>) -> Self {
+ Self { code, ..self }
+ }
}
#[derive(Debug)]
// Override severity and mark as unused.
res.borrow_mut().push(
Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
- .with_unused(true),
+ .with_unused(true)
+ .with_code(Some(d.code())),
);
})
// Only collect experimental diagnostics when they're enabled.
let mut sink = sink_builder
// Diagnostics not handled above get no fix and default treatment.
.build(|d| {
- res.borrow_mut()
- .push(Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()));
+ res.borrow_mut().push(
+ Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
+ .with_code(Some(d.code())),
+ );
});
if let Some(m) = sema.to_module_def(file_id) {
}
fn diagnostic_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
- Diagnostic::error(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
+ Diagnostic::error(sema.diagnostics_display_range(d).range, d.message())
+ .with_fix(d.fix(&sema))
+ .with_code(Some(d.code()))
}
fn warning_with_fix<D: DiagnosticWithFix>(d: &D, sema: &Semantics<RootDatabase>) -> Diagnostic {
- Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message()).with_fix(d.fix(&sema))
+ Diagnostic::hint(sema.diagnostics_display_range(d).range, d.message())
+ .with_fix(d.fix(&sema))
+ .with_code(Some(d.code()))
}
fn check_unnecessary_braces_in_use_statement(
},
),
unused: false,
+ code: Some(
+ DiagnosticCode(
+ "unresolved-module",
+ ),
+ ),
},
]
"#]],
//! This module contains an import search funcionality that is provided to the assists module.
//! Later, this should be moved away to a separate crate that is accessible from the assists module.
-use hir::{Crate, MacroDef, ModuleDef, Semantics};
+use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics};
use syntax::{ast, AstNode, SyntaxKind::NAME};
use crate::{
defs::{Definition, NameClass},
- symbol_index::{self, FileSymbol, Query},
+ symbol_index::{self, FileSymbol},
RootDatabase,
};
use either::Either;
use rustc_hash::FxHashSet;
-pub fn find_imports<'a>(
+pub fn find_exact_imports<'a>(
sema: &Semantics<'a, RootDatabase>,
krate: Crate,
name_to_import: &str,
-) -> Vec<Either<ModuleDef, MacroDef>> {
- let _p = profile::span("search_for_imports");
+) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
+ let _p = profile::span("find_exact_imports");
+ find_imports(
+ sema,
+ krate,
+ {
+ let mut local_query = symbol_index::Query::new(name_to_import.to_string());
+ local_query.exact();
+ local_query.limit(40);
+ local_query
+ },
+ import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40),
+ )
+}
+
+pub fn find_similar_imports<'a>(
+ sema: &Semantics<'a, RootDatabase>,
+ krate: Crate,
+ name_to_import: &str,
+ limit: usize,
+) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
+ let _p = profile::span("find_similar_imports");
+ find_imports(
+ sema,
+ krate,
+ {
+ let mut local_query = symbol_index::Query::new(name_to_import.to_string());
+ local_query.limit(limit);
+ local_query
+ },
+ import_map::Query::new(name_to_import).limit(limit),
+ )
+}
+
+fn find_imports<'a>(
+ sema: &Semantics<'a, RootDatabase>,
+ krate: Crate,
+ local_query: symbol_index::Query,
+ external_query: import_map::Query,
+) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
+ let _p = profile::span("find_similar_imports");
let db = sema.db;
// Query dependencies first.
let mut candidates: FxHashSet<_> =
- krate.query_external_importables(db, name_to_import).collect();
+ krate.query_external_importables(db, external_query).collect();
// Query the local crate using the symbol index.
- let local_results = {
- let mut query = Query::new(name_to_import.to_string());
- query.exact();
- query.limit(40);
- symbol_index::crate_symbols(db, krate.into(), query)
- };
+ let local_results = symbol_index::crate_symbols(db, krate.into(), local_query);
candidates.extend(
local_results
}),
);
- candidates.into_iter().collect()
+ candidates.into_iter()
}
fn get_name_definition<'a>(
max_length: data.inlayHints_maxLength,
};
- self.completion.enable_postfix_completions = data.completion_postfix_enable;
- self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
- self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
-
self.assist.insert_use.merge = match data.assist_importMergeBehaviour {
MergeBehaviourDef::None => None,
MergeBehaviourDef::Full => Some(MergeBehaviour::Full),
ImportPrefixDef::BySelf => PrefixKind::BySelf,
};
+ self.completion.enable_postfix_completions = data.completion_postfix_enable;
+ self.completion.add_call_parenthesis = data.completion_addCallParenthesis;
+ self.completion.add_call_argument_snippets = data.completion_addCallArgumentSnippets;
+ self.completion.merge = self.assist.insert_use.merge;
+
self.call_info_full = data.callInfo_full;
self.lens = LensConfig {
CallHierarchyOutgoingCall, CallHierarchyOutgoingCallsParams, CallHierarchyPrepareParams,
CodeActionKind, CodeLens, Command, CompletionItem, Diagnostic, DiagnosticTag,
DocumentFormattingParams, DocumentHighlight, DocumentSymbol, FoldingRange, FoldingRangeParams,
- HoverContents, Location, Position, PrepareRenameResponse, Range, RenameParams,
+ HoverContents, Location, NumberOrString, Position, PrepareRenameResponse, Range, RenameParams,
SemanticTokensDeltaParams, SemanticTokensFullDeltaResult, SemanticTokensParams,
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation,
SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
.flat_map(|item| to_proto::completion_item(&line_index, line_endings, item))
.collect();
- let completion_list = lsp_types::CompletionList { is_incomplete: false, items };
+ let completion_list = lsp_types::CompletionList { is_incomplete: true, items };
Ok(Some(completion_list.into()))
}
.map(|d| Diagnostic {
range: to_proto::range(&line_index, d.range),
severity: Some(to_proto::diagnostic_severity(d.severity)),
- code: None,
+ code: d.code.map(|d| d.as_str().to_owned()).map(NumberOrString::String),
code_description: None,
source: Some("rust-analyzer".to_string()),
message: d.message,
A function green tree is not super-convenient to use.
The biggest problem is accessing parents (there are no parent pointers!).
But there are also "identify" issues.
-Let's say you want to write a code which builds a list of expressions in a file: `fn collect_exrepssions(file: GreenNode) -> HashSet<GreenNode>`.
+Let's say you want to write a code which builds a list of expressions in a file: `fn collect_expressions(file: GreenNode) -> HashSet<GreenNode>`.
For the input like
```rust
self.parent.clone()
}
fn children(&self) -> impl Iterator<Item = SyntaxNode> {
- let mut offset = self.offset
+ let mut offset = self.offset;
self.green.children().map(|green_child| {
let child_offset = offset;
offset += green_child.text_len;
MIT
MIT / Apache-2.0
MIT OR Apache-2.0
+MIT OR Apache-2.0 OR Zlib
MIT OR Zlib OR Apache-2.0
MIT/Apache-2.0
Unlicense OR MIT