diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 5dad501df6731532bd713c8a4ae22eccfbda03f6..a870b70f7b1e04b654a1a515849166a4d3456bdb 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -30,6 +30,8 @@ pub use block_map::{ BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock, }; +use self::editor_addition_map::InlayHintToRender; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FoldStatus { Folded, @@ -286,6 +288,25 @@ impl DisplayMap { .update(cx, |map, cx| map.set_wrap_width(width, cx)) } + pub fn set_inlay_hints(&self, new_hints: &[project::InlayHint], cx: &mut ModelContext) { + let multi_buffer = self.buffer.read(cx); + self.editor_addition_map.set_inlay_hints( + new_hints + .into_iter() + .filter_map(|hint| { + let buffer = multi_buffer.buffer(hint.buffer_id)?.read(cx); + let snapshot = buffer.snapshot(); + Some(InlayHintToRender { + position: editor_addition_map::EditorAdditionPoint( + text::ToPoint::to_point(&hint.position, &snapshot), + ), + text: hint.text().trim_end().into(), + }) + }) + .collect(), + ) + } + fn tab_size(buffer: &ModelHandle, cx: &mut ModelContext) -> NonZeroU32 { let language = buffer .read(cx) diff --git a/crates/editor/src/display_map/editor_addition_map.rs b/crates/editor/src/display_map/editor_addition_map.rs index 1388f91d306351fc613322dec135c05d6a573cef..3603400efc985968b7ea3b10e530fca6bfd01b7b 100644 --- a/crates/editor/src/display_map/editor_addition_map.rs +++ b/crates/editor/src/display_map/editor_addition_map.rs @@ -10,17 +10,20 @@ use super::{ TextHighlights, }; use gpui::fonts::HighlightStyle; -use language::{Chunk, Edit, Point, TextSummary}; +use language::{Chunk, Edit, Point, Rope, TextSummary}; +use parking_lot::Mutex; +use project::InlayHint; use rand::Rng; use sum_tree::Bias; -pub struct EditorAdditionMap; +pub struct EditorAdditionMap(Mutex); #[derive(Clone)] pub struct EditorAdditionSnapshot { // TODO kb merge these two together pub suggestion_snapshot: SuggestionSnapshot, pub version: usize, + hints: Vec, } pub type EditorAdditionEdit = Edit; @@ -63,6 +66,12 @@ pub struct EditorAdditionChunks<'a> { _z: &'a std::marker::PhantomData<()>, } +#[derive(Clone)] +pub struct InlayHintToRender { + pub(super) position: EditorAdditionPoint, + pub(super) text: Rope, +} + impl<'a> Iterator for EditorAdditionChunks<'a> { type Item = Chunk<'a>; @@ -95,7 +104,12 @@ impl EditorAdditionPoint { impl EditorAdditionMap { pub fn new(suggestion_snapshot: SuggestionSnapshot) -> (Self, EditorAdditionSnapshot) { - todo!("TODO kb") + let snapshot = EditorAdditionSnapshot { + suggestion_snapshot: suggestion_snapshot.clone(), + version: 0, + hints: Vec::new(), + }; + (Self(Mutex::new(snapshot.clone())), snapshot) } pub fn sync( @@ -103,14 +117,24 @@ impl EditorAdditionMap { suggestion_snapshot: SuggestionSnapshot, suggestion_edits: Vec, ) -> (EditorAdditionSnapshot, Vec) { - todo!("TODO kb") + let mut snapshot = self.0.lock(); + + if snapshot.suggestion_snapshot.version != suggestion_snapshot.version { + snapshot.version += 1; + } + + let editor_addition_edits = Vec::new(); + { + todo!("TODO kb") + } + + snapshot.suggestion_snapshot = suggestion_snapshot; + + (snapshot.clone(), editor_addition_edits) } - pub fn randomly_mutate( - &self, - rng: &mut impl Rng, - ) -> (EditorAdditionSnapshot, Vec) { - todo!("TODO kb") + pub fn set_inlay_hints(&self, new_hints: Vec) { + self.0.lock().hints = new_hints; } } diff --git a/crates/editor/src/display_map/tab_map.rs b/crates/editor/src/display_map/tab_map.rs index b54de1ad651538d629bfc94ba03615268ea74339..a3e8cbdb373aff202d8b5f80b67ff873bbd8fdd6 100644 --- a/crates/editor/src/display_map/tab_map.rs +++ b/crates/editor/src/display_map/tab_map.rs @@ -761,11 +761,13 @@ mod tests { let (suggestion_map, _) = SuggestionMap::new(fold_snapshot); let (suggestion_snapshot, _) = suggestion_map.randomly_mutate(&mut rng); log::info!("SuggestionMap text: {:?}", suggestion_snapshot.text()); - let (editor_addition_map, _) = EditorAdditionMap::new(suggestion_snapshot.clone()); - let (suggestion_snapshot, _) = editor_addition_map.randomly_mutate(&mut rng); - log::info!("EditorAdditionMap text: {:?}", suggestion_snapshot.text()); + let (_, editor_addition_snapshot) = EditorAdditionMap::new(suggestion_snapshot.clone()); + log::info!( + "EditorAdditionMap text: {:?}", + editor_addition_snapshot.text() + ); - let (tab_map, _) = TabMap::new(suggestion_snapshot.clone(), tab_size); + let (tab_map, _) = TabMap::new(editor_addition_snapshot.clone(), tab_size); let tabs_snapshot = tab_map.set_max_expansion_column(32); let text = text::Rope::from(tabs_snapshot.text().as_str()); @@ -803,7 +805,7 @@ mod tests { ); let mut actual_summary = tabs_snapshot.text_summary_for_range(start..end); - if tab_size.get() > 1 && suggestion_snapshot.text().contains('\t') { + if tab_size.get() > 1 && editor_addition_snapshot.text().contains('\t') { actual_summary.longest_row = expected_summary.longest_row; actual_summary.longest_row_chars = expected_summary.longest_row_chars; } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 63b3c4f6a3b19d7b7b6a743b45db78ce431bf52f..bfdfbd2a77ef56c2dc704eadfcc25a857a9eef51 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1169,11 +1169,19 @@ impl InlayHintState { Self::first_timestamp_newer(timestamp, ¤t_timestamp) } - fn update_if_newer(&self, new_hints: Vec, new_timestamp: HashMap) { + fn update_if_newer( + &self, + new_hints: Vec, + new_timestamp: HashMap, + ) -> bool { let mut guard = self.0.write(); if Self::first_timestamp_newer(&new_timestamp, &guard.0) { guard.0 = new_timestamp; guard.1 = new_hints; + + true + } else { + false } } @@ -2688,7 +2696,7 @@ impl Editor { let inlay_hints_storage = Arc::clone(&self.inlay_hints); if inlay_hints_storage.is_newer(&new_timestamp) { - cx.spawn(|_, _| async move { + cx.spawn(|editor, mut cx| async move { let mut new_hints = Vec::new(); for task_result in futures::future::join_all(hint_fetch_tasks).await { match task_result { @@ -2696,7 +2704,18 @@ impl Editor { Err(e) => error!("Failed to update hints for buffer: {e:#}"), } } - inlay_hints_storage.update_if_newer(new_hints, new_timestamp); + + // TODO kb another odd clone, can be avoid all this? hide hints behind a handle? + if inlay_hints_storage.update_if_newer(new_hints.clone(), new_timestamp) { + editor + .update(&mut cx, |editor, cx| { + editor.display_map.update(cx, |display_map, cx| { + display_map.set_inlay_hints(&new_hints, cx) + }); + }) + .log_err() + .unwrap_or(()) + } }) .detach(); } diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index fb01becaf417c917da91251a19c561a8aec5462f..e735773f4b244a1603ed61ac7aa3691eadd35bc8 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -1834,6 +1834,7 @@ impl LspCommand for InlayHints { .unwrap_or_default() .into_iter() .map(|lsp_hint| InlayHint { + buffer_id: buffer.id() as u64, position: origin_buffer.anchor_after( origin_buffer .clip_point_utf16(point_from_lsp(lsp_hint.position), Bias::Left), @@ -2006,6 +2007,7 @@ impl LspCommand for InlayHints { let mut hints = Vec::new(); for message_hint in message.hints { let hint = InlayHint { + buffer_id: buffer.id() as u64, position: message_hint .position .and_then(language::proto::deserialize_anchor) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e941eee0329f753d5f9428507b370538b1cdcab6..c33b563ea5516caf7fbf883aacd801267114f207 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -29,6 +29,7 @@ use gpui::{ AnyModelHandle, AppContext, AsyncAppContext, BorrowAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle, }; +use itertools::Itertools; use language::{ language_settings::{language_settings, FormatOnSave, Formatter}, point_to_lsp, @@ -320,46 +321,56 @@ pub struct DiagnosticSummary { pub warning_count: usize, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Location { pub buffer: ModelHandle, pub range: Range, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct InlayHint { + pub buffer_id: u64, pub position: Anchor, pub label: InlayHintLabel, pub kind: Option, pub tooltip: Option, } -#[derive(Debug, Clone)] +impl InlayHint { + pub fn text(&self) -> String { + match &self.label { + InlayHintLabel::String(s) => s.to_owned(), + InlayHintLabel::LabelParts(parts) => parts.iter().map(|part| &part.value).join(""), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] pub enum InlayHintLabel { String(String), LabelParts(Vec), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct InlayHintLabelPart { pub value: String, pub tooltip: Option, pub location: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum InlayHintTooltip { String(String), MarkupContent(MarkupContent), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum InlayHintLabelPartTooltip { String(String), MarkupContent(MarkupContent), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct MarkupContent { pub kind: String, pub value: String,