Propagate inlay background highlights to data storage
This commit is contained in:
parent
4b78678923
commit
bcaff0a18a
6 changed files with 185 additions and 33 deletions
|
@ -1596,7 +1596,7 @@ mod tests {
|
|||
.map(|range| {
|
||||
buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_after(range.end)
|
||||
})
|
||||
// TODO add inlay highlight tests
|
||||
// TODO kb add inlay highlight tests
|
||||
.map(DocumentRange::Text)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
|
|
@ -223,7 +223,6 @@ impl TabSnapshot {
|
|||
&'a self,
|
||||
range: Range<TabPoint>,
|
||||
language_aware: bool,
|
||||
// TODO kb extract into one struct?
|
||||
text_highlights: Option<&'a TextHighlights>,
|
||||
inlay_highlight_style: Option<HighlightStyle>,
|
||||
suggestion_highlight_style: Option<HighlightStyle>,
|
||||
|
|
|
@ -7511,6 +7511,22 @@ impl Editor {
|
|||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn highlight_inlay_background<T: 'static>(
|
||||
&mut self,
|
||||
ranges: Vec<InlayRange>,
|
||||
color_fetcher: fn(&Theme) -> Color,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.background_highlights.insert(
|
||||
TypeId::of::<T>(),
|
||||
(
|
||||
color_fetcher,
|
||||
ranges.into_iter().map(DocumentRange::Inlay).collect(),
|
||||
),
|
||||
);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
pub fn clear_background_highlights<T: 'static>(
|
||||
&mut self,
|
||||
cx: &mut ViewContext<Self>,
|
||||
|
@ -7932,7 +7948,6 @@ impl Editor {
|
|||
Some(
|
||||
ranges
|
||||
.iter()
|
||||
// TODO kb mark inlays too
|
||||
.filter_map(|range| range.as_text_range())
|
||||
.map(move |range| {
|
||||
range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
|
||||
|
@ -8400,7 +8415,6 @@ impl View for Editor {
|
|||
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
|
||||
let snapshot = self.buffer.read(cx).read(cx);
|
||||
let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
|
||||
// TODO kb mark inlays too
|
||||
let range = range.as_text_range()?;
|
||||
Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
|
||||
}
|
||||
|
|
|
@ -8,8 +8,8 @@ use crate::{
|
|||
editor_settings::ShowScrollbar,
|
||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||
hover_popover::{
|
||||
hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
|
||||
MIN_POPOVER_LINE_HEIGHT,
|
||||
hide_hover, hover_at, hover_at_inlay, InlayHover, HOVER_POPOVER_GAP,
|
||||
MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
||||
},
|
||||
link_go_to_definition::{
|
||||
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
|
||||
|
@ -43,7 +43,8 @@ use language::{
|
|||
};
|
||||
use project::{
|
||||
project_settings::{GitGutterSetting, ProjectSettings},
|
||||
InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState,
|
||||
HoverBlock, HoverBlockKind, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip,
|
||||
Location, LocationLink, ProjectPath, ResolveState,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
|
@ -1860,8 +1861,7 @@ fn update_inlay_link_and_hover_points(
|
|||
None
|
||||
};
|
||||
if let Some(hovered_offset) = hovered_offset {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let previous_valid_anchor = snapshot.anchor_at(
|
||||
point_for_position
|
||||
.previous_valid
|
||||
|
@ -1885,15 +1885,14 @@ fn update_inlay_link_and_hover_points(
|
|||
.max_by_key(|hint| hint.id)
|
||||
{
|
||||
let inlay_hint_cache = editor.inlay_hint_cache();
|
||||
if let Some(cached_hint) =
|
||||
inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
|
||||
{
|
||||
let excerpt_id = previous_valid_anchor.excerpt_id;
|
||||
if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
|
||||
match cached_hint.resolve_state {
|
||||
ResolveState::CanResolve(_, _) => {
|
||||
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
|
||||
inlay_hint_cache.spawn_hint_resolve(
|
||||
buffer_id,
|
||||
previous_valid_anchor.excerpt_id,
|
||||
excerpt_id,
|
||||
hovered_hint.id,
|
||||
cx,
|
||||
);
|
||||
|
@ -1902,9 +1901,33 @@ fn update_inlay_link_and_hover_points(
|
|||
ResolveState::Resolved => {
|
||||
match cached_hint.label {
|
||||
project::InlayHintLabel::String(_) => {
|
||||
if cached_hint.tooltip.is_some() {
|
||||
dbg!(&cached_hint.tooltip); // TODO kb
|
||||
// hover_at_point = Some(hovered_offset);
|
||||
if let Some(tooltip) = cached_hint.tooltip {
|
||||
hover_at_inlay(
|
||||
editor,
|
||||
InlayHover {
|
||||
excerpt: excerpt_id,
|
||||
tooltip: match tooltip {
|
||||
InlayHintTooltip::String(text) => HoverBlock {
|
||||
text,
|
||||
kind: HoverBlockKind::PlainText,
|
||||
},
|
||||
InlayHintTooltip::MarkupContent(content) => {
|
||||
HoverBlock {
|
||||
text: content.value,
|
||||
kind: content.kind,
|
||||
}
|
||||
}
|
||||
},
|
||||
triggered_from: hovered_offset,
|
||||
range: InlayRange {
|
||||
inlay_position: hovered_hint.position,
|
||||
highlight_start: hint_start_offset,
|
||||
highlight_end: hint_end_offset,
|
||||
},
|
||||
},
|
||||
cx,
|
||||
);
|
||||
hover_updated = true;
|
||||
}
|
||||
}
|
||||
project::InlayHintLabel::LabelParts(label_parts) => {
|
||||
|
@ -1915,15 +1938,41 @@ fn update_inlay_link_and_hover_points(
|
|||
hovered_offset,
|
||||
)
|
||||
{
|
||||
if hovered_hint_part.tooltip.is_some() {
|
||||
dbg!(&hovered_hint_part.tooltip); // TODO kb
|
||||
// hover_at_point = Some(hovered_offset);
|
||||
if let Some(tooltip) = hovered_hint_part.tooltip {
|
||||
hover_at_inlay(
|
||||
editor,
|
||||
InlayHover {
|
||||
excerpt: excerpt_id,
|
||||
tooltip: match tooltip {
|
||||
InlayHintLabelPartTooltip::String(text) => {
|
||||
HoverBlock {
|
||||
text,
|
||||
kind: HoverBlockKind::PlainText,
|
||||
}
|
||||
}
|
||||
InlayHintLabelPartTooltip::MarkupContent(
|
||||
content,
|
||||
) => HoverBlock {
|
||||
text: content.value,
|
||||
kind: content.kind,
|
||||
},
|
||||
},
|
||||
triggered_from: hovered_offset,
|
||||
range: InlayRange {
|
||||
inlay_position: hovered_hint.position,
|
||||
highlight_start: part_range.start,
|
||||
highlight_end: part_range.end,
|
||||
},
|
||||
},
|
||||
cx,
|
||||
);
|
||||
hover_updated = true;
|
||||
}
|
||||
if let Some(location) = hovered_hint_part.location {
|
||||
if let Some(buffer) = cached_hint
|
||||
.position
|
||||
.buffer_id
|
||||
.and_then(|buffer_id| buffer.buffer(buffer_id))
|
||||
if let Some(buffer) =
|
||||
cached_hint.position.buffer_id.and_then(|buffer_id| {
|
||||
editor.buffer().read(cx).buffer(buffer_id)
|
||||
})
|
||||
{
|
||||
go_to_definition_updated = true;
|
||||
update_go_to_definition_link(
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{
|
||||
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
|
||||
EditorSnapshot, EditorStyle, RangeToAnchorExt,
|
||||
display_map::{InlayOffset, ToDisplayPoint},
|
||||
link_go_to_definition::{DocumentRange, InlayRange},
|
||||
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
||||
ExcerptId, RangeToAnchorExt,
|
||||
};
|
||||
use futures::FutureExt;
|
||||
use gpui::{
|
||||
|
@ -46,6 +48,84 @@ pub fn hover_at(editor: &mut Editor, point: Option<DisplayPoint>, cx: &mut ViewC
|
|||
}
|
||||
}
|
||||
|
||||
pub struct InlayHover {
|
||||
pub excerpt: ExcerptId,
|
||||
pub triggered_from: InlayOffset,
|
||||
pub range: InlayRange,
|
||||
pub tooltip: HoverBlock,
|
||||
}
|
||||
|
||||
pub fn hover_at_inlay(editor: &mut Editor, inlay_hover: InlayHover, cx: &mut ViewContext<Editor>) {
|
||||
if settings::get::<EditorSettings>(cx).hover_popover_enabled {
|
||||
if editor.pending_rename.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(project) = editor.project.clone() else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
|
||||
if let DocumentRange::Inlay(range) = symbol_range {
|
||||
if (range.highlight_start..=range.highlight_end)
|
||||
.contains(&inlay_hover.triggered_from)
|
||||
{
|
||||
// Hover triggered from same location as last time. Don't show again.
|
||||
return;
|
||||
}
|
||||
}
|
||||
hide_hover(editor, cx);
|
||||
}
|
||||
|
||||
let snapshot = editor.snapshot(cx);
|
||||
// Don't request again if the location is the same as the previous request
|
||||
if let Some(triggered_from) = editor.hover_state.triggered_from {
|
||||
if inlay_hover.triggered_from
|
||||
== snapshot
|
||||
.display_snapshot
|
||||
.anchor_to_inlay_offset(triggered_from)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let task = cx.spawn(|this, mut cx| {
|
||||
async move {
|
||||
cx.background()
|
||||
.timer(Duration::from_millis(HOVER_DELAY_MILLIS))
|
||||
.await;
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.hover_state.diagnostic_popover = None;
|
||||
})?;
|
||||
|
||||
let hover_popover = InfoPopover {
|
||||
project: project.clone(),
|
||||
symbol_range: DocumentRange::Inlay(inlay_hover.range),
|
||||
blocks: vec![inlay_hover.tooltip],
|
||||
language: None,
|
||||
rendered_content: None,
|
||||
};
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
// Highlight the selected symbol using a background highlight
|
||||
this.highlight_inlay_background::<HoverState>(
|
||||
vec![inlay_hover.range],
|
||||
|theme| theme.editor.hover_popover.highlight,
|
||||
cx,
|
||||
);
|
||||
this.hover_state.info_popover = Some(hover_popover);
|
||||
cx.notify();
|
||||
})?;
|
||||
|
||||
anyhow::Ok(())
|
||||
}
|
||||
.log_err()
|
||||
});
|
||||
|
||||
editor.hover_state.info_task = Some(task);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hides the type information popup.
|
||||
/// Triggered by the `Hover` action when the cursor is not over a symbol or when the
|
||||
/// selections changed.
|
||||
|
@ -110,8 +190,13 @@ fn show_hover(
|
|||
if !ignore_timeout {
|
||||
if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
|
||||
if symbol_range
|
||||
.to_offset(&snapshot.buffer_snapshot)
|
||||
.contains(&multibuffer_offset)
|
||||
.as_text_range()
|
||||
.map(|range| {
|
||||
range
|
||||
.to_offset(&snapshot.buffer_snapshot)
|
||||
.contains(&multibuffer_offset)
|
||||
})
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// Hover triggered from same location as last time. Don't show again.
|
||||
return;
|
||||
|
@ -219,7 +304,7 @@ fn show_hover(
|
|||
|
||||
Some(InfoPopover {
|
||||
project: project.clone(),
|
||||
symbol_range: range,
|
||||
symbol_range: DocumentRange::Text(range),
|
||||
blocks: hover_result.contents,
|
||||
language: hover_result.language,
|
||||
rendered_content: None,
|
||||
|
@ -227,10 +312,13 @@ fn show_hover(
|
|||
});
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
if let Some(hover_popover) = hover_popover.as_ref() {
|
||||
if let Some(symbol_range) = hover_popover
|
||||
.as_ref()
|
||||
.and_then(|hover_popover| hover_popover.symbol_range.as_text_range())
|
||||
{
|
||||
// Highlight the selected symbol using a background highlight
|
||||
this.highlight_background::<HoverState>(
|
||||
vec![hover_popover.symbol_range.clone()],
|
||||
vec![symbol_range],
|
||||
|theme| theme.editor.hover_popover.highlight,
|
||||
cx,
|
||||
);
|
||||
|
@ -497,7 +585,10 @@ impl HoverState {
|
|||
.or_else(|| {
|
||||
self.info_popover
|
||||
.as_ref()
|
||||
.map(|info_popover| &info_popover.symbol_range.start)
|
||||
.map(|info_popover| match &info_popover.symbol_range {
|
||||
DocumentRange::Text(range) => &range.start,
|
||||
DocumentRange::Inlay(range) => &range.inlay_position,
|
||||
})
|
||||
})?;
|
||||
let point = anchor.to_display_point(&snapshot.display_snapshot);
|
||||
|
||||
|
@ -522,7 +613,7 @@ impl HoverState {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct InfoPopover {
|
||||
pub project: ModelHandle<Project>,
|
||||
pub symbol_range: Range<Anchor>,
|
||||
symbol_range: DocumentRange,
|
||||
pub blocks: Vec<HoverBlock>,
|
||||
language: Option<Arc<Language>>,
|
||||
rendered_content: Option<RenderedInfo>,
|
||||
|
|
|
@ -2126,7 +2126,6 @@ impl InlayHints {
|
|||
})
|
||||
}
|
||||
|
||||
// TODO kb instead, store all LSP data inside the project::InlayHint?
|
||||
pub fn project_to_lsp_hint(
|
||||
hint: InlayHint,
|
||||
project: &ModelHandle<Project>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue