Pass inlay go to definition data

This commit is contained in:
Kirill Bulatov 2023-08-21 17:54:34 +03:00
parent ac86bbac75
commit 7eab18ec89
4 changed files with 229 additions and 84 deletions

View file

@ -64,9 +64,7 @@ use language::{
Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt, Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
OffsetUtf16, Point, Selection, SelectionGoal, TransactionId, OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
}; };
use link_go_to_definition::{ use link_go_to_definition::{hide_link_definition, show_link_definition, LinkGoToDefinitionState};
hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState,
};
use log::error; use log::error;
use multi_buffer::ToOffsetUtf16; use multi_buffer::ToOffsetUtf16;
pub use multi_buffer::{ pub use multi_buffer::{
@ -8307,14 +8305,11 @@ impl View for Editor {
) -> bool { ) -> bool {
let pending_selection = self.has_pending_selection(); let pending_selection = self.has_pending_selection();
if let Some(point) = self.link_go_to_definition_state.last_mouse_location.clone() { if let Some(point) = &self.link_go_to_definition_state.last_trigger_point {
if event.cmd && !pending_selection { if event.cmd && !pending_selection {
let point = point.clone();
let snapshot = self.snapshot(cx); let snapshot = self.snapshot(cx);
let kind = if event.shift { let kind = point.definition_kind(event.shift);
LinkDefinitionKind::Type
} else {
LinkDefinitionKind::Symbol
};
show_link_definition(kind, self, point, snapshot, cx); show_link_definition(kind, self, point, snapshot, cx);
return false; return false;

View file

@ -13,6 +13,7 @@ use crate::{
}, },
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,
GoToDefinitionTrigger,
}, },
mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt, mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt,
}; };
@ -42,7 +43,7 @@ use language::{
}; };
use project::{ use project::{
project_settings::{GitGutterSetting, ProjectSettings}, project_settings::{GitGutterSetting, ProjectSettings},
InlayHintLabelPart, ProjectPath, ResolveState, InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState,
}; };
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
@ -395,7 +396,15 @@ impl EditorElement {
None None
}; };
update_go_to_definition_link(editor, point, cmd, shift, cx); update_go_to_definition_link(
editor,
point
.map(GoToDefinitionTrigger::Text)
.unwrap_or(GoToDefinitionTrigger::None),
cmd,
shift,
cx,
);
if editor.has_pending_selection() { if editor.has_pending_selection() {
let mut scroll_delta = Vector2F::zero(); let mut scroll_delta = Vector2F::zero();
@ -460,7 +469,13 @@ impl EditorElement {
let point_for_position = position_map.point_for_position(text_bounds, position); let point_for_position = position_map.point_for_position(text_bounds, position);
match point_for_position.as_valid() { match point_for_position.as_valid() {
Some(point) => { Some(point) => {
update_go_to_definition_link(editor, Some(point), cmd, shift, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(point),
cmd,
shift,
cx,
);
hover_at(editor, Some(point), cx); hover_at(editor, Some(point), cx);
} }
None => { None => {
@ -468,12 +483,13 @@ impl EditorElement {
position_map, position_map,
point_for_position, point_for_position,
editor, editor,
(cmd, shift),
cx, cx,
); );
} }
} }
} else { } else {
update_go_to_definition_link(editor, None, cmd, shift, cx); update_go_to_definition_link(editor, GoToDefinitionTrigger::None, cmd, shift, cx);
hover_at(editor, None, cx); hover_at(editor, None, cx);
} }
@ -1821,11 +1837,11 @@ impl EditorElement {
} }
} }
// TODO kb implement
fn update_inlay_link_and_hover_points( fn update_inlay_link_and_hover_points(
position_map: &PositionMap, position_map: &PositionMap,
point_for_position: PointForPosition, point_for_position: PointForPosition,
editor: &mut Editor, editor: &mut Editor,
(cmd_held, shift_held): (bool, bool),
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, '_, Editor>,
) { ) {
let hint_start_offset = position_map let hint_start_offset = position_map
@ -1861,6 +1877,9 @@ fn update_inlay_link_and_hover_points(
.to_point(&position_map.snapshot.display_snapshot), .to_point(&position_map.snapshot.display_snapshot),
Bias::Right, Bias::Right,
); );
let mut go_to_definition_updated = false;
let mut hover_updated = false;
if let Some(hovered_hint) = editor if let Some(hovered_hint) = editor
.visible_inlay_hints(cx) .visible_inlay_hints(cx)
.into_iter() .into_iter()
@ -1872,7 +1891,7 @@ fn update_inlay_link_and_hover_points(
if let Some(cached_hint) = if let Some(cached_hint) =
inlay_hint_cache.hint_by_id(previous_valid_anchor.excerpt_id, hovered_hint.id) inlay_hint_cache.hint_by_id(previous_valid_anchor.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(
@ -1884,7 +1903,7 @@ 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 cached_hint.tooltip.is_some() {
dbg!(&cached_hint.tooltip); // TODO kb dbg!(&cached_hint.tooltip); // TODO kb
@ -1893,7 +1912,7 @@ fn update_inlay_link_and_hover_points(
} }
project::InlayHintLabel::LabelParts(label_parts) => { project::InlayHintLabel::LabelParts(label_parts) => {
if let Some(hovered_hint_part) = find_hovered_hint_part( if let Some(hovered_hint_part) = find_hovered_hint_part(
&label_parts, label_parts,
hint_start_offset..hint_end_offset, hint_start_offset..hint_end_offset,
hovered_offset, hovered_offset,
) { ) {
@ -1901,9 +1920,31 @@ fn update_inlay_link_and_hover_points(
dbg!(&hovered_hint_part.tooltip); // TODO kb dbg!(&hovered_hint_part.tooltip); // TODO kb
// hover_at_point = Some(hovered_offset); // hover_at_point = Some(hovered_offset);
} }
if let Some(location) = &hovered_hint_part.location { if let Some(location) = hovered_hint_part.location {
dbg!(location); // TODO kb if let Some(buffer) = cached_hint
// go_to_definition_point = Some(location); .position
.buffer_id
.and_then(|buffer_id| buffer.buffer(buffer_id))
{
go_to_definition_updated = true;
update_go_to_definition_link(
editor,
GoToDefinitionTrigger::InlayHint(
hovered_hint.position,
LocationLink {
origin: Some(Location {
buffer,
range: cached_hint.position
..cached_hint.position,
}),
target: location,
},
),
cmd_held,
shift_held,
cx,
);
}
} }
} }
} }
@ -1913,14 +1954,27 @@ fn update_inlay_link_and_hover_points(
} }
} }
} }
if !go_to_definition_updated {
update_go_to_definition_link(
editor,
GoToDefinitionTrigger::None,
cmd_held,
shift_held,
cx,
);
}
if !hover_updated {
hover_at(editor, None, cx);
}
} }
} }
fn find_hovered_hint_part<'a>( fn find_hovered_hint_part(
label_parts: &'a [InlayHintLabelPart], label_parts: Vec<InlayHintLabelPart>,
hint_range: Range<InlayOffset>, hint_range: Range<InlayOffset>,
hovered_offset: InlayOffset, hovered_offset: InlayOffset,
) -> Option<&'a InlayHintLabelPart> { ) -> Option<InlayHintLabelPart> {
if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end { if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end {
let mut hovered_character = (hovered_offset - hint_range.start).0; let mut hovered_character = (hovered_offset - hint_range.start).0;
for part in label_parts { for part in label_parts {

View file

@ -615,7 +615,7 @@ impl Item for Editor {
fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) { fn workspace_deactivated(&mut self, cx: &mut ViewContext<Self>) {
hide_link_definition(self, cx); hide_link_definition(self, cx);
self.link_go_to_definition_state.last_mouse_location = None; self.link_go_to_definition_state.last_trigger_point = None;
} }
fn is_dirty(&self, cx: &AppContext) -> bool { fn is_dirty(&self, cx: &AppContext) -> bool {

View file

@ -7,16 +7,50 @@ use util::TryFutureExt;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LinkGoToDefinitionState { pub struct LinkGoToDefinitionState {
pub last_mouse_location: Option<Anchor>, pub last_trigger_point: Option<TriggerPoint>,
pub symbol_range: Option<Range<Anchor>>, pub symbol_range: Option<Range<Anchor>>,
pub kind: Option<LinkDefinitionKind>, pub kind: Option<LinkDefinitionKind>,
pub definitions: Vec<LocationLink>, pub definitions: Vec<LocationLink>,
pub task: Option<Task<Option<()>>>, pub task: Option<Task<Option<()>>>,
} }
pub enum GoToDefinitionTrigger {
Text(DisplayPoint),
InlayHint(Anchor, LocationLink),
None,
}
#[derive(Debug, Clone)]
pub enum TriggerPoint {
Text(Anchor),
InlayHint(Anchor, LocationLink),
}
impl TriggerPoint {
fn anchor(&self) -> &Anchor {
match self {
TriggerPoint::Text(anchor) => anchor,
TriggerPoint::InlayHint(anchor, _) => anchor,
}
}
pub fn definition_kind(&self, shift: bool) -> LinkDefinitionKind {
match self {
TriggerPoint::Text(_) => {
if shift {
LinkDefinitionKind::Type
} else {
LinkDefinitionKind::Symbol
}
}
TriggerPoint::InlayHint(_, link) => LinkDefinitionKind::Type,
}
}
}
pub fn update_go_to_definition_link( pub fn update_go_to_definition_link(
editor: &mut Editor, editor: &mut Editor,
point: Option<DisplayPoint>, origin: GoToDefinitionTrigger,
cmd_held: bool, cmd_held: bool,
shift_held: bool, shift_held: bool,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
@ -25,23 +59,30 @@ pub fn update_go_to_definition_link(
// Store new mouse point as an anchor // Store new mouse point as an anchor
let snapshot = editor.snapshot(cx); let snapshot = editor.snapshot(cx);
let point = point.map(|point| { let trigger_point = match origin {
snapshot GoToDefinitionTrigger::Text(p) => {
.buffer_snapshot Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before(
.anchor_before(point.to_offset(&snapshot.display_snapshot, Bias::Left)) p.to_offset(&snapshot.display_snapshot, Bias::Left),
}); )))
}
GoToDefinitionTrigger::InlayHint(p, target) => Some(TriggerPoint::InlayHint(p, target)),
GoToDefinitionTrigger::None => None,
};
// If the new point is the same as the previously stored one, return early // If the new point is the same as the previously stored one, return early
if let (Some(a), Some(b)) = ( if let (Some(a), Some(b)) = (
&point, &trigger_point,
&editor.link_go_to_definition_state.last_mouse_location, &editor.link_go_to_definition_state.last_trigger_point,
) { ) {
if a.cmp(b, &snapshot.buffer_snapshot).is_eq() { if a.anchor()
.cmp(b.anchor(), &snapshot.buffer_snapshot)
.is_eq()
{
return; return;
} }
} }
editor.link_go_to_definition_state.last_mouse_location = point.clone(); editor.link_go_to_definition_state.last_trigger_point = trigger_point.clone();
if pending_nonempty_selection { if pending_nonempty_selection {
hide_link_definition(editor, cx); hide_link_definition(editor, cx);
@ -49,14 +90,9 @@ pub fn update_go_to_definition_link(
} }
if cmd_held { if cmd_held {
if let Some(point) = point { if let Some(trigger_point) = trigger_point {
let kind = if shift_held { let kind = trigger_point.definition_kind(shift_held);
LinkDefinitionKind::Type show_link_definition(kind, editor, trigger_point, snapshot, cx);
} else {
LinkDefinitionKind::Symbol
};
show_link_definition(kind, editor, point, snapshot, cx);
return; return;
} }
} }
@ -73,7 +109,7 @@ pub enum LinkDefinitionKind {
pub fn show_link_definition( pub fn show_link_definition(
definition_kind: LinkDefinitionKind, definition_kind: LinkDefinitionKind,
editor: &mut Editor, editor: &mut Editor,
trigger_point: Anchor, trigger_point: TriggerPoint,
snapshot: EditorSnapshot, snapshot: EditorSnapshot,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) { ) {
@ -86,10 +122,11 @@ pub fn show_link_definition(
return; return;
} }
let trigger_anchor = trigger_point.anchor().clone();
let (buffer, buffer_position) = if let Some(output) = editor let (buffer, buffer_position) = if let Some(output) = editor
.buffer .buffer
.read(cx) .read(cx)
.text_anchor_for_position(trigger_point.clone(), cx) .text_anchor_for_position(trigger_anchor.clone(), cx)
{ {
output output
} else { } else {
@ -99,7 +136,7 @@ pub fn show_link_definition(
let excerpt_id = if let Some((excerpt_id, _, _)) = editor let excerpt_id = if let Some((excerpt_id, _, _)) = editor
.buffer() .buffer()
.read(cx) .read(cx)
.excerpt_containing(trigger_point.clone(), cx) .excerpt_containing(trigger_anchor.clone(), cx)
{ {
excerpt_id excerpt_id
} else { } else {
@ -116,12 +153,12 @@ pub fn show_link_definition(
if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range { if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range {
let point_after_start = symbol_range let point_after_start = symbol_range
.start .start
.cmp(&trigger_point, &snapshot.buffer_snapshot) .cmp(&trigger_anchor, &snapshot.buffer_snapshot)
.is_le(); .is_le();
let point_before_end = symbol_range let point_before_end = symbol_range
.end .end
.cmp(&trigger_point, &snapshot.buffer_snapshot) .cmp(&trigger_anchor, &snapshot.buffer_snapshot)
.is_ge(); .is_ge();
let point_within_range = point_after_start && point_before_end; let point_within_range = point_after_start && point_before_end;
@ -132,34 +169,45 @@ pub fn show_link_definition(
let task = cx.spawn(|this, mut cx| { let task = cx.spawn(|this, mut cx| {
async move { async move {
// query the LSP for definition info let result = match trigger_point {
let definition_request = cx.update(|cx| { TriggerPoint::Text(_) => {
project.update(cx, |project, cx| match definition_kind { // query the LSP for definition info
LinkDefinitionKind::Symbol => project.definition(&buffer, buffer_position, cx), cx.update(|cx| {
project.update(cx, |project, cx| match definition_kind {
LinkDefinitionKind::Symbol => {
project.definition(&buffer, buffer_position, cx)
}
LinkDefinitionKind::Type => { LinkDefinitionKind::Type => {
project.type_definition(&buffer, buffer_position, cx) project.type_definition(&buffer, buffer_position, cx)
} }
})
});
let result = definition_request.await.ok().map(|definition_result| {
(
definition_result.iter().find_map(|link| {
link.origin.as_ref().map(|origin| {
let start = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id.clone(), origin.range.start);
let end = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id.clone(), origin.range.end);
start..end
}) })
}), })
definition_result, .await
) .ok()
}); .map(|definition_result| {
(
definition_result.iter().find_map(|link| {
link.origin.as_ref().map(|origin| {
let start = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id.clone(), origin.range.start);
let end = snapshot
.buffer_snapshot
.anchor_in_excerpt(excerpt_id.clone(), origin.range.end);
start..end
})
}),
definition_result,
)
})
}
TriggerPoint::InlayHint(trigger_source, trigger_target) => {
// TODO kb range is wrong, should be in inlay coordinates
Some((Some(trigger_source..trigger_source), vec![trigger_target]))
}
};
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
// Clear any existing highlights // Clear any existing highlights
@ -202,7 +250,7 @@ pub fn show_link_definition(
// If no symbol range returned from language server, use the surrounding word. // If no symbol range returned from language server, use the surrounding word.
let highlight_range = symbol_range.unwrap_or_else(|| { let highlight_range = symbol_range.unwrap_or_else(|| {
let snapshot = &snapshot.buffer_snapshot; let snapshot = &snapshot.buffer_snapshot;
let (offset_range, _) = snapshot.surrounding_word(trigger_point); let (offset_range, _) = snapshot.surrounding_word(trigger_anchor);
snapshot.anchor_before(offset_range.start) snapshot.anchor_before(offset_range.start)
..snapshot.anchor_after(offset_range.end) ..snapshot.anchor_after(offset_range.end)
@ -355,7 +403,13 @@ mod tests {
// Press cmd+shift to trigger highlight // Press cmd+shift to trigger highlight
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, true, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
true,
cx,
);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -461,7 +515,13 @@ mod tests {
}); });
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -482,7 +542,7 @@ mod tests {
"}); "});
// Response without source range still highlights word // Response without source range still highlights word
cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_mouse_location = None); cx.update_editor(|editor, _| editor.link_go_to_definition_state.last_trigger_point = None);
let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move { let mut requests = cx.handle_request::<GotoDefinition, _, _>(move |url, _, _| async move {
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ Ok(Some(lsp::GotoDefinitionResponse::Link(vec![
lsp::LocationLink { lsp::LocationLink {
@ -495,7 +555,13 @@ mod tests {
]))) ])))
}); });
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -517,7 +583,13 @@ mod tests {
Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) Ok(Some(lsp::GotoDefinitionResponse::Link(vec![])))
}); });
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
requests.next().await; requests.next().await;
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -534,7 +606,13 @@ mod tests {
fn do_work() { teˇst(); } fn do_work() { teˇst(); }
"}); "});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), false, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
false,
false,
cx,
);
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
@ -593,7 +671,13 @@ mod tests {
// Moving the mouse restores the highlights. // Moving the mouse restores the highlights.
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
@ -607,7 +691,13 @@ mod tests {
fn do_work() { tesˇt(); } fn do_work() { tesˇt(); }
"}); "});
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {" cx.assert_editor_text_highlights::<LinkGoToDefinitionState>(indoc! {"
@ -703,7 +793,13 @@ mod tests {
}); });
}); });
cx.update_editor(|editor, cx| { cx.update_editor(|editor, cx| {
update_go_to_definition_link(editor, Some(hover_point), true, false, cx); update_go_to_definition_link(
editor,
GoToDefinitionTrigger::Text(hover_point),
true,
false,
cx,
);
}); });
cx.foreground().run_until_parked(); cx.foreground().run_until_parked();
assert!(requests.try_next().is_err()); assert!(requests.try_next().is_err());