From 7eab18ec897738b7e6e5a8b71f15bcc3a3af4fe5 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 21 Aug 2023 17:54:34 +0300 Subject: [PATCH] Pass inlay go to definition data --- crates/editor/src/editor.rs | 13 +- crates/editor/src/element.rs | 82 ++++++-- crates/editor/src/items.rs | 2 +- crates/editor/src/link_go_to_definition.rs | 216 +++++++++++++++------ 4 files changed, 229 insertions(+), 84 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 775f3c07ec..fb1e87580a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -64,9 +64,7 @@ use language::{ Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Point, Selection, SelectionGoal, TransactionId, }; -use link_go_to_definition::{ - hide_link_definition, show_link_definition, LinkDefinitionKind, LinkGoToDefinitionState, -}; +use link_go_to_definition::{hide_link_definition, show_link_definition, LinkGoToDefinitionState}; use log::error; use multi_buffer::ToOffsetUtf16; pub use multi_buffer::{ @@ -8307,14 +8305,11 @@ impl View for Editor { ) -> bool { 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 { + let point = point.clone(); let snapshot = self.snapshot(cx); - let kind = if event.shift { - LinkDefinitionKind::Type - } else { - LinkDefinitionKind::Symbol - }; + let kind = point.definition_kind(event.shift); show_link_definition(kind, self, point, snapshot, cx); return false; diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 405a6e1a08..15b28914af 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -13,6 +13,7 @@ use crate::{ }, link_go_to_definition::{ go_to_fetched_definition, go_to_fetched_type_definition, update_go_to_definition_link, + GoToDefinitionTrigger, }, mouse_context_menu, EditorSettings, EditorStyle, GutterHover, UnfoldAt, }; @@ -42,7 +43,7 @@ use language::{ }; use project::{ project_settings::{GitGutterSetting, ProjectSettings}, - InlayHintLabelPart, ProjectPath, ResolveState, + InlayHintLabelPart, Location, LocationLink, ProjectPath, ResolveState, }; use smallvec::SmallVec; use std::{ @@ -395,7 +396,15 @@ impl EditorElement { 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() { let mut scroll_delta = Vector2F::zero(); @@ -460,7 +469,13 @@ impl EditorElement { let point_for_position = position_map.point_for_position(text_bounds, position); match point_for_position.as_valid() { 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); } None => { @@ -468,12 +483,13 @@ impl EditorElement { position_map, point_for_position, editor, + (cmd, shift), cx, ); } } } 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); } @@ -1821,11 +1837,11 @@ impl EditorElement { } } -// TODO kb implement fn update_inlay_link_and_hover_points( position_map: &PositionMap, point_for_position: PointForPosition, editor: &mut Editor, + (cmd_held, shift_held): (bool, bool), cx: &mut ViewContext<'_, '_, Editor>, ) { let hint_start_offset = position_map @@ -1861,6 +1877,9 @@ fn update_inlay_link_and_hover_points( .to_point(&position_map.snapshot.display_snapshot), Bias::Right, ); + + let mut go_to_definition_updated = false; + let mut hover_updated = false; if let Some(hovered_hint) = editor .visible_inlay_hints(cx) .into_iter() @@ -1872,7 +1891,7 @@ fn update_inlay_link_and_hover_points( if let Some(cached_hint) = 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(_, _) => { if let Some(buffer_id) = previous_valid_anchor.buffer_id { inlay_hint_cache.spawn_hint_resolve( @@ -1884,7 +1903,7 @@ fn update_inlay_link_and_hover_points( } } ResolveState::Resolved => { - match &cached_hint.label { + match cached_hint.label { project::InlayHintLabel::String(_) => { if cached_hint.tooltip.is_some() { dbg!(&cached_hint.tooltip); // TODO kb @@ -1893,7 +1912,7 @@ fn update_inlay_link_and_hover_points( } project::InlayHintLabel::LabelParts(label_parts) => { if let Some(hovered_hint_part) = find_hovered_hint_part( - &label_parts, + label_parts, hint_start_offset..hint_end_offset, hovered_offset, ) { @@ -1901,9 +1920,31 @@ fn update_inlay_link_and_hover_points( dbg!(&hovered_hint_part.tooltip); // TODO kb // hover_at_point = Some(hovered_offset); } - if let Some(location) = &hovered_hint_part.location { - dbg!(location); // TODO kb - // go_to_definition_point = Some(location); + 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)) + { + 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>( - label_parts: &'a [InlayHintLabelPart], +fn find_hovered_hint_part( + label_parts: Vec, hint_range: Range, hovered_offset: InlayOffset, -) -> Option<&'a InlayHintLabelPart> { +) -> Option { if hovered_offset >= hint_range.start && hovered_offset <= hint_range.end { let mut hovered_character = (hovered_offset - hint_range.start).0; for part in label_parts { diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 477eab41ac..30ed56af47 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -615,7 +615,7 @@ impl Item for Editor { fn workspace_deactivated(&mut self, cx: &mut ViewContext) { 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 { diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index 31df11a019..a001433727 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -7,16 +7,50 @@ use util::TryFutureExt; #[derive(Debug, Default)] pub struct LinkGoToDefinitionState { - pub last_mouse_location: Option, + pub last_trigger_point: Option, pub symbol_range: Option>, pub kind: Option, pub definitions: Vec, pub 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( editor: &mut Editor, - point: Option, + origin: GoToDefinitionTrigger, cmd_held: bool, shift_held: bool, cx: &mut ViewContext, @@ -25,23 +59,30 @@ pub fn update_go_to_definition_link( // Store new mouse point as an anchor let snapshot = editor.snapshot(cx); - let point = point.map(|point| { - snapshot - .buffer_snapshot - .anchor_before(point.to_offset(&snapshot.display_snapshot, Bias::Left)) - }); + let trigger_point = match origin { + GoToDefinitionTrigger::Text(p) => { + Some(TriggerPoint::Text(snapshot.buffer_snapshot.anchor_before( + 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 let (Some(a), Some(b)) = ( - &point, - &editor.link_go_to_definition_state.last_mouse_location, + &trigger_point, + &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; } } - 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 { hide_link_definition(editor, cx); @@ -49,14 +90,9 @@ pub fn update_go_to_definition_link( } if cmd_held { - if let Some(point) = point { - let kind = if shift_held { - LinkDefinitionKind::Type - } else { - LinkDefinitionKind::Symbol - }; - - show_link_definition(kind, editor, point, snapshot, cx); + if let Some(trigger_point) = trigger_point { + let kind = trigger_point.definition_kind(shift_held); + show_link_definition(kind, editor, trigger_point, snapshot, cx); return; } } @@ -73,7 +109,7 @@ pub enum LinkDefinitionKind { pub fn show_link_definition( definition_kind: LinkDefinitionKind, editor: &mut Editor, - trigger_point: Anchor, + trigger_point: TriggerPoint, snapshot: EditorSnapshot, cx: &mut ViewContext, ) { @@ -86,10 +122,11 @@ pub fn show_link_definition( return; } + let trigger_anchor = trigger_point.anchor().clone(); let (buffer, buffer_position) = if let Some(output) = editor .buffer .read(cx) - .text_anchor_for_position(trigger_point.clone(), cx) + .text_anchor_for_position(trigger_anchor.clone(), cx) { output } else { @@ -99,7 +136,7 @@ pub fn show_link_definition( let excerpt_id = if let Some((excerpt_id, _, _)) = editor .buffer() .read(cx) - .excerpt_containing(trigger_point.clone(), cx) + .excerpt_containing(trigger_anchor.clone(), cx) { excerpt_id } else { @@ -116,12 +153,12 @@ pub fn show_link_definition( if let Some(symbol_range) = &editor.link_go_to_definition_state.symbol_range { let point_after_start = symbol_range .start - .cmp(&trigger_point, &snapshot.buffer_snapshot) + .cmp(&trigger_anchor, &snapshot.buffer_snapshot) .is_le(); let point_before_end = symbol_range .end - .cmp(&trigger_point, &snapshot.buffer_snapshot) + .cmp(&trigger_anchor, &snapshot.buffer_snapshot) .is_ge(); 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| { async move { - // query the LSP for definition info - let definition_request = cx.update(|cx| { - project.update(cx, |project, cx| match definition_kind { - LinkDefinitionKind::Symbol => project.definition(&buffer, buffer_position, cx), + let result = match trigger_point { + TriggerPoint::Text(_) => { + // query the LSP for definition info + cx.update(|cx| { + project.update(cx, |project, cx| match definition_kind { + LinkDefinitionKind::Symbol => { + project.definition(&buffer, buffer_position, cx) + } - LinkDefinitionKind::Type => { - 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 + LinkDefinitionKind::Type => { + project.type_definition(&buffer, buffer_position, cx) + } }) - }), - 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| { // 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. let highlight_range = symbol_range.unwrap_or_else(|| { 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_after(offset_range.end) @@ -355,7 +403,13 @@ mod tests { // Press cmd+shift to trigger highlight 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; cx.foreground().run_until_parked(); @@ -461,7 +515,13 @@ mod tests { }); 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; cx.foreground().run_until_parked(); @@ -482,7 +542,7 @@ mod tests { "}); // 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::(move |url, _, _| async move { Ok(Some(lsp::GotoDefinitionResponse::Link(vec![ lsp::LocationLink { @@ -495,7 +555,13 @@ mod tests { ]))) }); 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; cx.foreground().run_until_parked(); @@ -517,7 +583,13 @@ mod tests { Ok(Some(lsp::GotoDefinitionResponse::Link(vec![]))) }); 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; cx.foreground().run_until_parked(); @@ -534,7 +606,13 @@ mod tests { fn do_work() { teˇst(); } "}); 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(); @@ -593,7 +671,13 @@ mod tests { // Moving the mouse restores the highlights. 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.assert_editor_text_highlights::(indoc! {" @@ -607,7 +691,13 @@ mod tests { fn do_work() { tesˇt(); } "}); 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.assert_editor_text_highlights::(indoc! {" @@ -703,7 +793,13 @@ mod tests { }); }); 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(); assert!(requests.try_next().is_err());