Repository: rust-analyzer/rust-analyzer
Pull request #319 (2018-12-21 23:07:48)

Completion icons


Patch:
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml
index 5f90d39d6c..9776a82e37 100644
--- a/crates/gen_lsp_server/Cargo.toml
+++ b/crates/gen_lsp_server/Cargo.toml
@@ -12,5 +12,5 @@ languageserver-types = "0.53.0"
 log = "0.4.3"
 failure = "0.1.2"
 serde_json = "1.0.24"
-serde = "1.0.71"
+serde = { version = "1.0.71", features = ["derive"] }
 crossbeam-channel = "0.2.4"
diff --git a/crates/gen_lsp_server/src/msg.rs b/crates/gen_lsp_server/src/msg.rs
index ef6358cb17..af901d0d29 100644
--- a/crates/gen_lsp_server/src/msg.rs
+++ b/crates/gen_lsp_server/src/msg.rs
@@ -7,7 +7,7 @@ use failure::{bail, format_err};
 
 use crate::Result;
 
-#[derive(Debug, Serialize, Deserialize, Clone)]
+#[derive(Serialize, Deserialize, Debug, Clone)]
 #[serde(untagged)]
 pub enum RawMessage {
     Request(RawRequest),
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs
index 2d61a3aef5..d742d62955 100644
--- a/crates/ra_analysis/src/completion.rs
+++ b/crates/ra_analysis/src/completion.rs
@@ -18,7 +18,7 @@ use crate::{
     },
 };
 
-pub use crate::completion::completion_item::{CompletionItem, InsertText};
+pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind};
 
 /// Main entry point for copmletion. We run comletion as a two-phase process.
 ///
diff --git a/crates/ra_analysis/src/completion/complete_fn_param.rs b/crates/ra_analysis/src/completion/complete_fn_param.rs
index 6a6213e67b..bb5fdfda04 100644
--- a/crates/ra_analysis/src/completion/complete_fn_param.rs
+++ b/crates/ra_analysis/src/completion/complete_fn_param.rs
@@ -34,9 +34,8 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
             }
         })
         .for_each(|(label, lookup)| {
-            CompletionItem::new(label)
+            CompletionItem::new(CompletionKind::Magic, label)
                 .lookup_by(lookup)
-                .kind(CompletionKind::Magic)
                 .add_to(acc)
         });
 
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
index dead15bb6f..5427fcb11f 100644
--- a/crates/ra_analysis/src/completion/complete_keyword.rs
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -5,7 +5,14 @@ use ra_syntax::{
     SyntaxKind::*, SyntaxNodeRef,
 };
 
-use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind::*};
+use crate::completion::{CompletionContext, CompletionItem, Completions, CompletionKind, CompletionItemKind};
+
+fn keyword(kw: &str, snippet: &str) -> CompletionItem {
+    CompletionItem::new(CompletionKind::Keyword, kw)
+        .kind(CompletionItemKind::Keyword)
+        .snippet(snippet)
+        .build()
+}
 
 pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.is_trivial_path {
@@ -60,13 +67,6 @@ fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem>
     Some(keyword("return", snip))
 }
 
-fn keyword(kw: &str, snippet: &str) -> CompletionItem {
-    CompletionItem::new(kw)
-        .kind(Keyword)
-        .snippet(snippet)
-        .build()
-}
-
 #[cfg(test)]
 mod tests {
     use crate::completion::{CompletionKind, check_completion};
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs
index 5fc24af720..ad4d68a332 100644
--- a/crates/ra_analysis/src/completion/complete_path.rs
+++ b/crates/ra_analysis/src/completion/complete_path.rs
@@ -1,6 +1,6 @@
 use crate::{
     Cancelable,
-    completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext},
+    completion::{CompletionItem, Completions, CompletionKind, CompletionContext},
 };
 
 pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
@@ -17,9 +17,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
         _ => return Ok(()),
     };
     let module_scope = target_module.scope(ctx.db)?;
-    module_scope.entries().for_each(|(name, _res)| {
-        CompletionItem::new(name.to_string())
-            .kind(Reference)
+    module_scope.entries().for_each(|(name, res)| {
+        CompletionItem::new(CompletionKind::Reference, name.to_string())
+            .from_resolution(ctx.db, res)
             .add_to(acc)
     });
     Ok(())
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index d07c0e46d9..82610d63f1 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -3,7 +3,7 @@ use ra_syntax::TextUnit;
 
 use crate::{
     Cancelable,
-    completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext},
+    completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
 };
 
 pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
@@ -29,9 +29,9 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
                     }
                 }
             })
-            .for_each(|(name, _res)| {
-                CompletionItem::new(name.to_string())
-                    .kind(Reference)
+            .for_each(|(name, res)| {
+                CompletionItem::new(CompletionKind::Reference, name.to_string())
+                    .from_resolution(ctx.db, res)
                     .add_to(acc)
             });
     }
@@ -46,12 +46,12 @@ fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit)
         .flat_map(|scope| scopes.entries(scope).iter())
         .filter(|entry| shadowed.insert(entry.name()))
         .for_each(|entry| {
-            CompletionItem::new(entry.name().to_string())
-                .kind(Reference)
+            CompletionItem::new(CompletionKind::Reference, entry.name().to_string())
+                .kind(CompletionItemKind::Binding)
                 .add_to(acc)
         });
     if scopes.self_param.is_some() {
-        CompletionItem::new("self").kind(Reference).add_to(acc);
+        CompletionItem::new(CompletionKind::Reference, "self").add_to(acc);
     }
 }
 
diff --git a/crates/ra_analysis/src/completion/complete_snippet.rs b/crates/ra_analysis/src/completion/complete_snippet.rs
index ccd68832b4..fb9da0a4f8 100644
--- a/crates/ra_analysis/src/completion/complete_snippet.rs
+++ b/crates/ra_analysis/src/completion/complete_snippet.rs
@@ -1,38 +1,35 @@
-use crate::completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext};
+use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionItemKind, CompletionContext, completion_item::Builder};
+
+fn snippet(label: &str, snippet: &str) -> Builder {
+    CompletionItem::new(CompletionKind::Snippet, label)
+        .snippet(snippet)
+        .kind(CompletionItemKind::Snippet)
+}
 
 pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
     if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) {
         return;
     }
-    CompletionItem::new("pd")
-        .snippet("eprintln!(\"$0 = {:?}\", $0);")
-        .kind(Snippet)
-        .add_to(acc);
-    CompletionItem::new("ppd")
-        .snippet("eprintln!(\"$0 = {:#?}\", $0);")
-        .kind(Snippet)
-        .add_to(acc);
+    snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
+    snippet("ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
 }
 
 pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
     if !ctx.is_new_item {
         return;
     }
-    CompletionItem::new("Test function")
-        .lookup_by("tfn")
-        .snippet(
-            "\
+    snippet(
+        "Test function",
+        "\
 #[test]
 fn ${1:feature}() {
     $0
 }",
-        )
-        .kind(Snippet)
-        .add_to(acc);
-    CompletionItem::new("pub(crate)")
-        .snippet("pub(crate) $0")
-        .kind(Snippet)
-        .add_to(acc);
+    )
+    .lookup_by("tfn")
+    .add_to(acc);
+
+    snippet("pub(crate)", "pub(crate) $0").add_to(acc);
 }
 
 #[cfg(test)]
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index d5d7517598..911f08468e 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -1,13 +1,17 @@
+use crate::db;
+
 /// `CompletionItem` describes a single completion variant in the editor pop-up.
 /// It is basically a POD with various properties. To construct a
 /// `CompletionItem`, use `new` method and the `Builder` struct.
 #[derive(Debug)]
 pub struct CompletionItem {
+    /// Used only internally in tests, to check only specific kind of
+    /// completion.
+    completion_kind: CompletionKind,
     label: String,
     lookup: Option<String>,
     snippet: Option<String>,
-    /// Used only internally in test, to check only specific kind of completion.
-    kind: CompletionKind,
+    kind: Option<CompletionItemKind>,
 }
 
 pub enum InsertText {
@@ -15,6 +19,15 @@ pub enum InsertText {
     Snippet { text: String },
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum CompletionItemKind {
+    Snippet,
+    Keyword,
+    Module,
+    Function,
+    Binding,
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub(crate) enum CompletionKind {
     /// Parser-based keyword completion.
@@ -24,17 +37,17 @@ pub(crate) enum CompletionKind {
     /// "Secret sauce" completions.
     Magic,
     Snippet,
-    Unspecified,
 }
 
 impl CompletionItem {
-    pub(crate) fn new(label: impl Into<String>) -> Builder {
+    pub(crate) fn new(completion_kind: CompletionKind, label: impl Into<String>) -> Builder {
         let label = label.into();
         Builder {
+            completion_kind,
             label,
             lookup: None,
             snippet: None,
-            kind: CompletionKind::Unspecified,
+            kind: None,
         }
     }
     /// What user sees in pop-up in the UI.
@@ -57,15 +70,20 @@ impl CompletionItem {
             Some(it) => InsertText::Snippet { text: it.clone() },
         }
     }
+
+    pub fn kind(&self) -> Option<CompletionItemKind> {
+        self.kind
+    }
 }
 
 /// A helper to make `CompletionItem`s.
 #[must_use]
 pub(crate) struct Builder {
+    completion_kind: CompletionKind,
     label: String,
     lookup: Option<String>,
     snippet: Option<String>,
-    kind: CompletionKind,
+    kind: Option<CompletionItemKind>,
 }
 
 impl Builder {
@@ -79,6 +97,7 @@ impl Builder {
             lookup: self.lookup,
             snippet: self.snippet,
             kind: self.kind,
+            completion_kind: self.completion_kind,
         }
     }
     pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -89,8 +108,25 @@ impl Builder {
         self.snippet = Some(snippet.into());
         self
     }
-    pub(crate) fn kind(mut self, kind: CompletionKind) -> Builder {
-        self.kind = kind;
+    pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
+        self.kind = Some(kind);
+        self
+    }
+    pub(crate) fn from_resolution(
+        mut self,
+        db: &db::RootDatabase,
+        resolution: &hir::Resolution,
+    ) -> Builder {
+        if let Some(def_id) = resolution.def_id {
+            if let Ok(def) = def_id.resolve(db) {
+                let kind = match def {
+                    hir::Def::Module(..) => CompletionItemKind::Module,
+                    hir::Def::Function(..) => CompletionItemKind::Function,
+                    _ => return self,
+                };
+                self.kind = Some(kind);
+            }
+        }
         self
     }
 }
@@ -154,7 +190,7 @@ impl Completions {
     fn debug_render(&self, kind: CompletionKind) -> String {
         let mut res = String::new();
         for c in self.buf.iter() {
-            if c.kind == kind {
+            if c.completion_kind == kind {
                 if let Some(lookup) = &c.lookup {
                     res.push_str(lookup);
                     res.push_str(&format!(" {:?}", c.label));
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index b2f4cd228b..6fd1578801 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -30,7 +30,7 @@ use crate::{
 };
 
 pub use crate::{
-    completion::{CompletionItem, InsertText},
+    completion::{CompletionItem, CompletionItemKind, InsertText},
 };
 pub use ra_editor::{
     FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index 9f1aa1ef2d..77be25f1a8 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -95,7 +95,7 @@ impl FnScopes {
                     r1.start().cmp(&r2.start())
                 }
             })
-            .map(|(ptr, scope)| *scope)
+            .map(|(_ptr, scope)| *scope)
             .unwrap_or(original_scope)
     }
 
@@ -209,7 +209,6 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco
         }
     }
     if let Some(expr) = block.expr() {
-        eprintln!("{:?}", expr);
         scopes.set_scope(expr.syntax(), scope);
         compute_expr_scopes(expr, scopes, scope);
     }
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 5941a9ea3d..f56214b47a 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -39,7 +39,7 @@ use crate::{
 pub use self::{
     path::{Path, PathKind},
     krate::Crate,
-    module::{Module, ModuleId, Problem, nameres::ItemMap},
+    module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
     function::{Function, FnScopes},
 };
 
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index d5866f6ef5..cd31e8cfe6 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -16,7 +16,7 @@ use crate::{
     arena::{Arena, Id},
 };
 
-pub use self::nameres::ModuleScope;
+pub use self::nameres::{ModuleScope, Resolution};
 
 /// `Module` is API entry point to get all the information
 /// about a particular module.
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index f44abc7306..39e891cda5 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -49,7 +49,7 @@ pub struct ModuleScope {
 }
 
 impl ModuleScope {
-    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &Resolution)> + 'a {
+    pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &'a Resolution)> + 'a {
         self.items.iter()
     }
     pub fn get(&self, name: &SmolStr) -> Option<&Resolution> {
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index 218ded4eec..051f1f995b 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -1,8 +1,8 @@
 use languageserver_types::{
     self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
-    TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier,
+    TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat,
 };
-use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition};
+use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText};
 use ra_editor::{LineCol, LineIndex};
 use ra_text_edit::{AtomTextEdit, TextEdit};
 use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -45,6 +45,46 @@ impl Conv for SyntaxKind {
     }
 }
 
+impl Conv for CompletionItemKind {
+    type Output = ::languageserver_types::CompletionItemKind;
+
+    fn conv(self) -> <Self as Conv>::Output {
+        use ::languageserver_types::CompletionItemKind::*;
+        match self {
+            CompletionItemKind::Keyword => Keyword,
+            CompletionItemKind::Snippet => Snippet,
+            CompletionItemKind::Module => Module,
+            CompletionItemKind::Function => Function,
+            CompletionItemKind::Binding => Variable,
+        }
+    }
+}
+
+impl Conv for CompletionItem {
+    type Output = ::languageserver_types::CompletionItem;
+
+    fn conv(self) -> <Self as Conv>::Output {
+        let mut res = ::languageserver_types::CompletionItem {
+            label: self.label().to_string(),
+            filter_text: Some(self.lookup().to_string()),
+            kind: self.kind().map(|it| it.conv()),
+            ..Default::default()
+        };
+        match self.insert_text() {
+            InsertText::PlainText { text } => {
+                res.insert_text = Some(text);
+                res.insert_text_format = Some(InsertTextFormat::PlainText);
+            }
+            InsertText::Snippet { text } => {
+                res.insert_text = Some(text);
+                res.insert_text_format = Some(InsertTextFormat::Snippet);
+                res.kind = Some(languageserver_types::CompletionItemKind::Keyword);
+            }
+        }
+        res
+    }
+}
+
 impl ConvWith for Position {
     type Ctx = LineIndex;
     type Output = TextUnit;
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 2dfeb061a1..252d1ba3ea 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -2,13 +2,13 @@ use std::collections::HashMap;
 
 use gen_lsp_server::ErrorCode;
 use languageserver_types::{
-    CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic,
+    CodeActionResponse, Command, Diagnostic,
     DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
-    FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, MarkedString, Position,
+    FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
     PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
     WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
 };
-use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition, InsertText};
+use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition};
 use ra_syntax::{TextUnit, text_utils::intersect};
 use ra_text_edit::text_utils::contains_offset_nonstrict;
 use rustc_hash::FxHashMap;
@@ -419,28 +419,7 @@ pub fn handle_completion(
         None => return Ok(None),
         Some(items) => items,
     };
-    let items = items
-        .into_iter()
-        .map(|item| {
-            let mut res = CompletionItem {
-                label: item.label().to_string(),
-                filter_text: Some(item.lookup().to_string()),
-                ..Default::default()
-            };
-            match item.insert_text() {
-                InsertText::PlainText { text } => {
-                    res.insert_text = Some(text);
-                    res.insert_text_format = Some(InsertTextFormat::PlainText);
-                }
-                InsertText::Snippet { text } => {
-                    res.insert_text = Some(text);
-                    res.insert_text_format = Some(InsertTextFormat::Snippet);
-                    res.kind = Some(CompletionItemKind::Keyword);
-                }
-            }
-            res
-        })
-        .collect();
+    let items = items.into_iter().map(|item| item.conv()).collect();
 
     Ok(Some(req::CompletionResponse::Array(items)))
 }
diff --git a/crates/ra_syntax/src/lexer.rs b/crates/ra_syntax/src/lexer.rs
index c6acd095e5..f388da273f 100644
--- a/crates/ra_syntax/src/lexer.rs
+++ b/crates/ra_syntax/src/lexer.rs
@@ -160,7 +160,7 @@ fn next_token_inner(c: char, ptr: &mut Ptr) -> SyntaxKind {
                 // if we find one, then this is an invalid character literal
                 if ptr.at('\'') {
                     ptr.bump();
-                    return CHAR;
+                    return CHAR; // TODO: error reporting
                 }
                 LIFETIME
             } else {


Test patch:
diff --git a/crates/ra_syntax/tests/test.rs b/crates/ra_syntax/tests/test.rs
index 4266864bdf..14ad836b52 100644
--- a/crates/ra_syntax/tests/test.rs
+++ b/crates/ra_syntax/tests/test.rs
@@ -65,10 +65,14 @@ fn self_hosting_parsing() {
     for entry in walkdir::WalkDir::new(dir)
         .into_iter()
         .filter_entry(|entry| {
-            !entry.path().components().any(|component| {
-                // Get all files which are not in the crates/ra_syntax/tests/data folder
-                component == Component::Normal(OsStr::new("data"))
-            })
+            !entry
+                .path()
+                .components()
+                // TODO: this more neatly
+                .any(|component| {
+                    // Get all files which are not in the crates/ra_syntax/tests/data folder
+                    component == Component::Normal(OsStr::new("data"))
+                })
         })
         .map(|e| e.unwrap())
         .filter(|entry| {