Use info popovers instead of diagnostic for invisible char hover (#22701)
This will allow the diagnostic popover to be displayed even if hovering an invisible char. Beyond that, it solves a rare `DiagnosticPopover` corner case: * Supports moving the selection to a diagnostic when `GoToDiagnostic` is done while hovering, based on `group_id` * Provides Diagnostic values with `group_id: 0` providing information on hover about invisible characters. So, `GoToDiagnostic` would navigate to the very first error produced by a language server. Really not a big deal of course. Release Notes: - N/A
This commit is contained in:
parent
d02bfe1e95
commit
2dec4c2c91
1 changed files with 64 additions and 63 deletions
|
@ -11,11 +11,11 @@ use gpui::{
|
|||
StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{Diagnostic, DiagnosticEntry, Language, LanguageRegistry};
|
||||
use language::{DiagnosticEntry, Language, LanguageRegistry};
|
||||
use lsp::DiagnosticSeverity;
|
||||
use markdown::{Markdown, MarkdownStyle};
|
||||
use multi_buffer::ToOffset;
|
||||
use project::{HoverBlock, InlayHintLabelPart};
|
||||
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
|
||||
use settings::Settings;
|
||||
use std::rc::Rc;
|
||||
use std::{borrow::Cow, cell::RefCell};
|
||||
|
@ -263,50 +263,14 @@ fn show_hover(
|
|||
delay.await;
|
||||
}
|
||||
|
||||
let local_diagnostic = if let Some(invisible) = snapshot
|
||||
let local_diagnostic = snapshot
|
||||
.buffer_snapshot
|
||||
.chars_at(anchor)
|
||||
.next()
|
||||
.filter(|&c| is_invisible(c))
|
||||
{
|
||||
let after = snapshot.buffer_snapshot.anchor_after(
|
||||
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
|
||||
);
|
||||
Some(DiagnosticEntry {
|
||||
diagnostic: Diagnostic {
|
||||
severity: DiagnosticSeverity::HINT,
|
||||
message: format!("Unicode character U+{:02X}", invisible as u32),
|
||||
..Default::default()
|
||||
},
|
||||
range: anchor..after,
|
||||
})
|
||||
} else if let Some(invisible) = snapshot
|
||||
.buffer_snapshot
|
||||
.reversed_chars_at(anchor)
|
||||
.next()
|
||||
.filter(|&c| is_invisible(c))
|
||||
{
|
||||
let before = snapshot.buffer_snapshot.anchor_before(
|
||||
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
|
||||
);
|
||||
Some(DiagnosticEntry {
|
||||
diagnostic: Diagnostic {
|
||||
severity: DiagnosticSeverity::HINT,
|
||||
message: format!("Unicode character U+{:02X}", invisible as u32),
|
||||
..Default::default()
|
||||
},
|
||||
range: before..anchor,
|
||||
})
|
||||
} else {
|
||||
snapshot
|
||||
.buffer_snapshot
|
||||
.diagnostics_in_range(anchor..anchor, false)
|
||||
// Find the entry with the most specific range
|
||||
.min_by_key(|entry| {
|
||||
let range = entry.range.to_offset(&snapshot.buffer_snapshot);
|
||||
range.end - range.start
|
||||
})
|
||||
};
|
||||
.diagnostics_in_range(anchor..anchor, false)
|
||||
// Find the entry with the most specific range
|
||||
.min_by_key(|entry| {
|
||||
let range = entry.range.to_offset(&snapshot.buffer_snapshot);
|
||||
range.end - range.start
|
||||
});
|
||||
|
||||
let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
|
||||
let text = match local_diagnostic.diagnostic.source {
|
||||
|
@ -389,6 +353,31 @@ fn show_hover(
|
|||
this.hover_state.diagnostic_popover = diagnostic_popover;
|
||||
})?;
|
||||
|
||||
let invisible_char = if let Some(invisible) = snapshot
|
||||
.buffer_snapshot
|
||||
.chars_at(anchor)
|
||||
.next()
|
||||
.filter(|&c| is_invisible(c))
|
||||
{
|
||||
let after = snapshot.buffer_snapshot.anchor_after(
|
||||
anchor.to_offset(&snapshot.buffer_snapshot) + invisible.len_utf8(),
|
||||
);
|
||||
Some((invisible, anchor..after))
|
||||
} else if let Some(invisible) = snapshot
|
||||
.buffer_snapshot
|
||||
.reversed_chars_at(anchor)
|
||||
.next()
|
||||
.filter(|&c| is_invisible(c))
|
||||
{
|
||||
let before = snapshot.buffer_snapshot.anchor_before(
|
||||
anchor.to_offset(&snapshot.buffer_snapshot) - invisible.len_utf8(),
|
||||
);
|
||||
|
||||
Some((invisible, before..anchor))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let hovers_response = if let Some(hover_request) = hover_request {
|
||||
hover_request.await
|
||||
} else {
|
||||
|
@ -396,8 +385,26 @@ fn show_hover(
|
|||
};
|
||||
let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
|
||||
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
|
||||
let mut info_popovers = Vec::with_capacity(hovers_response.len());
|
||||
let mut info_popover_tasks = Vec::with_capacity(hovers_response.len());
|
||||
let mut info_popovers = Vec::with_capacity(
|
||||
hovers_response.len() + if invisible_char.is_some() { 1 } else { 0 },
|
||||
);
|
||||
|
||||
if let Some((invisible, range)) = invisible_char {
|
||||
let blocks = vec![HoverBlock {
|
||||
text: format!("Unicode character U+{:02X}", invisible as u32),
|
||||
kind: HoverBlockKind::PlainText,
|
||||
}];
|
||||
let parsed_content = parse_blocks(&blocks, &language_registry, None, &mut cx).await;
|
||||
let scroll_handle = ScrollHandle::new();
|
||||
info_popovers.push(InfoPopover {
|
||||
symbol_range: RangeInEditor::Text(range),
|
||||
parsed_content,
|
||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||
scroll_handle,
|
||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||
anchor: Some(anchor),
|
||||
})
|
||||
}
|
||||
|
||||
for hover_result in hovers_response {
|
||||
// Create symbol range of anchors for highlighting and filtering of future requests.
|
||||
|
@ -410,7 +417,6 @@ fn show_hover(
|
|||
let end = snapshot
|
||||
.buffer_snapshot
|
||||
.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||
|
||||
Some(start..end)
|
||||
})
|
||||
.or_else(|| {
|
||||
|
@ -428,21 +434,15 @@ fn show_hover(
|
|||
let parsed_content =
|
||||
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
|
||||
let scroll_handle = ScrollHandle::new();
|
||||
info_popover_tasks.push((
|
||||
range.clone(),
|
||||
InfoPopover {
|
||||
symbol_range: RangeInEditor::Text(range),
|
||||
parsed_content,
|
||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||
scroll_handle,
|
||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||
anchor: Some(anchor),
|
||||
},
|
||||
));
|
||||
}
|
||||
for (highlight_range, info_popover) in info_popover_tasks {
|
||||
hover_highlights.push(highlight_range);
|
||||
info_popovers.push(info_popover);
|
||||
hover_highlights.push(range.clone());
|
||||
info_popovers.push(InfoPopover {
|
||||
symbol_range: RangeInEditor::Text(range),
|
||||
parsed_content,
|
||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||
scroll_handle,
|
||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||
anchor: Some(anchor),
|
||||
});
|
||||
}
|
||||
|
||||
this.update(&mut cx, |editor, cx| {
|
||||
|
@ -732,6 +732,7 @@ impl InfoPopover {
|
|||
cx.notify();
|
||||
self.scroll_handle.set_offset(current);
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut ViewContext<Editor>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue