]> git.lizzy.rs Git - rust.git/commitdiff
Merge #6577 #6579 #6581
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>
Tue, 17 Nov 2020 18:01:18 +0000 (18:01 +0000)
committerGitHub <noreply@github.com>
Tue, 17 Nov 2020 18:01:18 +0000 (18:01 +0000)
6577: fix typos in syntax.md r=matklad a=jakobhellermann

6579: cargo update and add new license r=matklad a=kjeremy

6581: Use vscode-languageclient 7.0.0-next.14 r=matklad a=kjeremy

Co-authored-by: Jakob Hellermann <jakob.hellermann@protonmail.com>
Co-authored-by: kjeremy <kjeremy@gmail.com>
27 files changed:
Cargo.lock
crates/assists/src/handlers/auto_import.rs
crates/assists/src/handlers/extract_struct_from_enum_variant.rs
crates/assists/src/handlers/replace_derive_with_manual_impl.rs
crates/assists/src/handlers/replace_qualified_name_with_use.rs
crates/assists/src/utils.rs
crates/assists/src/utils/import_assets.rs
crates/assists/src/utils/insert_use.rs
crates/completion/Cargo.toml
crates/completion/src/completions.rs
crates/completion/src/completions/unqualified_path.rs
crates/completion/src/config.rs
crates/completion/src/item.rs
crates/completion/src/render.rs
crates/completion/src/render/enum_variant.rs
crates/completion/src/render/function.rs
crates/completion/src/render/macro_.rs
crates/hir/src/code_model.rs
crates/hir/src/diagnostics.rs
crates/hir/src/lib.rs
crates/hir_expand/src/diagnostics.rs
crates/ide/src/diagnostics.rs
crates/ide_db/src/imports_locator.rs
crates/rust-analyzer/src/config.rs
crates/rust-analyzer/src/handlers.rs
docs/dev/syntax.md
xtask/tests/tidy.rs

index 09215a37a2be110cfc2cb0ef610f446affb16e4f..6345ce9934343823187f626e70517410e708fc95 100644 (file)
@@ -242,9 +242,9 @@ dependencies = [
 
 [[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",
 ]
@@ -255,6 +255,7 @@ version = "0.0.0"
 dependencies = [
  "assists",
  "base_db",
+ "either",
  "expect-test",
  "hir",
  "ide_db",
@@ -1065,9 +1066,9 @@ checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
 
 [[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"
@@ -1739,9 +1740,18 @@ dependencies = [
 
 [[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"
@@ -1879,18 +1889,18 @@ dependencies = [
 
 [[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"
index 37dd6126618efd813d9bd592246b795481618214..d665837a2f3bad0b4b8279ca1ca16f2cfdac8fda 100644 (file)
@@ -98,7 +98,8 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
 
     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,
index 067afabf2edf7b45c765ac40c32d82e693ad10e0..cac77c49bbc46fdc601245e4881f945e1689e1ea 100644 (file)
@@ -143,8 +143,7 @@ fn insert_import(
     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(())
index 82625516c211a2f05724867a7ab9dc67c98be44a..453a6cebfb8cbca4082fe0433f6084ee451840bf 100644 (file)
@@ -62,19 +62,21 @@ pub(crate) fn replace_derive_with_manual_impl(
     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) {
index d7e1d95805e67529b4f172bd19b0bec2c5c1a55e..a66db9ae3a15f271b78fc8a9a7e58edda1b1ae9e 100644 (file)
@@ -34,7 +34,7 @@ pub(crate) fn replace_qualified_name_with_use(
     }
 
     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),
index d1a0a99b1dd6e91872a7d9f1be21caa3b34e3eea..66c0cdd5faf37b7ea82d0898e6528044922a40cc 100644 (file)
@@ -22,8 +22,7 @@
     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();
index f47edbb765851defcd73547e7de480f79b35dd01..ff5c0e78ee8c20f2d2ea70859f17b4a925bebc3d 100644 (file)
@@ -179,21 +179,25 @@ fn search_for(
             }
         };
 
-        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
     }
index af3fc96b6c2fce2b4e89bb624b9e85092256b17f..423782a0e0e728f426dd3bbe881fe00572ebbd07 100644 (file)
@@ -1,6 +1,8 @@
 //! 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,
@@ -13,8 +15,8 @@
 };
 use test_utils::mark;
 
-#[derive(Debug)]
-pub(crate) enum ImportScope {
+#[derive(Debug, Clone)]
+pub enum ImportScope {
     File(ast::SourceFile),
     Module(ast::ItemList),
 }
@@ -31,14 +33,14 @@ pub(crate) fn from(syntax: SyntaxNode) -> Option<Self> {
     }
 
     /// 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(),
@@ -88,7 +90,7 @@ fn is_inner_comment(token: SyntaxToken) -> bool {
 }
 
 /// 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>,
index 3015ec9e0ea4e49bf149c555cd55989fdb77a911..e7df9d955656bcf1b450ed2358b35a5254c42faa 100644 (file)
@@ -13,6 +13,7 @@ doctest = false
 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" }
index 75dbb1a23bb5c8c616d7c2b752994aa573e627e9..9b7d6c5809131b2819176037086402d516113267 100644 (file)
@@ -90,7 +90,7 @@ pub(crate) fn add_macro(
             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);
         }
     }
@@ -101,7 +101,7 @@ pub(crate) fn add_function(
         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)
     }
 
@@ -123,7 +123,7 @@ pub(crate) fn add_qualified_enum_variant(
         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);
     }
 
@@ -133,7 +133,7 @@ pub(crate) fn add_enum_variant(
         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);
     }
 }
index 7df58e1da82932c62fe27532a7223275388bec26..86c143b637f713bfbee8e25c574b1108947a26db 100644 (file)
@@ -1,10 +1,16 @@
 //! 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) {
@@ -37,6 +43,8 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
         }
         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) {
@@ -63,6 +71,45 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
     }
 }
 
+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};
@@ -676,4 +723,85 @@ impl My<|>
             "#]],
         )
     }
+
+    #[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
+}
+"#,
+        );
+    }
 }
index 71b49ace8bfb0e38a746ff34866d9f3a3cfb46dc..82874ff256a610cb37a4388bc69ea9a40fea70e7 100644 (file)
@@ -4,12 +4,15 @@
 //! 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 {
@@ -30,6 +33,7 @@ fn default() -> Self {
             add_call_parenthesis: true,
             add_call_argument_snippets: true,
             snippet_cap: Some(SnippetCap { _private: () }),
+            merge: Some(MergeBehaviour::Full),
         }
     }
 }
index 6d1d085f4bc779e63ebfe1750b11577ac18494c3..b13c3f37628a608a5875b6520e8aecbb69034353 100644 (file)
@@ -2,8 +2,9 @@
 
 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;
@@ -31,6 +32,7 @@ pub struct CompletionItem {
     ///
     /// Typically, replaces `source_range` with new identifier.
     text_edit: TextEdit,
+
     insert_text_format: InsertTextFormat,
 
     /// What item (struct, function, etc) are we completing.
@@ -199,8 +201,10 @@ pub(crate) fn new(
             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
@@ -257,6 +261,7 @@ pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
 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,
@@ -273,23 +278,50 @@ pub(crate) struct Builder {
 
 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),
@@ -358,6 +390,13 @@ pub(crate) fn trigger_call_info(mut self) -> Builder {
         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)>,
index 1fa02c37522f0333e5c82c832bcc189a39e53b7a..e892d4de8590a6c76d00f452a4ff5baa11e3b737 100644 (file)
@@ -9,7 +9,8 @@
 
 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;
@@ -42,7 +43,22 @@ pub(crate) fn render_resolution<'a>(
     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.
@@ -131,6 +147,7 @@ fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
     fn render_resolution(
         self,
         local_name: String,
+        import_data: Option<(ModPath, ImportScope, Option<MergeBehaviour>)>,
         resolution: &ScopeDef,
     ) -> Option<CompletionItem> {
         use hir::ModuleDef::*;
@@ -142,15 +159,15 @@ fn render_resolution(
 
         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;
             }
 
@@ -175,6 +192,7 @@ fn render_resolution(
                     local_name,
                 )
                 .kind(CompletionItemKind::UnresolvedReference)
+                .import_data(import_data)
                 .build();
                 return Some(item);
             }
@@ -227,7 +245,12 @@ fn render_resolution(
             }
         }
 
-        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)
     }
 
@@ -425,6 +448,28 @@ fn main() { let _: m::Spam = S<|> }
                         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,
index fd412ed0eef881f348f4d3b36a0a5016767eb834..6070e9b1d7935033dfa8cfcb7a8070db8b1b2287 100644 (file)
@@ -1,5 +1,6 @@
 //! 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)]
@@ -60,7 +62,10 @@ fn new(
         }
     }
 
-    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(),
@@ -69,6 +74,7 @@ fn render(self) -> CompletionItem {
         .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 {
index 4fa6eafd72f6f8d83ac64e9bc580df1471385d4e..9dd5cd18c569031b9ffee4abd0aa327e0768feac 100644 (file)
@@ -1,6 +1,7 @@
 //! 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)]
@@ -36,7 +38,10 @@ fn new(
         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())
@@ -44,6 +49,7 @@ fn render(self) -> CompletionItem {
             .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()
     }
 
index 96be59cc336aeda40e1b3865b6ce8a0c60c691a9..fead59e41c09bafed1495af05a3efdd751741892 100644 (file)
@@ -1,6 +1,7 @@
 //! 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)]
@@ -36,7 +38,10 @@ fn new(ctx: RenderContext<'a>, name: String, macro_: hir::MacroDef) -> MacroRend
         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() {
@@ -48,6 +53,7 @@ fn render(&self) -> Option<CompletionItem> {
                 .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();
index 30a5e45809b9a2bbfa5b315e482fb3a0433aa196..37ed092ad0ad43ed6c70acfc914be095a91fb89d 100644 (file)
@@ -110,15 +110,9 @@ pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> {
     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()),
         })
index c18c1c5873992c5ef8ec37ff6f9a5337b86d91f2..d9ad8db6f76999def37cbbf11d770ffa0d8c0b4c 100644 (file)
@@ -1,6 +1,8 @@
 //! 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,
index 0d184379f10242ded3695acebcf1f8dc52d39119..5fea25ef1b710b7e732e0a0c99196757fbc29e92 100644 (file)
@@ -49,6 +49,7 @@
     builtin_type::BuiltinType,
     docs::Documentation,
     find_path::PrefixKind,
+    import_map,
     item_scope::ItemInNs,
     nameres::ModuleSource,
     path::{ModPath, PathKind},
index 78ccc212c84c4214feb2a8747371b14b25389e48..1043c6aeb5becc71a32202b086a16574bafae0af 100644 (file)
@@ -20,7 +20,7 @@
 
 use crate::InFile;
 
-#[derive(Copy, Clone, PartialEq)]
+#[derive(Copy, Clone, Debug, PartialEq)]
 pub struct DiagnosticCode(pub &'static str);
 
 impl DiagnosticCode {
index 1c7f0276321fdc338f100ebe9f1b2639d48e27a7..3df73ed4fa3450a5c21a4ad78a5ac34516ef558d 100644 (file)
@@ -10,7 +10,7 @@
 use std::cell::RefCell;
 
 use hir::{
-    diagnostics::{Diagnostic as _, DiagnosticSinkBuilder},
+    diagnostics::{Diagnostic as _, DiagnosticCode, DiagnosticSinkBuilder},
     Semantics,
 };
 use ide_db::base_db::SourceDatabase;
@@ -35,15 +35,23 @@ pub struct Diagnostic {
     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 {
@@ -53,6 +61,10 @@ 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)]
@@ -126,7 +138,8 @@ pub(crate) fn diagnostics(
             // 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.
@@ -137,8 +150,10 @@ pub(crate) fn diagnostics(
     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) {
@@ -149,11 +164,15 @@ pub(crate) fn diagnostics(
 }
 
 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(
@@ -589,6 +608,11 @@ fn test_unresolved_module_diagnostic() {
                             },
                         ),
                         unused: false,
+                        code: Some(
+                            DiagnosticCode(
+                                "unresolved-module",
+                            ),
+                        ),
                     },
                 ]
             "#]],
index df74be00bb20861a4e7039825cf240c3071cacbd..9d8ea7368d3a5141ae14dff7d03f18b45917e3a7 100644 (file)
@@ -1,36 +1,70 @@
 //! 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
@@ -43,7 +77,7 @@ pub fn find_imports<'a>(
             }),
     );
 
-    candidates.into_iter().collect()
+    candidates.into_iter()
 }
 
 fn get_name_definition<'a>(
index d16796590087947737db0eb0e231d1b4dba43d07..5fc6800cf2fd9a6e934f73ed325ac8018470c1b8 100644 (file)
@@ -294,10 +294,6 @@ pub fn update(&mut self, json: serde_json::Value) {
             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),
@@ -309,6 +305,11 @@ pub fn update(&mut self, json: serde_json::Value) {
             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 {
index 4d1ebf6bf9890004dddb4c094a0f96acb830f293..118e7276f2b2e700ceba5a2b3c60ffdd69a97e79 100644 (file)
@@ -18,7 +18,7 @@
     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,
@@ -573,7 +573,7 @@ pub(crate) fn handle_completion(
         .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()))
 }
 
@@ -1128,7 +1128,7 @@ pub(crate) fn publish_diagnostics(
         .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,
index 2eb08b7cab86cccfd0c2a17bbf52e951f164aece..1edafab680e9930476b1c31d8b647f60687b087f 100644 (file)
@@ -195,7 +195,7 @@ Modeling this with immutable trees is possible, but annoying.
 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
@@ -236,7 +236,7 @@ impl SyntaxNode {
         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;
index 99652e76b64658577d25a8677f3041a41ce19c05..4c58aed599acb482383eb8e41898b0332837512b 100644 (file)
@@ -168,6 +168,7 @@ fn check_licenses() {
 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