completion fix

change-detection
cossonleo 2 years ago
parent a15420ed1c
commit aec001ad84

@ -2887,13 +2887,11 @@ pub mod insert {
super::completion(cx); super::completion(cx);
} }
fn language_server_completion(cx: &mut Context, ch: char) { pub fn is_server_trigger_char(doc: &Document, ch: char) -> bool {
use helix_lsp::lsp; use helix_lsp::lsp;
// if ch matches completion char, trigger completion
let doc = doc_mut!(cx.editor);
let language_server = match doc.language_server() { let language_server = match doc.language_server() {
Some(language_server) => language_server, Some(language_server) => language_server,
None => return, None => return false,
}; };
let capabilities = language_server.capabilities(); let capabilities = language_server.capabilities();
@ -2903,11 +2901,35 @@ pub mod insert {
.. ..
}) = &capabilities.completion_provider }) = &capabilities.completion_provider
{ {
// TODO: what if trigger is multiple chars long triggers.iter().any(|trigger| trigger.contains(ch))
if triggers.iter().any(|trigger| trigger.contains(ch)) { } else {
cx.editor.clear_idle_timer(); false
super::completion(cx); }
}
fn language_server_completion(cx: &mut Context, ch: char) {
use helix_core::chars::char_is_word;
let config = cx.editor.config();
if !config.auto_completion {
return;
}
let (view, doc) = current_ref!(cx.editor);
if char_is_word(ch) && doc.savepoint.is_none() {
let text = doc.text().slice(..);
let cursor = doc.selection(view.id).primary().cursor(text);
let mut iter = text.chars_at(cursor);
iter.reverse();
for _ in 0..config.completion_trigger_len {
if iter.next().map_or(true, |c| !char_is_word(c)) {
return;
}
} }
cx.editor.reset_idle_timer();
return;
}
if is_server_trigger_char(doc, ch) {
cx.editor.reset_idle_timer_zero();
} }
} }
@ -3819,6 +3841,13 @@ pub fn completion(cx: &mut Context) {
let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding); let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
let future = language_server.completion(doc.identifier(), pos, None); let future = language_server.completion(doc.identifier(), pos, None);
let future = async move {
match future.await {
Ok(v) => Ok(v),
Err(helix_lsp::Error::Timeout) => Ok(serde_json::Value::Null),
Err(e) => Err(e),
}
};
let trigger_offset = cursor; let trigger_offset = cursor;
@ -3832,11 +3861,21 @@ pub fn completion(cx: &mut Context) {
let start_offset = cursor.saturating_sub(offset); let start_offset = cursor.saturating_sub(offset);
let prefix = text.slice(start_offset..cursor).to_string(); let prefix = text.slice(start_offset..cursor).to_string();
doc.savepoint();
let trigger_version = doc.version();
cx.callback( cx.callback(
future, future,
move |editor, compositor, response: Option<lsp::CompletionResponse>| { move |editor, compositor, response: Option<lsp::CompletionResponse>| {
let doc = doc_mut!(editor);
let savepoint = match doc.savepoint.take() {
Some(s) => s,
None => return,
};
if editor.mode != Mode::Insert { if editor.mode != Mode::Insert {
// we're not in insert mode anymore return;
}
if savepoint.0 != trigger_version {
doc.savepoint = Some(savepoint);
return; return;
} }
@ -3847,25 +3886,27 @@ pub fn completion(cx: &mut Context) {
is_incomplete: _is_incomplete, is_incomplete: _is_incomplete,
items, items,
})) => items, })) => items,
None => Vec::new(), None => {
editor.set_status(
"The completion response is none and will request server again",
);
editor.reset_idle_timer();
return;
}
}; };
if !prefix.is_empty() { if !prefix.is_empty() {
items = items items.retain(|item| match &item.filter_text {
.into_iter() Some(t) => t.starts_with(&prefix),
.filter(|item| { None => item.label.starts_with(&prefix),
item.filter_text });
.as_ref() };
.unwrap_or(&item.label)
.starts_with(&prefix)
})
.collect();
}
if items.is_empty() { if items.is_empty() {
// editor.set_error("No completion available"); // editor.set_error("No completion available".to_string());
return; return;
} }
doc.savepoint = Some(savepoint);
let size = compositor.size(); let size = compositor.size();
let ui = compositor.find::<ui::EditorView>().unwrap(); let ui = compositor.find::<ui::EditorView>().unwrap();
ui.set_completion( ui.set_completion(

@ -394,6 +394,16 @@ impl Component for Completion {
height = rel_height.min(height); height = rel_height.min(height);
} }
Rect::new(x, y, width, height) Rect::new(x, y, width, height)
} else if popup_x > 30 {
let mut height = area.height.saturating_sub(popup_y);
let mut width = popup_x;
if let Some((rel_width, rel_height)) = markdown_doc.required_size((width, height)) {
width = rel_width.min(width);
height = rel_height.min(height);
}
let x = popup_x - width;
let y = popup_y;
Rect::new(x, y, width, height)
} else { } else {
let half = area.height / 2; let half = area.height / 2;
let height = 15.min(half); let height = 15.min(half);

@ -851,7 +851,7 @@ impl EditorView {
} }
(Mode::Insert, Mode::Normal) => { (Mode::Insert, Mode::Normal) => {
// if exiting insert mode, remove completion // if exiting insert mode, remove completion
self.completion = None; self.clear_completion(cxt.editor);
// TODO: Use an on_mode_change hook to remove signature help // TODO: Use an on_mode_change hook to remove signature help
cxt.jobs.callback(async { cxt.jobs.callback(async {
@ -985,9 +985,6 @@ impl EditorView {
return; return;
} }
// Immediately initialize a savepoint
doc_mut!(editor).savepoint();
editor.last_completion = None; editor.last_completion = None;
self.last_insert.1.push(InsertEvent::TriggerCompletion); self.last_insert.1.push(InsertEvent::TriggerCompletion);
@ -1006,23 +1003,20 @@ impl EditorView {
} }
pub fn handle_idle_timeout(&mut self, cx: &mut crate::compositor::Context) -> EventResult { pub fn handle_idle_timeout(&mut self, cx: &mut crate::compositor::Context) -> EventResult {
if self.completion.is_some() let config = cx.editor.config();
|| cx.editor.mode != Mode::Insert if cx.editor.mode != Mode::Insert || !config.auto_completion {
|| !cx.editor.config().auto_completion
{
return EventResult::Ignored(None); return EventResult::Ignored(None);
} }
let mut cx = commands::Context { self.clear_completion(cx.editor);
commands::completion(&mut commands::Context {
register: None, register: None,
editor: cx.editor, editor: cx.editor,
jobs: cx.jobs, jobs: cx.jobs,
count: None, count: None,
callback: None, callback: None,
on_next_key_callback: None, on_next_key_callback: None,
}; });
crate::commands::insert::idle_completion(&mut cx);
EventResult::Consumed(None) EventResult::Consumed(None)
} }
} }
@ -1246,7 +1240,7 @@ impl Component for EditorView {
EventResult::Consumed(None) EventResult::Consumed(None)
} }
Event::Key(mut key) => { Event::Key(mut key) => {
cx.editor.reset_idle_timer(); cx.editor.clear_idle_timer();
canonicalize_key(&mut key); canonicalize_key(&mut key);
// clear status // clear status
@ -1298,7 +1292,8 @@ impl Component for EditorView {
if let Some(completion) = &mut self.completion { if let Some(completion) = &mut self.completion {
completion.update(&mut cx); completion.update(&mut cx);
if completion.is_empty() { if completion.is_empty() {
self.clear_completion(cx.editor); self.completion = None;
doc_mut!(cx.editor).savepoint = None;
} }
} }
} }

@ -112,7 +112,7 @@ pub struct Document {
// be more troublesome. // be more troublesome.
pub history: Cell<History>, pub history: Cell<History>,
pub savepoint: Option<Transaction>, pub savepoint: Option<(i32, Transaction)>,
last_saved_revision: usize, last_saved_revision: usize,
version: i32, // should be usize? version: i32, // should be usize?
@ -768,7 +768,8 @@ impl Document {
if self.savepoint.is_some() { if self.savepoint.is_some() {
take_with(&mut self.savepoint, |prev_revert| { take_with(&mut self.savepoint, |prev_revert| {
let revert = transaction.invert(&old_doc); let revert = transaction.invert(&old_doc);
Some(revert.compose(prev_revert.unwrap())) let (version, prev_revert) = prev_revert.unwrap();
Some((version, revert.compose(prev_revert)))
}); });
} }
@ -856,11 +857,11 @@ impl Document {
} }
pub fn savepoint(&mut self) { pub fn savepoint(&mut self) {
self.savepoint = Some(Transaction::new(self.text())); self.savepoint = Some((self.version, Transaction::new(self.text())));
} }
pub fn restore(&mut self, view_id: ViewId) { pub fn restore(&mut self, view_id: ViewId) {
if let Some(revert) = self.savepoint.take() { if let Some((_, revert)) = self.savepoint.take() {
self.apply(&revert, view_id); self.apply(&revert, view_id);
} }
} }

@ -763,6 +763,10 @@ impl Editor {
.reset(Instant::now() + config.idle_timeout); .reset(Instant::now() + config.idle_timeout);
} }
pub fn reset_idle_timer_zero(&mut self) {
self.idle_timer.as_mut().reset(Instant::now());
}
pub fn clear_status(&mut self) { pub fn clear_status(&mut self) {
self.status_msg = None; self.status_msg = None;
} }

Loading…
Cancel
Save