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,
|
StyleRefinement, Styled, Task, TextStyleRefinement, View, ViewContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::{Diagnostic, DiagnosticEntry, Language, LanguageRegistry};
|
use language::{DiagnosticEntry, Language, LanguageRegistry};
|
||||||
use lsp::DiagnosticSeverity;
|
use lsp::DiagnosticSeverity;
|
||||||
use markdown::{Markdown, MarkdownStyle};
|
use markdown::{Markdown, MarkdownStyle};
|
||||||
use multi_buffer::ToOffset;
|
use multi_buffer::ToOffset;
|
||||||
use project::{HoverBlock, InlayHintLabelPart};
|
use project::{HoverBlock, HoverBlockKind, InlayHintLabelPart};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::{borrow::Cow, cell::RefCell};
|
use std::{borrow::Cow, cell::RefCell};
|
||||||
|
@ -263,50 +263,14 @@ fn show_hover(
|
||||||
delay.await;
|
delay.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
let local_diagnostic = if let Some(invisible) = snapshot
|
let local_diagnostic = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.chars_at(anchor)
|
.diagnostics_in_range(anchor..anchor, false)
|
||||||
.next()
|
// Find the entry with the most specific range
|
||||||
.filter(|&c| is_invisible(c))
|
.min_by_key(|entry| {
|
||||||
{
|
let range = entry.range.to_offset(&snapshot.buffer_snapshot);
|
||||||
let after = snapshot.buffer_snapshot.anchor_after(
|
range.end - range.start
|
||||||
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
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
|
let diagnostic_popover = if let Some(local_diagnostic) = local_diagnostic {
|
||||||
let text = match local_diagnostic.diagnostic.source {
|
let text = match local_diagnostic.diagnostic.source {
|
||||||
|
@ -389,6 +353,31 @@ fn show_hover(
|
||||||
this.hover_state.diagnostic_popover = diagnostic_popover;
|
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 {
|
let hovers_response = if let Some(hover_request) = hover_request {
|
||||||
hover_request.await
|
hover_request.await
|
||||||
} else {
|
} else {
|
||||||
|
@ -396,8 +385,26 @@ fn show_hover(
|
||||||
};
|
};
|
||||||
let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
|
let snapshot = this.update(&mut cx, |this, cx| this.snapshot(cx))?;
|
||||||
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
|
let mut hover_highlights = Vec::with_capacity(hovers_response.len());
|
||||||
let mut info_popovers = Vec::with_capacity(hovers_response.len());
|
let mut info_popovers = Vec::with_capacity(
|
||||||
let mut info_popover_tasks = Vec::with_capacity(hovers_response.len());
|
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 {
|
for hover_result in hovers_response {
|
||||||
// Create symbol range of anchors for highlighting and filtering of future requests.
|
// Create symbol range of anchors for highlighting and filtering of future requests.
|
||||||
|
@ -410,7 +417,6 @@ fn show_hover(
|
||||||
let end = snapshot
|
let end = snapshot
|
||||||
.buffer_snapshot
|
.buffer_snapshot
|
||||||
.anchor_in_excerpt(excerpt_id, range.end)?;
|
.anchor_in_excerpt(excerpt_id, range.end)?;
|
||||||
|
|
||||||
Some(start..end)
|
Some(start..end)
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
@ -428,21 +434,15 @@ fn show_hover(
|
||||||
let parsed_content =
|
let parsed_content =
|
||||||
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
|
parse_blocks(&blocks, &language_registry, language, &mut cx).await;
|
||||||
let scroll_handle = ScrollHandle::new();
|
let scroll_handle = ScrollHandle::new();
|
||||||
info_popover_tasks.push((
|
hover_highlights.push(range.clone());
|
||||||
range.clone(),
|
info_popovers.push(InfoPopover {
|
||||||
InfoPopover {
|
symbol_range: RangeInEditor::Text(range),
|
||||||
symbol_range: RangeInEditor::Text(range),
|
parsed_content,
|
||||||
parsed_content,
|
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
scroll_handle,
|
||||||
scroll_handle,
|
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
||||||
keyboard_grace: Rc::new(RefCell::new(ignore_timeout)),
|
anchor: Some(anchor),
|
||||||
anchor: Some(anchor),
|
});
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
for (highlight_range, info_popover) in info_popover_tasks {
|
|
||||||
hover_highlights.push(highlight_range);
|
|
||||||
info_popovers.push(info_popover);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.update(&mut cx, |editor, cx| {
|
this.update(&mut cx, |editor, cx| {
|
||||||
|
@ -732,6 +732,7 @@ impl InfoPopover {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
self.scroll_handle.set_offset(current);
|
self.scroll_handle.set_offset(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_vertical_scrollbar(&self, cx: &mut ViewContext<Editor>) -> Stateful<Div> {
|
fn render_vertical_scrollbar(&self, cx: &mut ViewContext<Editor>) -> Stateful<Div> {
|
||||||
div()
|
div()
|
||||||
.occlude()
|
.occlude()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue