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"
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"

View File

@ -7,7 +7,7 @@
use crate::Result;
#[derive(Debug, Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum RawMessage {
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.
///

View File

@ -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)
});

View File

@ -5,7 +5,14 @@
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};

View File

@ -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(())

View File

@ -3,7 +3,7 @@
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);
}
}

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) {
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)]

View File

@ -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 @@ pub fn insert_text(&self) -> InsertText {
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 @@ pub(crate) fn build(self) -> CompletionItem {
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 @@ pub(crate) fn snippet(mut self, snippet: impl Into<String>) -> 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 @@ fn normalize(expected: &str) -> String {
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));

View File

@ -30,7 +30,7 @@ macro_rules! ctry {
};
pub use crate::{
completion::{CompletionItem, InsertText},
completion::{CompletionItem, CompletionItemKind, InsertText},
};
pub use ra_editor::{
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())
}
})
.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);
}

View File

@ -39,7 +39,7 @@ macro_rules! ctry {
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},
};

View File

@ -16,7 +16,7 @@
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.

View File

@ -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> {

View File

@ -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 @@ 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 {
type Ctx = LineIndex;
type Output = TextUnit;

View File

@ -2,13 +2,13 @@
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)))
}