mirror of
https://github.com/oxalica/nil.git
synced 2024-11-22 11:22:46 +03:00
Impl simple completion
This commit is contained in:
parent
bd8be85e34
commit
023a99fd34
@ -1,7 +1,7 @@
|
||||
use crate::vfs::{Vfs, VfsPath};
|
||||
use crossbeam_channel::Sender;
|
||||
use lsp_types::{self as lsp, notification as notif, request as req, OneOf, Url};
|
||||
use nil::{Analysis, AnalysisHost, Change, FilePos, InFile};
|
||||
use nil::{Analysis, AnalysisHost, Change, CompletionItemKind, FilePos, InFile};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use text_size::TextRange;
|
||||
|
||||
@ -15,6 +15,10 @@ pub fn server_capabilities() -> lsp::ServerCapabilities {
|
||||
},
|
||||
)),
|
||||
definition_provider: Some(OneOf::Left(true)),
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".into()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
@ -80,6 +84,36 @@ impl State {
|
||||
None => Some(lsp::GotoDefinitionResponse::Array(Vec::new())),
|
||||
}
|
||||
})
|
||||
.on::<req::Completion>(|st, params| {
|
||||
let pos = get_file_pos(&st.vfs.read().unwrap(), ¶ms.text_document_position)?;
|
||||
let items = st.analysis.completions(pos).ok()??;
|
||||
let vfs = st.vfs.read().unwrap();
|
||||
let items = items
|
||||
.into_iter()
|
||||
.filter_map(|item| {
|
||||
let kind = match item.kind {
|
||||
// FIXME: More specific?
|
||||
CompletionItemKind::Builtin => lsp::CompletionItemKind::KEYWORD,
|
||||
CompletionItemKind::Binding => lsp::CompletionItemKind::VARIABLE,
|
||||
};
|
||||
Some(lsp::CompletionItem {
|
||||
label: item.label.into(),
|
||||
kind: Some(kind),
|
||||
insert_text: None,
|
||||
insert_text_format: Some(lsp::InsertTextFormat::PLAIN_TEXT),
|
||||
// We don't support indentation yet.
|
||||
insert_text_mode: Some(lsp::InsertTextMode::ADJUST_INDENTATION),
|
||||
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||
range: to_lsp_range(&vfs, pos.map(|_| item.source_range))?,
|
||||
new_text: item.replace.into(),
|
||||
})),
|
||||
// TODO
|
||||
..Default::default()
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
Some(lsp::CompletionResponse::Array(items))
|
||||
})
|
||||
.finish();
|
||||
}
|
||||
|
||||
@ -159,11 +193,14 @@ fn get_file_pos(vfs: &Vfs, params: &lsp::TextDocumentPositionParams) -> Option<F
|
||||
|
||||
fn to_lsp_location(vfs: &Vfs, pos: InFile<TextRange>) -> Option<lsp::Location> {
|
||||
let url = vfs.file_path(pos.file_id)?.try_into().ok()?;
|
||||
Some(lsp::Location::new(url, to_lsp_range(vfs, pos)?))
|
||||
}
|
||||
|
||||
fn to_lsp_range(vfs: &Vfs, pos: InFile<TextRange>) -> Option<lsp::Range> {
|
||||
let (_, line1, col1) = vfs.get_file_line_col(pos.map(|range| range.start()))?;
|
||||
let (_, line2, col2) = vfs.get_file_line_col(pos.map(|range| range.end()))?;
|
||||
let range = lsp::Range::new(
|
||||
Some(lsp::Range::new(
|
||||
lsp::Position::new(line1, col1),
|
||||
lsp::Position::new(line2, col2),
|
||||
);
|
||||
Some(lsp::Location::new(url, range))
|
||||
))
|
||||
}
|
||||
|
79
src/ide/completion.rs
Normal file
79
src/ide/completion.rs
Normal file
@ -0,0 +1,79 @@
|
||||
use crate::{
|
||||
builtin,
|
||||
def::{AstPtr, DefDatabase},
|
||||
FileId,
|
||||
};
|
||||
use rowan::ast::AstNode;
|
||||
use smol_str::SmolStr;
|
||||
use syntax::{ast, match_ast, SyntaxKind, TextRange, TextSize, T};
|
||||
|
||||
/// A single completion variant in the editor pop-up.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CompletionItem {
|
||||
/// The label to show in the completion menu.
|
||||
pub label: SmolStr,
|
||||
/// Range of identifier that is being completed.
|
||||
pub source_range: TextRange,
|
||||
/// What content replaces the source range when user selects this item.
|
||||
pub replace: SmolStr,
|
||||
/// What item (struct, function, etc) are we completing.
|
||||
pub kind: CompletionItemKind,
|
||||
}
|
||||
|
||||
/// The type of the completion item.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum CompletionItemKind {
|
||||
Builtin,
|
||||
Binding,
|
||||
}
|
||||
|
||||
pub(crate) fn completions(
|
||||
db: &dyn DefDatabase,
|
||||
file_id: FileId,
|
||||
pos: TextSize,
|
||||
) -> Option<Vec<CompletionItem>> {
|
||||
let parse = db.parse(file_id).value;
|
||||
|
||||
let tok = parse.syntax_node().token_at_offset(pos).left_biased()?;
|
||||
let source_range = match tok.kind() {
|
||||
T![.] => TextRange::empty(pos),
|
||||
SyntaxKind::IDENT => tok.text_range(),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let ref_node = tok.parent_ancestors().find_map(|node| {
|
||||
match_ast! {
|
||||
match node {
|
||||
ast::Ref(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
})?;
|
||||
let source_map = db.source_map(file_id);
|
||||
let expr_id = source_map.node_expr(AstPtr::new(ref_node.syntax()))?;
|
||||
let scopes = db.scopes(file_id);
|
||||
let scope_id = scopes.scope_by_expr(expr_id)?;
|
||||
|
||||
// TODO: Better sorting.
|
||||
let mut items = scopes
|
||||
.ancestors(scope_id)
|
||||
.filter_map(|scope| scope.as_name_defs())
|
||||
.flat_map(|scope| scope.keys())
|
||||
.map(|name| CompletionItem {
|
||||
label: name.clone(),
|
||||
source_range,
|
||||
replace: name.clone(),
|
||||
kind: CompletionItemKind::Binding,
|
||||
})
|
||||
.chain(builtin::NAMES.iter().map(|name| CompletionItem {
|
||||
label: name.into(),
|
||||
source_range,
|
||||
replace: name.into(),
|
||||
kind: CompletionItemKind::Builtin,
|
||||
}))
|
||||
.collect::<Vec<_>>();
|
||||
items.sort_by(|lhs, rhs| lhs.label.cmp(&rhs.label));
|
||||
items.dedup_by(|lhs, rhs| lhs.label == rhs.label);
|
||||
|
||||
Some(items)
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
mod completion;
|
||||
mod goto_definition;
|
||||
|
||||
use crate::{base::SourceDatabaseStorage, def::DefDatabaseStorage, Change, FileId, FilePos};
|
||||
use rowan::TextRange;
|
||||
use salsa::{Cancelled, Database, Durability, ParallelDatabase};
|
||||
use std::fmt;
|
||||
|
||||
mod goto_definition;
|
||||
pub use completion::{CompletionItem, CompletionItemKind};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct NavigationTarget {
|
||||
@ -78,4 +81,8 @@ impl Analysis {
|
||||
pub fn goto_definition(&self, pos: FilePos) -> Cancellable<Option<Vec<NavigationTarget>>> {
|
||||
self.with_db(|db| goto_definition::goto_definition(db, pos.file_id, pos.value))
|
||||
}
|
||||
|
||||
pub fn completions(&self, pos: FilePos) -> Cancellable<Option<Vec<CompletionItem>>> {
|
||||
self.with_db(|db| completion::completions(db, pos.file_id, pos.value))
|
||||
}
|
||||
}
|
||||
|
@ -9,4 +9,6 @@ mod tests;
|
||||
|
||||
pub use base::{Change, FileId, FilePos, FileRange, InFile};
|
||||
pub use diagnostic::Diagnostic;
|
||||
pub use ide::{Analysis, AnalysisHost, RootDatabase};
|
||||
pub use ide::{
|
||||
Analysis, AnalysisHost, CompletionItem, CompletionItemKind, NavigationTarget, RootDatabase,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user