Draft inlay hint part hover detection

This commit is contained in:
Kirill Bulatov 2023-08-16 14:21:26 +03:00
parent bc7e9088fe
commit d34491e822
3 changed files with 125 additions and 27 deletions

View File

@ -27,7 +27,7 @@ pub use block_map::{
BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
};
pub use self::inlay_map::Inlay;
pub use self::inlay_map::{Inlay, InlayOffset, InlayPoint};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FoldStatus {
@ -387,12 +387,25 @@ impl DisplaySnapshot {
}
fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
self.inlay_snapshot
.to_buffer_point(self.display_point_to_inlay_point(point, bias))
}
pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
self.inlay_snapshot
.to_offset(self.display_point_to_inlay_point(point, bias))
}
pub fn inlay_point_to_inlay_offset(&self, point: InlayPoint) -> InlayOffset {
self.inlay_snapshot.to_offset(point)
}
fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
let block_point = point.0;
let wrap_point = self.block_snapshot.to_wrap_point(block_point);
let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
self.inlay_snapshot.to_buffer_point(inlay_point)
fold_point.to_inlay_point(&self.fold_snapshot)
}
pub fn max_point(&self) -> DisplayPoint {

View File

@ -4,7 +4,7 @@ use super::{
MAX_LINE_LEN,
};
use crate::{
display_map::{BlockStyle, DisplaySnapshot, FoldStatus, TransformBlock},
display_map::{BlockStyle, DisplaySnapshot, FoldStatus, InlayPoint, TransformBlock},
editor_settings::ShowScrollbar,
git::{diff_hunk_to_display, DisplayDiffHunk},
hover_popover::{
@ -42,7 +42,7 @@ use language::{
};
use project::{
project_settings::{GitGutterSetting, ProjectSettings},
ProjectPath,
InlayHintLabelPart, ProjectPath,
};
use smallvec::SmallVec;
use std::{
@ -455,11 +455,67 @@ impl EditorElement {
) -> bool {
// This will be handled more correctly once https://github.com/zed-industries/zed/issues/1218 is completed
// Don't trigger hover popover if mouse is hovering over context menu
let point = position_to_display_point(position, text_bounds, position_map);
update_go_to_definition_link(editor, point, cmd, shift, cx);
hover_at(editor, point, cx);
if text_bounds.contains_point(position) {
let (nearest_valid_position, unclipped_position) =
position_map.point_for_position(text_bounds, position);
if nearest_valid_position == unclipped_position {
update_go_to_definition_link(editor, Some(nearest_valid_position), cmd, shift, cx);
hover_at(editor, Some(nearest_valid_position), cx);
return true;
} else {
let buffer = editor.buffer().read(cx);
let snapshot = buffer.snapshot(cx);
let previous_valid_position = position_map
.snapshot
.clip_point(unclipped_position, Bias::Left)
.to_point(&position_map.snapshot.display_snapshot);
let previous_valid_anchor = snapshot.anchor_at(previous_valid_position, Bias::Left);
let next_valid_position = position_map
.snapshot
.clip_point(unclipped_position, Bias::Right)
.to_point(&position_map.snapshot.display_snapshot);
let next_valid_anchor = snapshot.anchor_at(next_valid_position, Bias::Right);
if let Some(hovered_hint) = editor
.visible_inlay_hints(cx)
.into_iter()
.skip_while(|hint| hint.position.cmp(&previous_valid_anchor, &snapshot).is_lt())
.take_while(|hint| hint.position.cmp(&next_valid_anchor, &snapshot).is_le())
.max_by_key(|hint| hint.id)
{
if let Some(cached_hint) = editor
.inlay_hint_cache()
.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id)
{
match &cached_hint.label {
project::InlayHintLabel::String(regular_label) => {
// TODO kb remove + check for tooltip for hover and resolve, if needed
eprintln!("regular string: {regular_label}");
}
project::InlayHintLabel::LabelParts(label_parts) => {
// TODO kb how to properly convert it?
let unclipped_inlay_position = InlayPoint::new(
unclipped_position.row(),
unclipped_position.column(),
);
if let Some(hovered_hint_part) = find_hovered_hint_part(
&position_map.snapshot,
&label_parts,
previous_valid_position,
next_valid_position,
unclipped_inlay_position,
) {
// TODO kb remove + check for tooltip and location and resolve, if needed
eprintln!("hint_part: {hovered_hint_part:?}");
}
}
};
}
}
}
};
update_go_to_definition_link(editor, None, cmd, shift, cx);
hover_at(editor, None, cx);
true
}
@ -909,7 +965,7 @@ impl EditorElement {
&text,
cursor_row_layout.font_size(),
&[(
text.len(),
text.chars().count(),
RunStyle {
font_id,
color: style.background,
@ -1804,6 +1860,40 @@ impl EditorElement {
}
}
fn find_hovered_hint_part<'a>(
snapshot: &EditorSnapshot,
label_parts: &'a [InlayHintLabelPart],
hint_start: Point,
hint_end: Point,
hovered_position: InlayPoint,
) -> Option<&'a InlayHintLabelPart> {
let hint_start_offset =
snapshot.display_point_to_inlay_offset(hint_start.to_display_point(&snapshot), Bias::Left);
let hint_end_offset =
snapshot.display_point_to_inlay_offset(hint_end.to_display_point(&snapshot), Bias::Right);
dbg!((
"~~~~~~~~~",
hint_start,
hint_start_offset,
hint_end,
hint_end_offset,
hovered_position
));
let hovered_offset = snapshot.inlay_point_to_inlay_offset(hovered_position);
if hovered_offset >= hint_start_offset && hovered_offset <= hint_end_offset {
let mut hovered_character = (hovered_offset - hint_start_offset).0;
for part in label_parts {
let part_len = part.value.chars().count();
if hovered_character >= part_len {
hovered_character -= part_len;
} else {
return Some(part);
}
}
}
None
}
struct HighlightedChunk<'a> {
chunk: &'a str,
style: Option<HighlightStyle>,
@ -2663,6 +2753,7 @@ impl PositionMap {
let mut target_point = DisplayPoint::new(row, column);
let point = self.snapshot.clip_point(target_point, Bias::Left);
// TODO kb looks wrong, need to construct inlay point instead? operate offsets?
*target_point.column_mut() += (x_overshoot / self.em_advance) as u32;
(point, target_point)
@ -2919,23 +3010,6 @@ impl HighlightedRange {
}
}
fn position_to_display_point(
position: Vector2F,
text_bounds: RectF,
position_map: &PositionMap,
) -> Option<DisplayPoint> {
if text_bounds.contains_point(position) {
let (point, target_point) = position_map.point_for_position(text_bounds, position);
if point == target_point {
Some(point)
} else {
None
}
} else {
None
}
}
fn range_to_bounds(
range: &Range<DisplayPoint>,
content_origin: Vector2F,

View File

@ -386,6 +386,17 @@ impl InlayHintCache {
self.hints.clear();
}
pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option<InlayHint> {
self.hints
.get(&excerpt_id)?
.read()
.hints
.iter()
.find(|&(id, _)| id == &hint_id)
.map(|(_, hint)| hint)
.cloned()
}
pub fn hints(&self) -> Vec<InlayHint> {
let mut hints = Vec::new();
for excerpt_hints in self.hints.values() {