319: Completion icons r=matklad a=matklad



Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2018-12-21 23:28:47 +00:00
commit 4e4ca27eab
16 changed files with 136 additions and 86 deletions

View File

@ -12,5 +12,5 @@ languageserver-types = "0.53.0"
log = "0.4.3" log = "0.4.3"
failure = "0.1.2" failure = "0.1.2"
serde_json = "1.0.24" serde_json = "1.0.24"
serde = "1.0.71" serde = { version = "1.0.71", features = ["derive"] }
crossbeam-channel = "0.2.4" crossbeam-channel = "0.2.4"

View File

@ -7,7 +7,7 @@
use crate::Result; use crate::Result;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)] #[serde(untagged)]
pub enum RawMessage { pub enum RawMessage {
Request(RawRequest), Request(RawRequest),

View File

@ -18,7 +18,7 @@
}, },
}; };
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. /// Main entry point for copmletion. We run comletion as a two-phase process.
/// ///

View File

@ -34,9 +34,8 @@ pub(super) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
} }
}) })
.for_each(|(label, lookup)| { .for_each(|(label, lookup)| {
CompletionItem::new(label) CompletionItem::new(CompletionKind::Magic, label)
.lookup_by(lookup) .lookup_by(lookup)
.kind(CompletionKind::Magic)
.add_to(acc) .add_to(acc)
}); });

View File

@ -5,7 +5,14 @@
SyntaxKind::*, SyntaxNodeRef, 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) { pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.is_trivial_path { 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)) Some(keyword("return", snip))
} }
fn keyword(kw: &str, snippet: &str) -> CompletionItem {
CompletionItem::new(kw)
.kind(Keyword)
.snippet(snippet)
.build()
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::completion::{CompletionKind, check_completion}; use crate::completion::{CompletionKind, check_completion};

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
Cancelable, Cancelable,
completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext}, completion::{CompletionItem, Completions, CompletionKind, CompletionContext},
}; };
pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 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(()), _ => return Ok(()),
}; };
let module_scope = target_module.scope(ctx.db)?; let module_scope = target_module.scope(ctx.db)?;
module_scope.entries().for_each(|(name, _res)| { module_scope.entries().for_each(|(name, res)| {
CompletionItem::new(name.to_string()) CompletionItem::new(CompletionKind::Reference, name.to_string())
.kind(Reference) .from_resolution(ctx.db, res)
.add_to(acc) .add_to(acc)
}); });
Ok(()) Ok(())

View File

@ -3,7 +3,7 @@
use crate::{ use crate::{
Cancelable, Cancelable,
completion::{CompletionItem, Completions, CompletionKind::*, CompletionContext}, completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
}; };
pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 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)| { .for_each(|(name, res)| {
CompletionItem::new(name.to_string()) CompletionItem::new(CompletionKind::Reference, name.to_string())
.kind(Reference) .from_resolution(ctx.db, res)
.add_to(acc) .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()) .flat_map(|scope| scopes.entries(scope).iter())
.filter(|entry| shadowed.insert(entry.name())) .filter(|entry| shadowed.insert(entry.name()))
.for_each(|entry| { .for_each(|entry| {
CompletionItem::new(entry.name().to_string()) CompletionItem::new(CompletionKind::Reference, entry.name().to_string())
.kind(Reference) .kind(CompletionItemKind::Binding)
.add_to(acc) .add_to(acc)
}); });
if scopes.self_param.is_some() { if scopes.self_param.is_some() {
CompletionItem::new("self").kind(Reference).add_to(acc); CompletionItem::new(CompletionKind::Reference, "self").add_to(acc);
} }
} }

View File

@ -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) { pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) { if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) {
return; return;
} }
CompletionItem::new("pd") snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
.snippet("eprintln!(\"$0 = {:?}\", $0);") snippet("ppd", "eprintln!(\"$0 = {:#?}\", $0);").add_to(acc);
.kind(Snippet)
.add_to(acc);
CompletionItem::new("ppd")
.snippet("eprintln!(\"$0 = {:#?}\", $0);")
.kind(Snippet)
.add_to(acc);
} }
pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) { pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionContext) {
if !ctx.is_new_item { if !ctx.is_new_item {
return; return;
} }
CompletionItem::new("Test function") snippet(
.lookup_by("tfn") "Test function",
.snippet( "\
"\
#[test] #[test]
fn ${1:feature}() { fn ${1:feature}() {
$0 $0
}", }",
) )
.kind(Snippet) .lookup_by("tfn")
.add_to(acc); .add_to(acc);
CompletionItem::new("pub(crate)")
.snippet("pub(crate) $0") snippet("pub(crate)", "pub(crate) $0").add_to(acc);
.kind(Snippet)
.add_to(acc);
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,13 +1,17 @@
use crate::db;
/// `CompletionItem` describes a single completion variant in the editor pop-up. /// `CompletionItem` describes a single completion variant in the editor pop-up.
/// It is basically a POD with various properties. To construct a /// It is basically a POD with various properties. To construct a
/// `CompletionItem`, use `new` method and the `Builder` struct. /// `CompletionItem`, use `new` method and the `Builder` struct.
#[derive(Debug)] #[derive(Debug)]
pub struct CompletionItem { pub struct CompletionItem {
/// Used only internally in tests, to check only specific kind of
/// completion.
completion_kind: CompletionKind,
label: String, label: String,
lookup: Option<String>, lookup: Option<String>,
snippet: Option<String>, snippet: Option<String>,
/// Used only internally in test, to check only specific kind of completion. kind: Option<CompletionItemKind>,
kind: CompletionKind,
} }
pub enum InsertText { pub enum InsertText {
@ -15,6 +19,15 @@ pub enum InsertText {
Snippet { text: String }, Snippet { text: String },
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompletionItemKind {
Snippet,
Keyword,
Module,
Function,
Binding,
}
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub(crate) enum CompletionKind { pub(crate) enum CompletionKind {
/// Parser-based keyword completion. /// Parser-based keyword completion.
@ -24,17 +37,17 @@ pub(crate) enum CompletionKind {
/// "Secret sauce" completions. /// "Secret sauce" completions.
Magic, Magic,
Snippet, Snippet,
Unspecified,
} }
impl CompletionItem { 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(); let label = label.into();
Builder { Builder {
completion_kind,
label, label,
lookup: None, lookup: None,
snippet: None, snippet: None,
kind: CompletionKind::Unspecified, kind: None,
} }
} }
/// What user sees in pop-up in the UI. /// What user sees in pop-up in the UI.
@ -57,15 +70,20 @@ pub fn insert_text(&self) -> InsertText {
Some(it) => InsertText::Snippet { text: it.clone() }, Some(it) => InsertText::Snippet { text: it.clone() },
} }
} }
pub fn kind(&self) -> Option<CompletionItemKind> {
self.kind
}
} }
/// A helper to make `CompletionItem`s. /// A helper to make `CompletionItem`s.
#[must_use] #[must_use]
pub(crate) struct Builder { pub(crate) struct Builder {
completion_kind: CompletionKind,
label: String, label: String,
lookup: Option<String>, lookup: Option<String>,
snippet: Option<String>, snippet: Option<String>,
kind: CompletionKind, kind: Option<CompletionItemKind>,
} }
impl Builder { impl Builder {
@ -79,6 +97,7 @@ pub(crate) fn build(self) -> CompletionItem {
lookup: self.lookup, lookup: self.lookup,
snippet: self.snippet, snippet: self.snippet,
kind: self.kind, kind: self.kind,
completion_kind: self.completion_kind,
} }
} }
pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@ -89,8 +108,25 @@ pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> Builder {
self.snippet = Some(snippet.into()); self.snippet = Some(snippet.into());
self self
} }
pub(crate) fn kind(mut self, kind: CompletionKind) -> Builder { pub(crate) fn kind(mut self, kind: CompletionItemKind) -> Builder {
self.kind = kind; 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 self
} }
} }
@ -154,7 +190,7 @@ fn normalize(expected: &str) -> String {
fn debug_render(&self, kind: CompletionKind) -> String { fn debug_render(&self, kind: CompletionKind) -> String {
let mut res = String::new(); let mut res = String::new();
for c in self.buf.iter() { for c in self.buf.iter() {
if c.kind == kind { if c.completion_kind == kind {
if let Some(lookup) = &c.lookup { if let Some(lookup) = &c.lookup {
res.push_str(lookup); res.push_str(lookup);
res.push_str(&format!(" {:?}", c.label)); res.push_str(&format!(" {:?}", c.label));

View File

@ -30,7 +30,7 @@ macro_rules! ctry {
}; };
pub use crate::{ pub use crate::{
completion::{CompletionItem, InsertText}, completion::{CompletionItem, CompletionItemKind, InsertText},
}; };
pub use ra_editor::{ pub use ra_editor::{
FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode, FileSymbol, Fold, FoldKind, HighlightedRange, LineIndex, Runnable, RunnableKind, StructureNode,

View File

@ -95,7 +95,7 @@ fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit)
r1.start().cmp(&r2.start()) r1.start().cmp(&r2.start())
} }
}) })
.map(|(ptr, scope)| *scope) .map(|(_ptr, scope)| *scope)
.unwrap_or(original_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() { if let Some(expr) = block.expr() {
eprintln!("{:?}", expr);
scopes.set_scope(expr.syntax(), scope); scopes.set_scope(expr.syntax(), scope);
compute_expr_scopes(expr, scopes, scope); compute_expr_scopes(expr, scopes, scope);
} }

View File

@ -39,7 +39,7 @@ macro_rules! ctry {
pub use self::{ pub use self::{
path::{Path, PathKind}, path::{Path, PathKind},
krate::Crate, krate::Crate,
module::{Module, ModuleId, Problem, nameres::ItemMap}, module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
function::{Function, FnScopes}, function::{Function, FnScopes},
}; };

View File

@ -16,7 +16,7 @@
arena::{Arena, Id}, arena::{Arena, Id},
}; };
pub use self::nameres::ModuleScope; pub use self::nameres::{ModuleScope, Resolution};
/// `Module` is API entry point to get all the information /// `Module` is API entry point to get all the information
/// about a particular module. /// about a particular module.

View File

@ -49,7 +49,7 @@ pub struct ModuleScope {
} }
impl 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() self.items.iter()
} }
pub fn get(&self, name: &SmolStr) -> Option<&Resolution> { pub fn get(&self, name: &SmolStr) -> Option<&Resolution> {

View File

@ -1,8 +1,8 @@
use languageserver_types::{ use languageserver_types::{
self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, 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_editor::{LineCol, LineIndex};
use ra_text_edit::{AtomTextEdit, TextEdit}; use ra_text_edit::{AtomTextEdit, TextEdit};
use ra_syntax::{SyntaxKind, TextRange, TextUnit}; use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@ -45,6 +45,46 @@ fn conv(self) -> <Self as Conv>::Output {
} }
} }
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 { impl ConvWith for Position {
type Ctx = LineIndex; type Ctx = LineIndex;
type Output = TextUnit; type Output = TextUnit;

View File

@ -2,13 +2,13 @@
use gen_lsp_server::ErrorCode; use gen_lsp_server::ErrorCode;
use languageserver_types::{ use languageserver_types::{
CodeActionResponse, Command, CompletionItem, CompletionItemKind, Diagnostic, CodeActionResponse, Command, Diagnostic,
DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
FoldingRangeParams, InsertTextFormat, Location, MarkupContent, MarkupKind, MarkedString, Position, FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, 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_syntax::{TextUnit, text_utils::intersect};
use ra_text_edit::text_utils::contains_offset_nonstrict; use ra_text_edit::text_utils::contains_offset_nonstrict;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -419,28 +419,7 @@ pub fn handle_completion(
None => return Ok(None), None => return Ok(None),
Some(items) => items, Some(items) => items,
}; };
let items = items let items = items.into_iter().map(|item| item.conv()).collect();
.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();
Ok(Some(req::CompletionResponse::Array(items))) Ok(Some(req::CompletionResponse::Array(items)))
} }