mirror of
https://github.com/helix-editor/helix.git
synced 2025-04-02 02:17:44 +03:00
Move LSP inlay hints to the event system
This commit is contained in:
parent
a2c580c4ae
commit
cbea11947d
8 changed files with 368 additions and 216 deletions
|
@ -13,18 +13,9 @@ use tui::{text::Span, widgets::Row};
|
|||
|
||||
use super::{align_view, push_jump, Align, Context, Editor};
|
||||
|
||||
use helix_core::{
|
||||
diagnostic::DiagnosticProvider, syntax::LanguageServerFeature,
|
||||
text_annotations::InlineAnnotation, Selection, Uri,
|
||||
};
|
||||
use helix_core::{diagnostic::DiagnosticProvider, syntax::LanguageServerFeature, Selection, Uri};
|
||||
use helix_stdx::path;
|
||||
use helix_view::{
|
||||
document::{DocumentInlayHints, DocumentInlayHintsId},
|
||||
editor::Action,
|
||||
handlers::lsp::SignatureHelpInvoked,
|
||||
theme::Style,
|
||||
Document, View,
|
||||
};
|
||||
use helix_view::{editor::Action, handlers::lsp::SignatureHelpInvoked, theme::Style};
|
||||
|
||||
use crate::{
|
||||
compositor::{self, Compositor},
|
||||
|
@ -1251,164 +1242,3 @@ pub fn select_references_to_symbol_under_cursor(cx: &mut Context) {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn compute_inlay_hints_for_all_views(editor: &mut Editor, jobs: &mut crate::job::Jobs) {
|
||||
if !editor.config().lsp.display_inlay_hints {
|
||||
return;
|
||||
}
|
||||
|
||||
for (view, _) in editor.tree.views() {
|
||||
let doc = match editor.documents.get(&view.doc) {
|
||||
Some(doc) => doc,
|
||||
None => continue,
|
||||
};
|
||||
if let Some(callback) = compute_inlay_hints_for_view(view, doc) {
|
||||
jobs.callback(callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_inlay_hints_for_view(
|
||||
view: &View,
|
||||
doc: &Document,
|
||||
) -> Option<std::pin::Pin<Box<impl Future<Output = Result<crate::job::Callback, anyhow::Error>>>>> {
|
||||
let view_id = view.id;
|
||||
let doc_id = view.doc;
|
||||
|
||||
let language_server = doc
|
||||
.language_servers_with_feature(LanguageServerFeature::InlayHints)
|
||||
.next()?;
|
||||
|
||||
let doc_text = doc.text();
|
||||
let len_lines = doc_text.len_lines();
|
||||
|
||||
// Compute ~3 times the current view height of inlay hints, that way some scrolling
|
||||
// will not show half the view with hints and half without while still being faster
|
||||
// than computing all the hints for the full file (which could be dozens of time
|
||||
// longer than the view is).
|
||||
let view_height = view.inner_height();
|
||||
let first_visible_line =
|
||||
doc_text.char_to_line(doc.view_offset(view_id).anchor.min(doc_text.len_chars()));
|
||||
let first_line = first_visible_line.saturating_sub(view_height);
|
||||
let last_line = first_visible_line
|
||||
.saturating_add(view_height.saturating_mul(2))
|
||||
.min(len_lines);
|
||||
|
||||
let new_doc_inlay_hints_id = DocumentInlayHintsId {
|
||||
first_line,
|
||||
last_line,
|
||||
};
|
||||
// Don't recompute the annotations in case nothing has changed about the view
|
||||
if !doc.inlay_hints_oudated
|
||||
&& doc
|
||||
.inlay_hints(view_id)
|
||||
.is_some_and(|dih| dih.id == new_doc_inlay_hints_id)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let doc_slice = doc_text.slice(..);
|
||||
let first_char_in_range = doc_slice.line_to_char(first_line);
|
||||
let last_char_in_range = doc_slice.line_to_char(last_line);
|
||||
|
||||
let range = helix_lsp::util::range_to_lsp_range(
|
||||
doc_text,
|
||||
helix_core::Range::new(first_char_in_range, last_char_in_range),
|
||||
language_server.offset_encoding(),
|
||||
);
|
||||
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
|
||||
let callback = super::make_job_callback(
|
||||
language_server.text_document_range_inlay_hints(doc.identifier(), range, None)?,
|
||||
move |editor, _compositor, response: Option<Vec<lsp::InlayHint>>| {
|
||||
// The config was modified or the window was closed while the request was in flight
|
||||
if !editor.config().lsp.display_inlay_hints || editor.tree.try_get(view_id).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add annotations to relevant document, not the current one (it may have changed in between)
|
||||
let doc = match editor.documents.get_mut(&doc_id) {
|
||||
Some(doc) => doc,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// If we have neither hints nor an LSP, empty the inlay hints since they're now oudated
|
||||
let mut hints = match response {
|
||||
Some(hints) if !hints.is_empty() => hints,
|
||||
_ => {
|
||||
doc.set_inlay_hints(
|
||||
view_id,
|
||||
DocumentInlayHints::empty_with_id(new_doc_inlay_hints_id),
|
||||
);
|
||||
doc.inlay_hints_oudated = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Most language servers will already send them sorted but ensure this is the case to
|
||||
// avoid errors on our end.
|
||||
hints.sort_by_key(|inlay_hint| inlay_hint.position);
|
||||
|
||||
let mut padding_before_inlay_hints = Vec::new();
|
||||
let mut type_inlay_hints = Vec::new();
|
||||
let mut parameter_inlay_hints = Vec::new();
|
||||
let mut other_inlay_hints = Vec::new();
|
||||
let mut padding_after_inlay_hints = Vec::new();
|
||||
|
||||
let doc_text = doc.text();
|
||||
|
||||
for hint in hints {
|
||||
let char_idx =
|
||||
match helix_lsp::util::lsp_pos_to_pos(doc_text, hint.position, offset_encoding)
|
||||
{
|
||||
Some(pos) => pos,
|
||||
// Skip inlay hints that have no "real" position
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let label = match hint.label {
|
||||
lsp::InlayHintLabel::String(s) => s,
|
||||
lsp::InlayHintLabel::LabelParts(parts) => parts
|
||||
.into_iter()
|
||||
.map(|p| p.value)
|
||||
.collect::<Vec<_>>()
|
||||
.join(""),
|
||||
};
|
||||
|
||||
let inlay_hints_vec = match hint.kind {
|
||||
Some(lsp::InlayHintKind::TYPE) => &mut type_inlay_hints,
|
||||
Some(lsp::InlayHintKind::PARAMETER) => &mut parameter_inlay_hints,
|
||||
// We can't warn on unknown kind here since LSPs are free to set it or not, for
|
||||
// example Rust Analyzer does not: every kind will be `None`.
|
||||
_ => &mut other_inlay_hints,
|
||||
};
|
||||
|
||||
if let Some(true) = hint.padding_left {
|
||||
padding_before_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
|
||||
}
|
||||
|
||||
inlay_hints_vec.push(InlineAnnotation::new(char_idx, label));
|
||||
|
||||
if let Some(true) = hint.padding_right {
|
||||
padding_after_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
|
||||
}
|
||||
}
|
||||
|
||||
doc.set_inlay_hints(
|
||||
view_id,
|
||||
DocumentInlayHints {
|
||||
id: new_doc_inlay_hints_id,
|
||||
type_inlay_hints,
|
||||
parameter_inlay_hints,
|
||||
other_inlay_hints,
|
||||
padding_before_inlay_hints,
|
||||
padding_after_inlay_hints,
|
||||
},
|
||||
);
|
||||
doc.inlay_hints_oudated = false;
|
||||
},
|
||||
);
|
||||
|
||||
Some(callback)
|
||||
}
|
||||
|
|
|
@ -1623,8 +1623,6 @@ fn lsp_stop(cx: &mut compositor::Context, args: Args, event: PromptEvent) -> any
|
|||
for doc in cx.editor.documents_mut() {
|
||||
if let Some(client) = doc.remove_language_server_by_name(ls_name) {
|
||||
doc.clear_diagnostics_for_language_server(client.id());
|
||||
doc.reset_all_inlay_hints();
|
||||
doc.inlay_hints_oudated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use helix_event::AsyncHook;
|
|||
use crate::config::Config;
|
||||
use crate::events;
|
||||
use crate::handlers::auto_save::AutoSaveHandler;
|
||||
use crate::handlers::inlay_hints::InlayHintHandler;
|
||||
use crate::handlers::signature_help::SignatureHelpHandler;
|
||||
|
||||
pub use helix_view::handlers::Handlers;
|
||||
|
@ -16,6 +17,7 @@ mod auto_save;
|
|||
pub mod completion;
|
||||
mod diagnostics;
|
||||
mod document_colors;
|
||||
mod inlay_hints;
|
||||
mod signature_help;
|
||||
mod snippet;
|
||||
|
||||
|
@ -24,12 +26,14 @@ pub fn setup(config: Arc<ArcSwap<Config>>) -> Handlers {
|
|||
|
||||
let event_tx = completion::CompletionHandler::new(config).spawn();
|
||||
let signature_hints = SignatureHelpHandler::new().spawn();
|
||||
let inlay_hints = InlayHintHandler::default().spawn();
|
||||
let auto_save = AutoSaveHandler::new().spawn();
|
||||
let document_colors = DocumentColorsHandler::default().spawn();
|
||||
|
||||
let handlers = Handlers {
|
||||
completions: helix_view::handlers::completion::CompletionHandler::new(event_tx),
|
||||
signature_hints,
|
||||
inlay_hints,
|
||||
auto_save,
|
||||
document_colors,
|
||||
};
|
||||
|
@ -41,5 +45,6 @@ pub fn setup(config: Arc<ArcSwap<Config>>) -> Handlers {
|
|||
diagnostics::register_hooks(&handlers);
|
||||
snippet::register_hooks(&handlers);
|
||||
document_colors::register_hooks(&handlers);
|
||||
inlay_hints::register_hooks(&handlers);
|
||||
handlers
|
||||
}
|
||||
|
|
338
helix-term/src/handlers/inlay_hints.rs
Normal file
338
helix-term/src/handlers/inlay_hints.rs
Normal file
|
@ -0,0 +1,338 @@
|
|||
use std::{collections::HashSet, mem, time::Duration};
|
||||
|
||||
use crate::job;
|
||||
|
||||
use super::Handlers;
|
||||
|
||||
use helix_core::{syntax::LanguageServerFeature, text_annotations::InlineAnnotation};
|
||||
use helix_event::{cancelable_future, register_hook, send_blocking};
|
||||
use helix_lsp::lsp;
|
||||
use helix_view::{
|
||||
document::{DocumentInlayHints, DocumentInlayHintsId},
|
||||
events::{
|
||||
DocumentDidChange, DocumentDidOpen, LanguageServerExited, LanguageServerInitialized,
|
||||
SelectionDidChange,
|
||||
},
|
||||
handlers::lsp::InlayHintEvent,
|
||||
DocumentId, Editor, ViewId,
|
||||
};
|
||||
use tokio::time::Instant;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct InlayHintHandler {
|
||||
views: HashSet<ViewId>,
|
||||
docs: HashSet<DocumentId>,
|
||||
}
|
||||
|
||||
const DOCUMENT_CHANGE_DEBOUNCE: Duration = Duration::from_millis(500);
|
||||
const VIEWPORT_SCROLL_DEBOUNCE: Duration = Duration::from_millis(100);
|
||||
|
||||
impl helix_event::AsyncHook for InlayHintHandler {
|
||||
type Event = InlayHintEvent;
|
||||
|
||||
fn handle_event(&mut self, event: Self::Event, timeout: Option<Instant>) -> Option<Instant> {
|
||||
match event {
|
||||
InlayHintEvent::DocumentChanged(doc) => {
|
||||
self.docs.insert(doc);
|
||||
Some(Instant::now() + DOCUMENT_CHANGE_DEBOUNCE)
|
||||
}
|
||||
InlayHintEvent::ViewportScrolled(view) => {
|
||||
self.views.insert(view);
|
||||
let mut new_timeout = Instant::now() + VIEWPORT_SCROLL_DEBOUNCE;
|
||||
if let Some(timeout) = timeout {
|
||||
new_timeout = new_timeout.max(timeout);
|
||||
}
|
||||
Some(new_timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_debounce(&mut self) {
|
||||
let mut views = mem::take(&mut self.views);
|
||||
let docs = mem::take(&mut self.docs);
|
||||
|
||||
job::dispatch_blocking(move |editor, _compositor| {
|
||||
// Drop any views which have been closed.
|
||||
views.retain(|&view| editor.tree.contains(view));
|
||||
// Add any views that show documents which changed.
|
||||
views.extend(
|
||||
editor
|
||||
.tree
|
||||
.views()
|
||||
.filter_map(|(view, _)| docs.contains(&view.doc).then_some(view.id)),
|
||||
);
|
||||
|
||||
for view in views {
|
||||
let doc = editor.tree.get(view).doc;
|
||||
let is_scroll = !docs.contains(&doc);
|
||||
request_inlay_hints_for_view(editor, view, doc, is_scroll);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn request_inlay_hints_for_view(
|
||||
editor: &mut Editor,
|
||||
view_id: ViewId,
|
||||
doc_id: DocumentId,
|
||||
is_scroll: bool,
|
||||
) {
|
||||
if !editor.config().lsp.display_inlay_hints {
|
||||
return;
|
||||
}
|
||||
let Some(doc) = editor.documents.get_mut(&doc_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(view) = editor.tree.try_get(view_id) else {
|
||||
return;
|
||||
};
|
||||
let Some(language_server) = doc
|
||||
.language_servers_with_feature(LanguageServerFeature::InlayHints)
|
||||
.next()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let rope = doc.text();
|
||||
let text = rope.slice(..);
|
||||
let len_lines = text.len_lines();
|
||||
let view_height = view.inner_height();
|
||||
let first_visible_line =
|
||||
text.char_to_line(doc.view_offset(view_id).anchor.min(text.len_chars()));
|
||||
let first_line = first_visible_line.saturating_sub(view_height);
|
||||
let last_line = first_visible_line
|
||||
.saturating_add(view_height.saturating_mul(2))
|
||||
.min(len_lines);
|
||||
let new_doc_inlay_hints_id = DocumentInlayHintsId {
|
||||
first_line,
|
||||
last_line,
|
||||
};
|
||||
// If the view was updated by scrolling (rather than changing) and the viewport still has the
|
||||
// the same position, we can reuse the hints.
|
||||
if is_scroll
|
||||
&& doc
|
||||
.inlay_hints(view_id)
|
||||
.is_some_and(|hint| hint.id == new_doc_inlay_hints_id)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let offset_encoding = language_server.offset_encoding();
|
||||
let range = helix_lsp::util::range_to_lsp_range(
|
||||
rope,
|
||||
helix_core::Range::new(text.line_to_char(first_line), text.line_to_char(last_line)),
|
||||
offset_encoding,
|
||||
);
|
||||
let future = language_server
|
||||
.text_document_range_inlay_hints(doc.identifier(), range, None)
|
||||
.expect("language server must return Some if it supports inlay hints");
|
||||
let controller = doc.inlay_hint_controllers.entry(view_id).or_default();
|
||||
let cancel = controller.restart();
|
||||
|
||||
tokio::spawn(async move {
|
||||
match cancelable_future(future, cancel).await {
|
||||
Some(Ok(res)) => {
|
||||
job::dispatch(move |editor, _compositor| {
|
||||
attach_inlay_hints(
|
||||
editor,
|
||||
view_id,
|
||||
doc_id,
|
||||
new_doc_inlay_hints_id,
|
||||
offset_encoding,
|
||||
res,
|
||||
);
|
||||
})
|
||||
.await
|
||||
}
|
||||
Some(Err(err)) => log::error!("inlay hint request failed: {err}"),
|
||||
None => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn attach_inlay_hints(
|
||||
editor: &mut Editor,
|
||||
view_id: ViewId,
|
||||
doc_id: DocumentId,
|
||||
id: DocumentInlayHintsId,
|
||||
offset_encoding: helix_lsp::OffsetEncoding,
|
||||
response: Option<Vec<lsp::InlayHint>>,
|
||||
) {
|
||||
if !editor.config().lsp.display_inlay_hints || editor.tree.try_get(view_id).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(doc) = editor.documents.get_mut(&doc_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut hints = match response {
|
||||
Some(hints) if !hints.is_empty() => hints,
|
||||
_ => {
|
||||
doc.set_inlay_hints(view_id, DocumentInlayHints::empty_with_id(id));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Most language servers will already send them sorted but ensure this is the case to
|
||||
// avoid errors on our end.
|
||||
hints.sort_by_key(|inlay_hint| inlay_hint.position);
|
||||
|
||||
let mut padding_before_inlay_hints = Vec::new();
|
||||
let mut type_inlay_hints = Vec::new();
|
||||
let mut parameter_inlay_hints = Vec::new();
|
||||
let mut other_inlay_hints = Vec::new();
|
||||
let mut padding_after_inlay_hints = Vec::new();
|
||||
|
||||
let doc_text = doc.text();
|
||||
|
||||
for hint in hints {
|
||||
let char_idx =
|
||||
match helix_lsp::util::lsp_pos_to_pos(doc_text, hint.position, offset_encoding) {
|
||||
Some(pos) => pos,
|
||||
// Skip inlay hints that have no "real" position
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let label = match hint.label {
|
||||
lsp::InlayHintLabel::String(s) => s,
|
||||
lsp::InlayHintLabel::LabelParts(parts) => parts
|
||||
.into_iter()
|
||||
.map(|p| p.value)
|
||||
.collect::<Vec<_>>()
|
||||
.join(""),
|
||||
};
|
||||
|
||||
let inlay_hints_vec = match hint.kind {
|
||||
Some(lsp::InlayHintKind::TYPE) => &mut type_inlay_hints,
|
||||
Some(lsp::InlayHintKind::PARAMETER) => &mut parameter_inlay_hints,
|
||||
// We can't warn on unknown kind here since LSPs are free to set it or not, for
|
||||
// example Rust Analyzer does not: every kind will be `None`.
|
||||
_ => &mut other_inlay_hints,
|
||||
};
|
||||
|
||||
if let Some(true) = hint.padding_left {
|
||||
padding_before_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
|
||||
}
|
||||
|
||||
inlay_hints_vec.push(InlineAnnotation::new(char_idx, label));
|
||||
|
||||
if let Some(true) = hint.padding_right {
|
||||
padding_after_inlay_hints.push(InlineAnnotation::new(char_idx, " "));
|
||||
}
|
||||
}
|
||||
|
||||
doc.set_inlay_hints(
|
||||
view_id,
|
||||
DocumentInlayHints {
|
||||
id,
|
||||
type_inlay_hints,
|
||||
parameter_inlay_hints,
|
||||
other_inlay_hints,
|
||||
padding_before_inlay_hints,
|
||||
padding_after_inlay_hints,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn register_hooks(handlers: &Handlers) {
|
||||
register_hook!(move |event: &mut DocumentDidOpen<'_>| {
|
||||
// When a document is initially opened, request inlay hints for it.
|
||||
let views: Vec<_> = event
|
||||
.editor
|
||||
.tree
|
||||
.views()
|
||||
.filter_map(|(view, _)| (view.doc == event.doc).then_some(view.id))
|
||||
.collect();
|
||||
for view in views {
|
||||
request_inlay_hints_for_view(event.editor, view, event.doc, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let tx = handlers.inlay_hints.clone();
|
||||
register_hook!(move |event: &mut DocumentDidChange<'_>| {
|
||||
// Update the inlay hint annotations' positions, helping ensure they are displayed in the
|
||||
// proper place.
|
||||
let apply_inlay_hint_changes = |annotations: &mut Vec<InlineAnnotation>| {
|
||||
event.changes.update_positions(
|
||||
annotations
|
||||
.iter_mut()
|
||||
.map(|annotation| (&mut annotation.char_idx, helix_core::Assoc::After)),
|
||||
);
|
||||
};
|
||||
|
||||
for (_view_id, text_annotation) in event.doc.inlay_hints_mut() {
|
||||
let DocumentInlayHints {
|
||||
id: _,
|
||||
type_inlay_hints,
|
||||
parameter_inlay_hints,
|
||||
other_inlay_hints,
|
||||
padding_before_inlay_hints,
|
||||
padding_after_inlay_hints,
|
||||
} = text_annotation;
|
||||
|
||||
apply_inlay_hint_changes(padding_before_inlay_hints);
|
||||
apply_inlay_hint_changes(type_inlay_hints);
|
||||
apply_inlay_hint_changes(parameter_inlay_hints);
|
||||
apply_inlay_hint_changes(other_inlay_hints);
|
||||
apply_inlay_hint_changes(padding_after_inlay_hints);
|
||||
}
|
||||
|
||||
if !event.ghost_transaction {
|
||||
if let Some(controller) = event.doc.inlay_hint_controllers.get_mut(&event.view) {
|
||||
controller.cancel();
|
||||
}
|
||||
// TODO: ideally we should only send this if the document is visible.
|
||||
send_blocking(&tx, InlayHintEvent::DocumentChanged(event.doc.id()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let tx = handlers.inlay_hints.clone();
|
||||
register_hook!(move |event: &mut SelectionDidChange<'_>| {
|
||||
if let Some(controller) = event.doc.inlay_hint_controllers.get_mut(&event.view) {
|
||||
controller.cancel();
|
||||
}
|
||||
// Ideally this would only trigger an update if the viewport changed...
|
||||
send_blocking(&tx, InlayHintEvent::ViewportScrolled(event.view));
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
register_hook!(move |event: &mut LanguageServerInitialized<'_>| {
|
||||
let views: Vec<_> = event
|
||||
.editor
|
||||
.tree
|
||||
.views()
|
||||
.map(|(view, _)| (view.id, view.doc))
|
||||
.collect();
|
||||
for (view, doc) in views {
|
||||
request_inlay_hints_for_view(event.editor, view, doc, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
register_hook!(move |event: &mut LanguageServerExited<'_>| {
|
||||
// Clear and re-request all annotations when a server exits.
|
||||
for doc in event.editor.documents_mut() {
|
||||
if doc.supports_language_server(event.server_id) {
|
||||
doc.reset_all_inlay_hints();
|
||||
}
|
||||
}
|
||||
|
||||
let views: Vec<_> = event
|
||||
.editor
|
||||
.tree
|
||||
.views()
|
||||
.map(|(view, _)| (view.id, view.doc))
|
||||
.collect();
|
||||
for (view, doc) in views {
|
||||
request_inlay_hints_for_view(event.editor, view, doc, false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
});
|
||||
}
|
|
@ -1105,12 +1105,6 @@ impl EditorView {
|
|||
}
|
||||
on_next_key
|
||||
}
|
||||
|
||||
pub fn handle_idle_timeout(&mut self, cx: &mut commands::Context) -> EventResult {
|
||||
commands::compute_inlay_hints_for_all_views(cx.editor, cx.jobs);
|
||||
|
||||
EventResult::Ignored(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl EditorView {
|
||||
|
@ -1516,7 +1510,7 @@ impl Component for EditorView {
|
|||
}
|
||||
|
||||
Event::Mouse(event) => self.handle_mouse_event(event, &mut cx),
|
||||
Event::IdleTimeout => self.handle_idle_timeout(&mut cx),
|
||||
Event::IdleTimeout => EventResult::Ignored(None),
|
||||
Event::FocusGained => {
|
||||
self.terminal_focused = true;
|
||||
EventResult::Consumed(None)
|
||||
|
|
|
@ -148,9 +148,6 @@ pub struct Document {
|
|||
/// To know if they're up-to-date, check the `id` field in `DocumentInlayHints`.
|
||||
pub(crate) inlay_hints: HashMap<ViewId, DocumentInlayHints>,
|
||||
pub(crate) jump_labels: HashMap<ViewId, Vec<Overlay>>,
|
||||
/// Set to `true` when the document is updated, reset to `false` on the next inlay hints
|
||||
/// update from the LSP
|
||||
pub inlay_hints_oudated: bool,
|
||||
|
||||
path: Option<PathBuf>,
|
||||
relative_path: OnceCell<Option<PathBuf>>,
|
||||
|
@ -207,6 +204,8 @@ pub struct Document {
|
|||
// NOTE: ideally this would live on the handler for color swatches. This is blocked on a
|
||||
// large refactor that would make `&mut Editor` available on the `DocumentDidChange` event.
|
||||
pub color_swatch_controller: TaskController,
|
||||
// NOTE: ideally this would live on the handler for inlay hints, see the comment above.
|
||||
pub inlay_hint_controllers: HashMap<ViewId, TaskController>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
@ -290,7 +289,6 @@ impl fmt::Debug for Document {
|
|||
.field("id", &self.id)
|
||||
.field("text", &self.text)
|
||||
.field("selections", &self.selections)
|
||||
.field("inlay_hints_oudated", &self.inlay_hints_oudated)
|
||||
.field("text_annotations", &self.inlay_hints)
|
||||
.field("view_data", &self.view_data)
|
||||
.field("path", &self.path)
|
||||
|
@ -693,7 +691,7 @@ impl Document {
|
|||
text,
|
||||
selections: HashMap::default(),
|
||||
inlay_hints: HashMap::default(),
|
||||
inlay_hints_oudated: false,
|
||||
inlay_hint_controllers: HashMap::default(),
|
||||
view_data: Default::default(),
|
||||
indent_style: DEFAULT_INDENT,
|
||||
editor_config: EditorConfig::default(),
|
||||
|
@ -1461,33 +1459,6 @@ impl Document {
|
|||
)
|
||||
});
|
||||
|
||||
// Update the inlay hint annotations' positions, helping ensure they are displayed in the proper place
|
||||
let apply_inlay_hint_changes = |annotations: &mut Vec<InlineAnnotation>| {
|
||||
changes.update_positions(
|
||||
annotations
|
||||
.iter_mut()
|
||||
.map(|annotation| (&mut annotation.char_idx, Assoc::After)),
|
||||
);
|
||||
};
|
||||
|
||||
self.inlay_hints_oudated = true;
|
||||
for text_annotation in self.inlay_hints.values_mut() {
|
||||
let DocumentInlayHints {
|
||||
id: _,
|
||||
type_inlay_hints,
|
||||
parameter_inlay_hints,
|
||||
other_inlay_hints,
|
||||
padding_before_inlay_hints,
|
||||
padding_after_inlay_hints,
|
||||
} = text_annotation;
|
||||
|
||||
apply_inlay_hint_changes(padding_before_inlay_hints);
|
||||
apply_inlay_hint_changes(type_inlay_hints);
|
||||
apply_inlay_hint_changes(parameter_inlay_hints);
|
||||
apply_inlay_hint_changes(other_inlay_hints);
|
||||
apply_inlay_hint_changes(padding_after_inlay_hints);
|
||||
}
|
||||
|
||||
helix_event::dispatch(DocumentDidChange {
|
||||
doc: self,
|
||||
view: view_id,
|
||||
|
@ -2242,6 +2213,12 @@ impl Document {
|
|||
self.inlay_hints.get(&view_id)
|
||||
}
|
||||
|
||||
pub fn inlay_hints_mut(&mut self) -> impl Iterator<Item = (ViewId, &mut DocumentInlayHints)> {
|
||||
self.inlay_hints
|
||||
.iter_mut()
|
||||
.map(|(view_id, hints)| (*view_id, hints))
|
||||
}
|
||||
|
||||
/// Completely removes all the inlay hints saved for the document, dropping them to free memory
|
||||
/// (since it often means inlay hints have been fully deactivated).
|
||||
pub fn reset_all_inlay_hints(&mut self) {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub struct Handlers {
|
|||
// only public because most of the actual implementation is in helix-term right now :/
|
||||
pub completions: CompletionHandler,
|
||||
pub signature_hints: Sender<lsp::SignatureHelpEvent>,
|
||||
pub inlay_hints: Sender<lsp::InlayHintEvent>,
|
||||
pub auto_save: Sender<AutoSaveEvent>,
|
||||
pub document_colors: Sender<lsp::DocumentColorsEvent>,
|
||||
}
|
||||
|
|
|
@ -5,9 +5,8 @@ use crate::editor::Action;
|
|||
use crate::events::{
|
||||
DiagnosticsDidChange, DocumentDidChange, DocumentDidClose, LanguageServerInitialized,
|
||||
};
|
||||
use crate::{DocumentId, Editor};
|
||||
use helix_core::diagnostic::DiagnosticProvider;
|
||||
use helix_core::Uri;
|
||||
use crate::{DocumentId, Editor, ViewId};
|
||||
use helix_core::{diagnostic::DiagnosticProvider, Uri};
|
||||
use helix_event::register_hook;
|
||||
use helix_lsp::util::generate_transaction_from_edits;
|
||||
use helix_lsp::{lsp, LanguageServerId, OffsetEncoding};
|
||||
|
@ -30,6 +29,16 @@ pub enum SignatureHelpEvent {
|
|||
RequestComplete { open: bool },
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InlayHintEvent {
|
||||
/// The contents of a document changed.
|
||||
/// This event should request annotations after a long debounce.
|
||||
DocumentChanged(DocumentId),
|
||||
/// The viewport was scrolled and/or the selection changed.
|
||||
/// This event should request annotations after a short debounce.
|
||||
ViewportScrolled(ViewId),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ApplyEditError {
|
||||
pub kind: ApplyEditErrorKind,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue