diff --git a/crates/editor/src/display_map.rs b/crates/editor/src/display_map.rs index 59ba9d8f4e87ff511094938bfdcc20bfb5606c26..40f6ecb76ee3af8131fa210c3cc4bc4f34017ec9 100644 --- a/crates/editor/src/display_map.rs +++ b/crates/editor/src/display_map.rs @@ -6,8 +6,8 @@ mod tab_map; mod wrap_map; use crate::{ - display_map::inlay_map::InlayProperties, Anchor, AnchorRangeExt, InlayHintLocation, - MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint, + display_map::inlay_map::InlayProperties, Anchor, AnchorRangeExt, MultiBuffer, + MultiBufferSnapshot, ToOffset, ToPoint, }; pub use block_map::{BlockMap, BlockPoint}; use collections::{HashMap, HashSet}; @@ -287,7 +287,7 @@ impl DisplayMap { pub fn splice_inlays( &mut self, - new_hints: &HashMap>, + new_hints: Vec<(Anchor, project::InlayHint)>, cx: &mut ModelContext, ) { let buffer_snapshot = self.buffer.read(cx).snapshot(cx); @@ -302,18 +302,13 @@ impl DisplayMap { .update(cx, |map, cx| map.sync(snapshot, edits, cx)); self.block_map.read(snapshot, edits); - let mut new_inlays = Vec::new(); - for (&location, hints) in new_hints { - for hint in hints { - let hint_anchor = - buffer_snapshot.anchor_in_excerpt(location.excerpt_id, hint.position); - new_inlays.push(InlayProperties { - position: hint_anchor.bias_left(&buffer_snapshot), - text: hint.text(), - }); - } - } - + let new_inlays = new_hints + .into_iter() + .map(|(hint_anchor, hint)| InlayProperties { + position: hint_anchor.bias_left(&buffer_snapshot), + text: hint.text(), + }) + .collect(); let (snapshot, edits, _) = self.inlay_map.splice( // TODO kb this is wrong, calc diffs in the editor instead. self.inlay_map.inlays.keys().copied().collect(), diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 2d5224de32a3794860653739cd683c0ceee985de..ef1db6fcc6b1f38f8765715cb22f1cf61f1625eb 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2,6 +2,7 @@ mod blink_manager; pub mod display_map; mod editor_settings; mod element; +mod inlay_hint_storage; mod git; mod highlight_matching_bracket; @@ -25,7 +26,7 @@ use aho_corasick::AhoCorasick; use anyhow::{anyhow, Context, Result}; use blink_manager::BlinkManager; use client::{ClickhouseEvent, TelemetrySettings}; -use clock::{Global, ReplicaId}; +use clock::ReplicaId; use collections::{BTreeMap, Bound, HashMap, HashSet, VecDeque}; use copilot::Copilot; pub use display_map::DisplayPoint; @@ -52,6 +53,7 @@ use gpui::{ }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; +use inlay_hint_storage::InlayHintStorage; pub use items::MAX_TAB_TITLE_LEN; use itertools::Itertools; pub use language::{char_kind, CharKind}; @@ -71,7 +73,9 @@ pub use multi_buffer::{ }; use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; -use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction}; +use project::{ + FormatTrigger, InlayHint, Location, LocationLink, Project, ProjectPath, ProjectTransaction, +}; use scroll::{ autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide, }; @@ -536,7 +540,7 @@ pub struct Editor { gutter_hovered: bool, link_go_to_definition_state: LinkGoToDefinitionState, copilot_state: CopilotState, - inlay_hint_versions: InlayHintVersions, + inlay_hint_storage: InlayHintStorage, _subscriptions: Vec, } @@ -1153,37 +1157,6 @@ impl CopilotState { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct InlayHintLocation { - pub buffer_id: u64, - pub excerpt_id: ExcerptId, -} - -// TODO kb -#[derive(Debug, Default, Clone)] -struct InlayHintVersions { - last_buffer_versions_with_hints: HashMap, -} - -impl InlayHintVersions { - fn absent_or_newer(&self, location: &InlayHintLocation, new_version: &Global) -> bool { - self.last_buffer_versions_with_hints - .get(location) - .map(|last_version_with_hints| new_version.changed_since(&last_version_with_hints)) - .unwrap_or(true) - } - - fn insert(&mut self, location: InlayHintLocation, new_version: Global) -> bool { - if self.absent_or_newer(&location, &new_version) { - self.last_buffer_versions_with_hints - .insert(location, new_version); - true - } else { - false - } - } -} - #[derive(Debug)] struct ActiveDiagnosticGroup { primary_range: Range, @@ -1324,7 +1297,7 @@ impl Editor { project_subscriptions.push(cx.subscribe(project, |editor, _, event, cx| { match event { project::Event::ReloadInlayHints => { - editor.try_update_inlay_hints(cx); + editor.reload_inlay_hints(cx); } _ => {} }; @@ -1382,7 +1355,7 @@ impl Editor { hover_state: Default::default(), link_go_to_definition_state: Default::default(), copilot_state: Default::default(), - inlay_hint_versions: InlayHintVersions::default(), + inlay_hint_storage: InlayHintStorage::default(), gutter_hovered: false, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), @@ -2618,44 +2591,44 @@ impl Editor { } } - fn try_update_inlay_hints(&self, cx: &mut ViewContext) { + fn reload_inlay_hints(&self, cx: &mut ViewContext) { if self.mode != EditorMode::Full { return; } - let multi_buffer = self.buffer().read(cx); - let buffer_snapshot = multi_buffer.snapshot(cx); - let hint_fetch_tasks = buffer_snapshot - .excerpts() - .map(|(excerpt_id, excerpt_buffer_snapshot, _)| { - (excerpt_id, excerpt_buffer_snapshot.clone()) - }) - .map(|(excerpt_id, excerpt_buffer_snapshot)| { + let multi_buffer = self.buffer(); + let hint_fetch_tasks = multi_buffer + .read(cx) + .all_buffers() + .into_iter() + .map(|buffer_handle| { + let buffer = buffer_handle.read(cx); + // TODO kb every time I reopen the same buffer, it's different. + // Find a way to understand it's the same buffer. Use paths? + dbg!(buffer_handle.id()); + let buffer_id = dbg!(buffer.remote_id()); + let buffer_len = buffer.len(); + cx.spawn(|editor, mut cx| async move { let task = editor .update(&mut cx, |editor, cx| { - editor.project.as_ref().and_then(|project| { + editor.project.as_ref().map(|project| { project.update(cx, |project, cx| { - Some( - project.inlay_hints_for_buffer( - editor - .buffer() - .read(cx) - .buffer(excerpt_buffer_snapshot.remote_id())?, - 0..excerpt_buffer_snapshot.len(), - cx, - ), - ) + project.inlay_hints_for_buffer(buffer_handle, 0..buffer_len, cx) }) }) }) .context("inlay hints fecth task spawn")?; anyhow::Ok(( - excerpt_id, - excerpt_buffer_snapshot, + buffer_id, match task { - Some(task) => task.await.context("inlay hints for buffer task")?, + Some(task) => { + let mut buffer_hints = + task.await.context("inlay hints for buffer task")?; + buffer_hints.sort_unstable_by_key(|hint| hint.position.offset); + buffer_hints + } None => Vec::new(), }, )) @@ -2664,52 +2637,57 @@ impl Editor { .collect::>(); cx.spawn(|editor, mut cx| async move { - let mut new_hints: HashMap> = - HashMap::default(); + let mut hints_to_draw: Vec<(Anchor, InlayHint)> = Vec::new(); + let (multi_buffer, multi_buffer_snapshot) = editor.read_with(&cx, |editor, cx| { + let multi_buffer = editor.buffer().clone(); + let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); + (multi_buffer, multi_buffer_snapshot) + })?; + for task_result in futures::future::join_all(hint_fetch_tasks).await { match task_result { - Ok((excerpt_id, excerpt_buffer_snapshot, excerpt_hints)) => { - let buffer_id = excerpt_buffer_snapshot.remote_id(); - let should_update_hints = editor - .update(&mut cx, |editor, _| { - // TODO kb wrong: need to query hints per buffer, not per excerpt - // need to store the previous state and calculate the diff between them, and calculate anchors here too. - editor.inlay_hint_versions.insert( - InlayHintLocation { - buffer_id, - excerpt_id, - }, - excerpt_buffer_snapshot.version().clone(), - ) - }) - .log_err() - .unwrap_or(false); - if should_update_hints { - new_hints - .entry(InlayHintLocation { - buffer_id, - excerpt_id, + Ok((buffer_id, sorted_buffer_hints)) => { + let Some(buffer_excerpts) = cx.read(|cx| { + let multi_buffer = multi_buffer.read(cx); + multi_buffer.buffer(buffer_id).map(|buffer| multi_buffer.excerpts_for_buffer(&buffer, cx)) + }) else { continue }; + for (excerpt_id, excerpt_range) in buffer_excerpts { + let excerpt_hints = sorted_buffer_hints + .iter() + .cloned() + .skip_while(|hint| { + hint.position.offset < excerpt_range.context.start.offset + }) + .take_while(|hint| { + hint.position.offset <= excerpt_range.context.end.offset }) - .or_default() - .extend(excerpt_hints); + .collect::>(); + + if !excerpt_hints.is_empty() { + hints_to_draw.extend(excerpt_hints.into_iter().map(|hint| { + let anchor = multi_buffer_snapshot + .anchor_in_excerpt(excerpt_id, hint.position); + (anchor, hint) + })); + } } } Err(e) => error!("Failed to update hints for buffer: {e:#}"), } } - if !new_hints.is_empty() { - editor - .update(&mut cx, |editor, cx| { - editor.display_map.update(cx, |display_map, cx| { - display_map.splice_inlays(&new_hints, cx); - }); - }) - .log_err() - .unwrap_or(()) + // TODO kb calculate diffs using the storage instead + if !hints_to_draw.is_empty() { + editor.update(&mut cx, |editor, cx| { + editor.display_map.update(cx, |display_map, cx| { + display_map.splice_inlays(hints_to_draw, cx); + }); + })?; } + + anyhow::Ok(()) }) - .detach(); + .detach_and_log_err(cx); } fn trigger_on_type_formatting( @@ -7292,7 +7270,7 @@ impl Editor { }; if update_inlay_hints { - self.try_update_inlay_hints(cx); + self.reload_inlay_hints(cx); } } diff --git a/crates/editor/src/inlay_hint_storage.rs b/crates/editor/src/inlay_hint_storage.rs new file mode 100644 index 0000000000000000000000000000000000000000..90e5ea01fbbbdb766e39f03dd19c646241b1eaea --- /dev/null +++ b/crates/editor/src/inlay_hint_storage.rs @@ -0,0 +1,43 @@ +use crate::Anchor; +use project::InlayHint; + +use collections::BTreeMap; + +#[derive(Debug, Default)] +pub struct InlayHintStorage { + hints: BTreeMap, +} + +impl InlayHintStorage { + // TODO kb calculate the diff instead + fn insert(&mut self) -> bool { + todo!("TODO kb") + } +} + +// let buffer_version = +// cx.read(|cx| buffer.read(cx).version().clone()); + +// #[derive(Debug, Default, Clone)] +// struct InlayHintVersions { +// last_buffer_versions_with_hints: HashMap, +// } + +// impl InlayHintVersions { +// fn absent_or_newer(&self, location: &InlayHintLocation, new_version: &Global) -> bool { +// self.last_buffer_versions_with_hints +// .get(location) +// .map(|last_version_with_hints| new_version.changed_since(&last_version_with_hints)) +// .unwrap_or(true) +// } + +// fn insert(&mut self, location: InlayHintLocation, new_version: Global) -> bool { +// if self.absent_or_newer(&location, &new_version) { +// self.last_buffer_versions_with_hints +// .insert(location, new_version); +// true +// } else { +// false +// } +// } +// }