feat: add live-cycling LSP completion
This commit is contained in:
+75
-11
@@ -41,7 +41,9 @@ pub struct EditorSession {
|
||||
lsp: Lsp,
|
||||
lsp_request_id: u64,
|
||||
lsp_source_cursor: Option<Cursor>,
|
||||
lsp_source_mode: Mode,
|
||||
pending_hover: bool,
|
||||
pending_completion: bool,
|
||||
hover_owner: Option<String>,
|
||||
}
|
||||
|
||||
@@ -129,7 +131,9 @@ impl EditorSession {
|
||||
lsp: Lsp::new(config.lsp),
|
||||
lsp_request_id: 0,
|
||||
lsp_source_cursor: None,
|
||||
lsp_source_mode: Mode::Normal,
|
||||
pending_hover: false,
|
||||
pending_completion: false,
|
||||
hover_owner: None,
|
||||
};
|
||||
session.synchronize_documents();
|
||||
@@ -141,7 +145,7 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
pub fn handle(&mut self, input: Input) {
|
||||
self.cancel_pending_hover();
|
||||
self.cancel_pending_popup_request();
|
||||
let invocation = self.editor.handle(input);
|
||||
self.synchronize_documents();
|
||||
if let Some(invocation) = invocation {
|
||||
@@ -151,7 +155,7 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, command: &str) {
|
||||
self.cancel_pending_hover();
|
||||
self.cancel_pending_popup_request();
|
||||
let invocation = self.editor.execute(command);
|
||||
self.synchronize_documents();
|
||||
if let Some(invocation) = invocation {
|
||||
@@ -163,7 +167,7 @@ impl EditorSession {
|
||||
fn dispatch(&mut self, invocation: Invocation) {
|
||||
if invocation.plugin == "core" {
|
||||
let result = match invocation.command.as_str() {
|
||||
"lsp.definition" | "lsp.hover" => {
|
||||
"lsp.definition" | "lsp.hover" | "lsp.completion" => {
|
||||
if let Some(id) = self.lsp_request_id.checked_add(1) {
|
||||
self.lsp_request_id = id;
|
||||
let context = self.editor.context();
|
||||
@@ -173,11 +177,13 @@ impl EditorSession {
|
||||
};
|
||||
self.requests.entry("core".into()).or_default().latest = Some((id, source));
|
||||
self.lsp_source_cursor = Some(self.editor.cursor());
|
||||
self.lsp_source_mode = self.editor.mode();
|
||||
self.pending_hover = invocation.command == "lsp.hover";
|
||||
if self.pending_hover {
|
||||
self.lsp.hover(&context, id)
|
||||
} else {
|
||||
self.lsp.definition(&context, id)
|
||||
self.pending_completion = invocation.command == "lsp.completion";
|
||||
match invocation.command.as_str() {
|
||||
"lsp.hover" => self.lsp.hover(&context, id),
|
||||
"lsp.completion" => self.lsp.completion(&context, id),
|
||||
_ => self.lsp.definition(&context, id),
|
||||
}
|
||||
} else {
|
||||
Err("LSP request IDs exhausted".into())
|
||||
@@ -187,6 +193,7 @@ impl EditorSession {
|
||||
_ => unreachable!("session-dispatched core command"),
|
||||
};
|
||||
if let Err(error) = result {
|
||||
self.cancel_pending_popup_request();
|
||||
self.editor.set_message(format!("LSP: {error}"));
|
||||
}
|
||||
return;
|
||||
@@ -216,12 +223,13 @@ impl EditorSession {
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_pending_hover(&mut self) {
|
||||
if self.pending_hover {
|
||||
fn cancel_pending_popup_request(&mut self) {
|
||||
if self.pending_hover || self.pending_completion {
|
||||
if let Some(requests) = self.requests.get_mut("core") {
|
||||
requests.latest = None;
|
||||
}
|
||||
self.pending_hover = false;
|
||||
self.pending_completion = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,12 +251,13 @@ impl EditorSession {
|
||||
source.buffer_id != self.editor.buffer_id()
|
||||
|| source.revision != self.editor.text_revision()
|
||||
|| self.lsp_source_cursor != Some(self.editor.cursor())
|
||||
|| self.editor.mode() != Mode::Normal
|
||||
|| self.editor.mode() != self.lsp_source_mode
|
||||
})
|
||||
{
|
||||
requests.latest = None;
|
||||
self.lsp_source_cursor = None;
|
||||
self.pending_hover = false;
|
||||
self.pending_completion = false;
|
||||
}
|
||||
if !self.lsp.is_enabled() && !self.plugins.values().any(RubyPlugin::is_async) {
|
||||
self.documents.clear();
|
||||
@@ -363,7 +372,7 @@ impl EditorSession {
|
||||
&& self.source_is_current(source)
|
||||
&& (owner != "core"
|
||||
|| (self.lsp_source_cursor == Some(self.editor.cursor())
|
||||
&& self.editor.mode() == Mode::Normal))
|
||||
&& self.editor.mode() == self.lsp_source_mode))
|
||||
&& self
|
||||
.requests
|
||||
.get(owner)
|
||||
@@ -390,6 +399,18 @@ impl EditorSession {
|
||||
.is_none_or(|picker| !picker.focused)
|
||||
}
|
||||
|
||||
fn completion_is_current(&self, request_id: Option<u64>, source: InvocationSource) -> bool {
|
||||
self.pending_completion
|
||||
&& self.request_is_current("core", request_id, source)
|
||||
&& self.editor.mode() == Mode::Insert
|
||||
&& self.editor.palette().is_none()
|
||||
&& self
|
||||
.editor
|
||||
.file_picker()
|
||||
.is_none_or(|picker| !picker.focused)
|
||||
&& self.editor.startup_diagnostic().is_none()
|
||||
}
|
||||
|
||||
fn apply_update(&mut self, owner: &str, update: PluginUpdate) -> Result<bool, String> {
|
||||
if owner == "core"
|
||||
&& let Some(id) = update.request_id
|
||||
@@ -402,6 +423,30 @@ impl EditorSession {
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
for effect in &update.effects {
|
||||
if let UpdateEffect::Completion {
|
||||
buffer_id,
|
||||
revision,
|
||||
items,
|
||||
} = effect
|
||||
{
|
||||
if owner != "core" {
|
||||
return Err("Completion is a native LSP update only".into());
|
||||
}
|
||||
let source = InvocationSource {
|
||||
buffer_id: *buffer_id,
|
||||
revision: *revision,
|
||||
};
|
||||
if self.completion_is_current(update.request_id, source)
|
||||
&& let Err(error) = self.editor.validate_completion(items)
|
||||
{
|
||||
self.pending_completion = false;
|
||||
self.editor
|
||||
.set_message(format!("LSP completion failed: {error}"));
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Validate the whole applicable annotation batch before changing any buffer.
|
||||
let validation = (|| -> Result<(), String> {
|
||||
for effect in &update.effects {
|
||||
@@ -460,6 +505,7 @@ impl EditorSession {
|
||||
crate::editor::validate_hover_text(text)?;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Completion { .. } => {}
|
||||
UpdateEffect::Message { .. } => {}
|
||||
}
|
||||
}
|
||||
@@ -531,6 +577,24 @@ impl EditorSession {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Completion {
|
||||
buffer_id,
|
||||
revision,
|
||||
items,
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id,
|
||||
revision,
|
||||
};
|
||||
if self.completion_is_current(update.request_id, source) {
|
||||
self.pending_completion = false;
|
||||
if let Err(error) = self.editor.show_completion(items) {
|
||||
self.editor
|
||||
.set_message(format!("LSP completion failed: {error}"));
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Message { text } => {
|
||||
changed |= self.editor.message() != text;
|
||||
self.editor.set_message(text);
|
||||
|
||||
Reference in New Issue
Block a user