Merge branch 'completion'

change-detection
trivernis 2 years ago
commit c7492aea1f
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -2931,13 +2931,11 @@ pub mod insert {
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;
// if ch matches completion char, trigger completion
let doc = doc_mut!(cx.editor);
let language_server = match doc.language_server() {
Some(language_server) => language_server,
None => return,
None => return false,
};
let capabilities = language_server.capabilities();
@ -2947,11 +2945,35 @@ pub mod insert {
..
}) = &capabilities.completion_provider
{
// TODO: what if trigger is multiple chars long
if triggers.iter().any(|trigger| trigger.contains(ch)) {
cx.editor.clear_idle_timer();
super::completion(cx);
triggers.iter().any(|trigger| trigger.contains(ch))
} else {
false
}
}
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();
}
}
@ -3863,6 +3885,13 @@ pub fn completion(cx: &mut Context) {
let pos = pos_to_lsp_pos(doc.text(), cursor, offset_encoding);
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;
@ -3876,11 +3905,21 @@ pub fn completion(cx: &mut Context) {
let start_offset = cursor.saturating_sub(offset);
let prefix = text.slice(start_offset..cursor).to_string();
doc.savepoint();
let trigger_version = doc.version();
cx.callback(
future,
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 {
// we're not in insert mode anymore
return;
}
if savepoint.0 != trigger_version {
doc.savepoint = Some(savepoint);
return;
}
@ -3891,25 +3930,27 @@ pub fn completion(cx: &mut Context) {
is_incomplete: _is_incomplete,
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() {
items = items
.into_iter()
.filter(|item| {
item.filter_text
.as_ref()
.unwrap_or(&item.label)
.starts_with(&prefix)
})
.collect();
}
items.retain(|item| match &item.filter_text {
Some(t) => t.starts_with(&prefix),
None => item.label.starts_with(&prefix),
});
};
if items.is_empty() {
// editor.set_error("No completion available");
// editor.set_error("No completion available".to_string());
return;
}
doc.savepoint = Some(savepoint);
let size = compositor.size();
let ui = compositor.find::<ui::EditorView>().unwrap();
ui.set_completion(

@ -394,6 +394,16 @@ impl Component for Completion {
height = rel_height.min(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 {
let half = area.height / 2;
let height = 15.min(half);

@ -853,7 +853,7 @@ impl EditorView {
}
(Mode::Insert, Mode::Normal) => {
// 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
cxt.jobs.callback(async {
@ -987,9 +987,6 @@ impl EditorView {
return;
}
// Immediately initialize a savepoint
doc_mut!(editor).savepoint();
editor.last_completion = None;
self.last_insert.1.push(InsertEvent::TriggerCompletion);
@ -1008,23 +1005,20 @@ impl EditorView {
}
pub fn handle_idle_timeout(&mut self, cx: &mut crate::compositor::Context) -> EventResult {
if self.completion.is_some()
|| cx.editor.mode != Mode::Insert
|| !cx.editor.config().auto_completion
{
let config = cx.editor.config();
if cx.editor.mode != Mode::Insert || !config.auto_completion {
return EventResult::Ignored(None);
}
let mut cx = commands::Context {
self.clear_completion(cx.editor);
commands::completion(&mut commands::Context {
register: None,
editor: cx.editor,
jobs: cx.jobs,
count: None,
callback: None,
on_next_key_callback: None,
};
crate::commands::insert::idle_completion(&mut cx);
});
EventResult::Consumed(None)
}
}
@ -1253,7 +1247,7 @@ impl Component for EditorView {
EventResult::Consumed(None)
}
Event::Key(mut key) => {
cx.editor.reset_idle_timer();
cx.editor.clear_idle_timer();
canonicalize_key(&mut key);
// clear status
@ -1305,7 +1299,8 @@ impl Component for EditorView {
if let Some(completion) = &mut self.completion {
completion.update(&mut cx);
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.
pub history: Cell<History>,
pub savepoint: Option<Transaction>,
pub savepoint: Option<(i32, Transaction)>,
last_saved_revision: usize,
version: i32, // should be usize?
@ -768,7 +768,8 @@ impl Document {
if self.savepoint.is_some() {
take_with(&mut self.savepoint, |prev_revert| {
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)))
});
}
@ -858,11 +859,11 @@ impl Document {
}
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) {
if let Some(revert) = self.savepoint.take() {
if let Some((_, revert)) = self.savepoint.take() {
self.apply(&revert, view_id);
}
}

@ -826,6 +826,10 @@ impl Editor {
.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) {
self.status_msg = None;
}

Loading…
Cancel
Save