feat: add native LSP hover action and popup
This commit is contained in:
+86
-5
@@ -41,6 +41,8 @@ pub struct EditorSession {
|
||||
lsp: Lsp,
|
||||
lsp_request_id: u64,
|
||||
lsp_source_cursor: Option<Cursor>,
|
||||
pending_hover: bool,
|
||||
hover_owner: Option<String>,
|
||||
}
|
||||
|
||||
impl EditorSession {
|
||||
@@ -124,6 +126,8 @@ impl EditorSession {
|
||||
lsp: Lsp::new(config.lsp),
|
||||
lsp_request_id: 0,
|
||||
lsp_source_cursor: None,
|
||||
pending_hover: false,
|
||||
hover_owner: None,
|
||||
};
|
||||
session.synchronize_documents();
|
||||
Ok(session)
|
||||
@@ -134,6 +138,7 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
pub fn handle(&mut self, input: Input) {
|
||||
self.cancel_pending_hover();
|
||||
let invocation = self.editor.handle(input);
|
||||
self.synchronize_documents();
|
||||
if let Some(invocation) = invocation {
|
||||
@@ -143,6 +148,7 @@ impl EditorSession {
|
||||
}
|
||||
|
||||
pub fn execute(&mut self, command: &str) {
|
||||
self.cancel_pending_hover();
|
||||
let invocation = self.editor.execute(command);
|
||||
self.synchronize_documents();
|
||||
if let Some(invocation) = invocation {
|
||||
@@ -154,7 +160,7 @@ impl EditorSession {
|
||||
fn dispatch(&mut self, invocation: Invocation) {
|
||||
if invocation.plugin == "core" {
|
||||
let result = match invocation.command.as_str() {
|
||||
"lsp_definition" => {
|
||||
"lsp_definition" | "lsp_hover" => {
|
||||
if let Some(id) = self.lsp_request_id.checked_add(1) {
|
||||
self.lsp_request_id = id;
|
||||
let context = self.editor.context();
|
||||
@@ -164,7 +170,12 @@ impl EditorSession {
|
||||
};
|
||||
self.requests.entry("core".into()).or_default().latest = Some((id, source));
|
||||
self.lsp_source_cursor = Some(self.editor.cursor());
|
||||
self.lsp.definition(&context, id)
|
||||
self.pending_hover = invocation.command == "lsp_hover";
|
||||
if self.pending_hover {
|
||||
self.lsp.hover(&context, id)
|
||||
} else {
|
||||
self.lsp.definition(&context, id)
|
||||
}
|
||||
} else {
|
||||
Err("LSP request IDs exhausted".into())
|
||||
}
|
||||
@@ -202,11 +213,24 @@ impl EditorSession {
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_pending_hover(&mut self) {
|
||||
if self.pending_hover {
|
||||
if let Some(requests) = self.requests.get_mut("core") {
|
||||
requests.latest = None;
|
||||
}
|
||||
self.pending_hover = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn retire(&mut self, owner: &str, error: &str) {
|
||||
// Drop also stops a live host whose protocol-valid effects were rejected.
|
||||
drop(self.plugins.remove(owner));
|
||||
self.requests.remove(owner);
|
||||
self.editor.disable_plugin(owner, error);
|
||||
if self.hover_owner.as_deref() == Some(owner) {
|
||||
self.editor.clear_hover();
|
||||
self.hover_owner = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn synchronize_documents(&mut self) -> bool {
|
||||
@@ -313,7 +337,7 @@ impl EditorSession {
|
||||
.any(|document| document.id == source.buffer_id && document.revision == source.revision)
|
||||
}
|
||||
|
||||
fn navigation_is_current(
|
||||
fn request_is_current(
|
||||
&self,
|
||||
owner: &str,
|
||||
request_id: Option<u64>,
|
||||
@@ -335,7 +359,33 @@ impl EditorSession {
|
||||
})
|
||||
}
|
||||
|
||||
fn hover_is_current(
|
||||
&self,
|
||||
owner: &str,
|
||||
request_id: Option<u64>,
|
||||
source: InvocationSource,
|
||||
) -> bool {
|
||||
self.request_is_current(owner, request_id, source)
|
||||
&& self.editor.mode() == Mode::Normal
|
||||
&& self.editor.palette().is_none()
|
||||
&& self
|
||||
.editor
|
||||
.file_picker()
|
||||
.is_none_or(|picker| !picker.focused)
|
||||
}
|
||||
|
||||
fn apply_update(&mut self, owner: &str, update: PluginUpdate) -> Result<bool, String> {
|
||||
if owner == "core"
|
||||
&& let Some(id) = update.request_id
|
||||
&& self
|
||||
.requests
|
||||
.get(owner)
|
||||
.and_then(|requests| requests.latest)
|
||||
.map(|latest| latest.0)
|
||||
!= Some(id)
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
// Validate the whole applicable annotation batch before changing any buffer.
|
||||
for effect in &update.effects {
|
||||
match effect {
|
||||
@@ -357,7 +407,7 @@ impl EditorSession {
|
||||
buffer_id: *buffer_id,
|
||||
revision: *revision,
|
||||
};
|
||||
if self.navigation_is_current(owner, update.request_id, source)
|
||||
if self.request_is_current(owner, update.request_id, source)
|
||||
&& (path.as_os_str().is_empty()
|
||||
|| path.to_string_lossy().chars().any(char::is_control))
|
||||
{
|
||||
@@ -366,6 +416,19 @@ impl EditorSession {
|
||||
);
|
||||
}
|
||||
}
|
||||
UpdateEffect::Hover {
|
||||
buffer_id,
|
||||
revision,
|
||||
text,
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id: *buffer_id,
|
||||
revision: *revision,
|
||||
};
|
||||
if self.hover_is_current(owner, update.request_id, source) {
|
||||
crate::editor::validate_hover_text(text)?;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Message { .. } => {}
|
||||
}
|
||||
}
|
||||
@@ -392,7 +455,7 @@ impl EditorSession {
|
||||
buffer_id,
|
||||
revision,
|
||||
};
|
||||
if self.navigation_is_current(owner, update.request_id, source) {
|
||||
if self.request_is_current(owner, update.request_id, source) {
|
||||
if let Err(error) =
|
||||
self.editor.navigate_to(path, TextPosition { row, column })
|
||||
{
|
||||
@@ -402,6 +465,24 @@ impl EditorSession {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Hover {
|
||||
buffer_id,
|
||||
revision,
|
||||
text,
|
||||
} => {
|
||||
let source = InvocationSource {
|
||||
buffer_id,
|
||||
revision,
|
||||
};
|
||||
if self.hover_is_current(owner, update.request_id, source) {
|
||||
self.editor.show_hover(text)?;
|
||||
self.hover_owner = Some(owner.into());
|
||||
if owner == "core" {
|
||||
self.pending_hover = false;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
UpdateEffect::Message { text } => {
|
||||
changed |= self.editor.message() != text;
|
||||
self.editor.set_message(text);
|
||||
|
||||
Reference in New Issue
Block a user