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| {
|
.map(|range| {
|
||||||
buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_after(range.end)
|
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)
|
.map(DocumentRange::Text)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
|
|
@ -223,7 +223,6 @@ impl TabSnapshot {
|
||||||
&'a self,
|
&'a self,
|
||||||
range: Range<TabPoint>,
|
range: Range<TabPoint>,
|
||||||
language_aware: bool,
|
language_aware: bool,
|
||||||
// TODO kb extract into one struct?
|
|
||||||
text_highlights: Option<&'a TextHighlights>,
|
text_highlights: Option<&'a TextHighlights>,
|
||||||
inlay_highlight_style: Option<HighlightStyle>,
|
inlay_highlight_style: Option<HighlightStyle>,
|
||||||
suggestion_highlight_style: Option<HighlightStyle>,
|
suggestion_highlight_style: Option<HighlightStyle>,
|
||||||
|
|
|
@ -7511,6 +7511,22 @@ impl Editor {
|
||||||
cx.notify();
|
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>(
|
pub fn clear_background_highlights<T: 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
|
@ -7932,7 +7948,6 @@ impl Editor {
|
||||||
Some(
|
Some(
|
||||||
ranges
|
ranges
|
||||||
.iter()
|
.iter()
|
||||||
// TODO kb mark inlays too
|
|
||||||
.filter_map(|range| range.as_text_range())
|
.filter_map(|range| range.as_text_range())
|
||||||
.map(move |range| {
|
.map(move |range| {
|
||||||
range.start.to_offset_utf16(&snapshot)..range.end.to_offset_utf16(&snapshot)
|
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>> {
|
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
|
||||||
let snapshot = self.buffer.read(cx).read(cx);
|
let snapshot = self.buffer.read(cx).read(cx);
|
||||||
let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
|
let range = self.text_highlights::<InputComposition>(cx)?.1.get(0)?;
|
||||||
// TODO kb mark inlays too
|
|
||||||
let range = range.as_text_range()?;
|
let range = range.as_text_range()?;
|
||||||
Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
|
Some(range.start.to_offset_utf16(&snapshot).0..range.end.to_offset_utf16(&snapshot).0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ use crate::{
|
||||||
editor_settings::ShowScrollbar,
|
editor_settings::ShowScrollbar,
|
||||||
git::{diff_hunk_to_display, DisplayDiffHunk},
|
git::{diff_hunk_to_display, DisplayDiffHunk},
|
||||||
hover_popover::{
|
hover_popover::{
|
||||||
hide_hover, hover_at, HOVER_POPOVER_GAP, MIN_POPOVER_CHARACTER_WIDTH,
|
hide_hover, hover_at, hover_at_inlay, InlayHover, HOVER_POPOVER_GAP,
|
||||||
MIN_POPOVER_LINE_HEIGHT,
|
MIN_POPOVER_CHARACTER_WIDTH, MIN_POPOVER_LINE_HEIGHT,
|
||||||
},
|
},
|
||||||
link_go_to_definition::{
|
link_go_to_definition::{
|
||||||
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
|
go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link,
|
||||||
|
@ -43,7 +43,8 @@ use language::{
|
||||||
};
|
};
|
||||||
use project::{
|
use project::{
|
||||||
project_settings::{GitGutterSetting, ProjectSettings},
|
project_settings::{GitGutterSetting, ProjectSettings},
|
||||||
InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState,
|
HoverBlock, HoverBlockKind, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip,
|
||||||
|
Location, LocationLink, ProjectPath, ResolveState,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -1860,8 +1861,7 @@ fn update_inlay_link_and_hover_points(
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
if let Some(hovered_offset) = hovered_offset {
|
if let Some(hovered_offset) = hovered_offset {
|
||||||
let buffer = editor.buffer().read(cx);
|
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||||
let snapshot = buffer.snapshot(cx);
|
|
||||||
let previous_valid_anchor = snapshot.anchor_at(
|
let previous_valid_anchor = snapshot.anchor_at(
|
||||||
point_for_position
|
point_for_position
|
||||||
.previous_valid
|
.previous_valid
|
||||||
|
@ -1885,15 +1885,14 @@ fn update_inlay_link_and_hover_points(
|
||||||
.max_by_key(|hint| hint.id)
|
.max_by_key(|hint| hint.id)
|
||||||
{
|
{
|
||||||
let inlay_hint_cache = editor.inlay_hint_cache();
|
let inlay_hint_cache = editor.inlay_hint_cache();
|
||||||
if let Some(cached_hint) =
|
let excerpt_id = previous_valid_anchor.excerpt_id;
|
||||||
inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
|
if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) {
|
||||||
{
|
|
||||||
match cached_hint.resolve_state {
|
match cached_hint.resolve_state {
|
||||||
ResolveState::CanResolve(_, _) => {
|
ResolveState::CanResolve(_, _) => {
|
||||||
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
|
if let Some(buffer_id) = previous_valid_anchor.buffer_id {
|
||||||
inlay_hint_cache.spawn_hint_resolve(
|
inlay_hint_cache.spawn_hint_resolve(
|
||||||
buffer_id,
|
buffer_id,
|
||||||
previous_valid_anchor.excerpt_id,
|
excerpt_id,
|
||||||
hovered_hint.id,
|
hovered_hint.id,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -1902,9 +1901,33 @@ fn update_inlay_link_and_hover_points(
|
||||||
ResolveState::Resolved => {
|
ResolveState::Resolved => {
|
||||||
match cached_hint.label {
|
match cached_hint.label {
|
||||||
project::InlayHintLabel::String(_) => {
|
project::InlayHintLabel::String(_) => {
|
||||||
if cached_hint.tooltip.is_some() {
|
if let Some(tooltip) = cached_hint.tooltip {
|
||||||
dbg!(&cached_hint.tooltip); // TODO kb
|
hover_at_inlay(
|
||||||
// hover_at_point = Some(hovered_offset);
|
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) => {
|
project::InlayHintLabel::LabelParts(label_parts) => {
|
||||||
|
@ -1915,15 +1938,41 @@ fn update_inlay_link_and_hover_points(
|
||||||
hovered_offset,
|
hovered_offset,
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if hovered_hint_part.tooltip.is_some() {
|
if let Some(tooltip) = hovered_hint_part.tooltip {
|
||||||
dbg!(&hovered_hint_part.tooltip); // TODO kb
|
hover_at_inlay(
|
||||||
// hover_at_point = Some(hovered_offset);
|
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(location) = hovered_hint_part.location {
|
||||||
if let Some(buffer) = cached_hint
|
if let Some(buffer) =
|
||||||
.position
|
cached_hint.position.buffer_id.and_then(|buffer_id| {
|
||||||
.buffer_id
|
editor.buffer().read(cx).buffer(buffer_id)
|
||||||
.and_then(|buffer_id| buffer.buffer(buffer_id))
|
})
|
||||||
{
|
{
|
||||||
go_to_definition_updated = true;
|
go_to_definition_updated = true;
|
||||||
update_go_to_definition_link(
|
update_go_to_definition_link(
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
display_map::ToDisplayPoint, Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings,
|
display_map::{InlayOffset, ToDisplayPoint},
|
||||||
EditorSnapshot, EditorStyle, RangeToAnchorExt,
|
link_go_to_definition::{DocumentRange, InlayRange},
|
||||||
|
Anchor, AnchorRangeExt, DisplayPoint, Editor, EditorSettings, EditorSnapshot, EditorStyle,
|
||||||
|
ExcerptId, RangeToAnchorExt,
|
||||||
};
|
};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use gpui::{
|
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.
|
/// Hides the type information popup.
|
||||||
/// Triggered by the `Hover` action when the cursor is not over a symbol or when the
|
/// Triggered by the `Hover` action when the cursor is not over a symbol or when the
|
||||||
/// selections changed.
|
/// selections changed.
|
||||||
|
@ -110,8 +190,13 @@ fn show_hover(
|
||||||
if !ignore_timeout {
|
if !ignore_timeout {
|
||||||
if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
|
if let Some(InfoPopover { symbol_range, .. }) = &editor.hover_state.info_popover {
|
||||||
if symbol_range
|
if symbol_range
|
||||||
|
.as_text_range()
|
||||||
|
.map(|range| {
|
||||||
|
range
|
||||||
.to_offset(&snapshot.buffer_snapshot)
|
.to_offset(&snapshot.buffer_snapshot)
|
||||||
.contains(&multibuffer_offset)
|
.contains(&multibuffer_offset)
|
||||||
|
})
|
||||||
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
// Hover triggered from same location as last time. Don't show again.
|
// Hover triggered from same location as last time. Don't show again.
|
||||||
return;
|
return;
|
||||||
|
@ -219,7 +304,7 @@ fn show_hover(
|
||||||
|
|
||||||
Some(InfoPopover {
|
Some(InfoPopover {
|
||||||
project: project.clone(),
|
project: project.clone(),
|
||||||
symbol_range: range,
|
symbol_range: DocumentRange::Text(range),
|
||||||
blocks: hover_result.contents,
|
blocks: hover_result.contents,
|
||||||
language: hover_result.language,
|
language: hover_result.language,
|
||||||
rendered_content: None,
|
rendered_content: None,
|
||||||
|
@ -227,10 +312,13 @@ fn show_hover(
|
||||||
});
|
});
|
||||||
|
|
||||||
this.update(&mut cx, |this, cx| {
|
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
|
// Highlight the selected symbol using a background highlight
|
||||||
this.highlight_background::<HoverState>(
|
this.highlight_background::<HoverState>(
|
||||||
vec![hover_popover.symbol_range.clone()],
|
vec![symbol_range],
|
||||||
|theme| theme.editor.hover_popover.highlight,
|
|theme| theme.editor.hover_popover.highlight,
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
@ -497,7 +585,10 @@ impl HoverState {
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.info_popover
|
self.info_popover
|
||||||
.as_ref()
|
.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);
|
let point = anchor.to_display_point(&snapshot.display_snapshot);
|
||||||
|
|
||||||
|
@ -522,7 +613,7 @@ impl HoverState {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InfoPopover {
|
pub struct InfoPopover {
|
||||||
pub project: ModelHandle<Project>,
|
pub project: ModelHandle<Project>,
|
||||||
pub symbol_range: Range<Anchor>,
|
symbol_range: DocumentRange,
|
||||||
pub blocks: Vec<HoverBlock>,
|
pub blocks: Vec<HoverBlock>,
|
||||||
language: Option<Arc<Language>>,
|
language: Option<Arc<Language>>,
|
||||||
rendered_content: Option<RenderedInfo>,
|
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(
|
pub fn project_to_lsp_hint(
|
||||||
hint: InlayHint,
|
hint: InlayHint,
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue