Retrieve completion options on ctrl-x.

This commit is contained in:
Blaž Hrastnik 2020-12-23 16:20:49 +09:00
parent 955cb81687
commit 56f2193811
7 changed files with 86 additions and 3 deletions

View file

@ -196,6 +196,12 @@ impl Client {
root_uri: None, // set to project root in the future root_uri: None, // set to project root in the future
initialization_options: None, initialization_options: None,
capabilities: lsp::ClientCapabilities { capabilities: lsp::ClientCapabilities {
// text_document:
// { completion: {
// dynamic_registration: bool
// completion_item: { snippet, documentation_format, ... }
// completion_item_kind: { }
// } }
..Default::default() ..Default::default()
}, },
trace: None, trace: None,
@ -358,4 +364,41 @@ impl Client {
pub async fn text_document_did_save(&self) -> anyhow::Result<()> { pub async fn text_document_did_save(&self) -> anyhow::Result<()> {
unimplemented!() unimplemented!()
} }
pub async fn completion(&self, doc: &Document) -> anyhow::Result<Vec<lsp::CompletionItem>> {
// TODO: figure out what should happen when you complete with multiple cursors
let params = lsp::CompletionParams {
text_document_position: lsp::TextDocumentPositionParams {
text_document: text_document_identifier(doc),
position: crate::util::pos_to_lsp_pos(
&doc.text().slice(..),
doc.selection().cursor(),
),
},
// TODO: support these tokens by async receiving and updating the choice list
work_done_progress_params: lsp::WorkDoneProgressParams {
work_done_token: None,
},
partial_result_params: lsp::PartialResultParams {
partial_result_token: None,
},
context: None,
// lsp::CompletionContext { trigger_kind: , trigger_character: Some(), }
};
let response = self.request::<lsp::request::Completion>(params).await?;
let items = match response {
Some(lsp::CompletionResponse::Array(items)) => items,
// TODO: do something with is_incomplete
Some(lsp::CompletionResponse::List(lsp::CompletionList {
is_incomplete: _is_incomplete,
items,
})) => items,
None => Vec::new(),
};
Ok(items)
}
} }

View file

@ -69,10 +69,15 @@ impl Application {
fn render(&mut self) { fn render(&mut self) {
let executor = &self.executor; let executor = &self.executor;
let language_servers = &self.language_servers;
let editor = &mut self.editor; let editor = &mut self.editor;
let compositor = &self.compositor; let compositor = &self.compositor;
let mut cx = crate::compositor::Context { editor, executor }; let mut cx = crate::compositor::Context {
editor,
executor,
language_servers,
};
let area = self.terminal.size().unwrap(); let area = self.terminal.size().unwrap();
compositor.render(area, self.terminal.current_buffer_mut(), &mut cx); compositor.render(area, self.terminal.current_buffer_mut(), &mut cx);
@ -113,6 +118,7 @@ impl Application {
let mut cx = crate::compositor::Context { let mut cx = crate::compositor::Context {
editor: &mut self.editor, editor: &mut self.editor,
executor: &self.executor, executor: &self.executor,
language_servers: &self.language_servers,
}; };
// Handle key events // Handle key events
let should_redraw = match event { let should_redraw = match event {

View file

@ -22,6 +22,7 @@ pub struct Context<'a, 'b> {
pub count: usize, pub count: usize,
pub view: &'a mut View, pub view: &'a mut View,
pub executor: &'a smol::Executor<'b>, pub executor: &'a smol::Executor<'b>,
pub language_servers: &'a helix_lsp::Registry,
pub callback: Option<crate::compositor::Callback>, pub callback: Option<crate::compositor::Callback>,
} }
@ -831,3 +832,31 @@ pub fn save(cx: &mut Context) {
// TODO: handle save errors somehow? // TODO: handle save errors somehow?
cx.executor.spawn(cx.view.doc.save()).detach(); cx.executor.spawn(cx.view.doc.save()).detach();
} }
pub fn completion(cx: &mut Context) {
let language_server = cx.language_servers.get("rust", &cx.executor).unwrap();
use log::info;
// TODO: blocking here is not ideal
let res = smol::block_on(language_server.completion(&cx.view.doc)).expect("completion failed!");
let picker = ui::Picker::new(
res,
|item| {
// format_fn
item.label.as_str().into()
},
|editor: &mut Editor, item| {
//
},
);
cx.callback = Some(Box::new(
move |compositor: &mut Compositor, editor: &mut Editor| {
compositor.push(Box::new(picker));
},
));
// TODO: when iterating over items, show the docs in popup
// language server client needs to be accessible via a registry of some sort
}

View file

@ -39,6 +39,7 @@ use helix_view::{Editor, View};
// shared with commands.rs // shared with commands.rs
pub struct Context<'a> { pub struct Context<'a> {
pub editor: &'a mut Editor, pub editor: &'a mut Editor,
pub language_servers: &'a helix_lsp::Registry,
pub executor: &'static smol::Executor<'static>, pub executor: &'static smol::Executor<'static>,
} }

View file

@ -208,6 +208,8 @@ pub fn default() -> Keymaps {
code: KeyCode::Tab, code: KeyCode::Tab,
modifiers: Modifiers::NONE modifiers: Modifiers::NONE
}] => commands::insert::insert_tab, }] => commands::insert::insert_tab,
vec![ctrl!('x')] => commands::completion,
), ),
Mode::Goto => hashmap!( Mode::Goto => hashmap!(
vec![Key { vec![Key {

View file

@ -263,6 +263,7 @@ impl Component for EditorView {
let mut cx = commands::Context { let mut cx = commands::Context {
view, view,
executor: cx.executor, executor: cx.executor,
language_servers: cx.language_servers,
count: 1, count: 1,
callback: None, callback: None,
}; };

View file

@ -196,12 +196,13 @@ impl<T> Component for Picker<T> {
// -- Render the frame: // -- Render the frame:
// clear area // clear area
let background = cx.editor.theme.get("ui.background"); let background = cx.editor.theme.get("ui.background");
for y in area.top()..area.bottom() { for y in area.top()..area.bottom() {
for x in area.left()..area.right() { for x in area.left()..area.right() {
let cell = surface.get_mut(x, y); let cell = surface.get_mut(x, y);
cell.symbol.clear(); cell.reset();
// cell.symbol.clear();
cell.set_style(background); cell.set_style(background);
} }
} }