diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d32dc47154eb62592a0826578d0c05089e0b50df..fc3319dc56c438ac81993254912c0e3ebe79515b 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -3,7 +3,7 @@ mod blink_manager; pub mod display_map; mod editor_settings; mod element; -mod inlay_cache; +mod inlay_hint_cache; mod git; mod highlight_matching_bracket; @@ -54,7 +54,7 @@ use gpui::{ }; use highlight_matching_bracket::refresh_matching_bracket_highlights; use hover_popover::{hide_hover, HoverState}; -use inlay_cache::{InlayCache, InlayHintQuery, InlayRefreshReason, InlaySplice}; +use inlay_hint_cache::{InlayHintCache, InlayHintQuery, InlayRefreshReason, InlaySplice}; pub use items::MAX_TAB_TITLE_LEN; use itertools::Itertools; pub use language::{char_kind, CharKind}; @@ -540,7 +540,7 @@ pub struct Editor { gutter_hovered: bool, link_go_to_definition_state: LinkGoToDefinitionState, copilot_state: CopilotState, - inlay_cache: InlayCache, + inlay_cache: InlayHintCache, _subscriptions: Vec, } @@ -1355,7 +1355,7 @@ impl Editor { hover_state: Default::default(), link_go_to_definition_state: Default::default(), copilot_state: Default::default(), - inlay_cache: InlayCache::new(settings::get::(cx).inlay_hints), + inlay_cache: InlayHintCache::new(settings::get::(cx).inlay_hints), gutter_hovered: false, _subscriptions: vec![ cx.observe(&buffer, Self::on_buffer_changed), @@ -2604,23 +2604,37 @@ impl Editor { } let multi_buffer_handle = self.buffer().clone(); + let multi_buffer_snapshot = multi_buffer_handle.read(cx).snapshot(cx); let currently_visible_ranges = self.excerpt_visible_offsets(&multi_buffer_handle, cx); - let currently_shown_inlays = self - .display_map - .read(cx) - .current_inlays() - .map(|inlay| (inlay.position, inlay.id)) - .collect::>(); + let currently_shown_inlay_hints = self.display_map.read(cx).current_inlays().fold( + HashMap::>>::default(), + |mut current_hints, inlay| { + if let Some(buffer_id) = inlay.position.buffer_id { + let excerpt_hints = current_hints + .entry(buffer_id) + .or_default() + .entry(inlay.position.excerpt_id) + .or_default(); + match excerpt_hints.binary_search_by(|probe| { + inlay.position.cmp(&probe.0, &multi_buffer_snapshot) + }) { + Ok(ix) | Err(ix) => { + excerpt_hints.insert(ix, (inlay.position, inlay.id)); + } + } + } + current_hints + }, + ); match reason { InlayRefreshReason::SettingsChange(new_settings) => { if let Some(InlaySplice { to_remove, to_insert, }) = self.inlay_cache.apply_settings( - multi_buffer_handle, new_settings, currently_visible_ranges, - currently_shown_inlays, + currently_shown_inlay_hints, cx, ) { self.splice_inlay_hints(to_remove, to_insert, cx); @@ -2653,7 +2667,7 @@ impl Editor { editor.inlay_cache.append_inlays( multi_buffer_handle, std::iter::once(updated_range_query), - currently_shown_inlays, + currently_shown_inlay_hints, cx, ) })? @@ -2683,7 +2697,7 @@ impl Editor { editor.inlay_cache.replace_inlays( multi_buffer_handle, replacement_queries.into_iter(), - currently_shown_inlays, + currently_shown_inlay_hints, cx, ) })? diff --git a/crates/editor/src/inlay_cache.rs b/crates/editor/src/inlay_hint_cache.rs similarity index 74% rename from crates/editor/src/inlay_cache.rs rename to crates/editor/src/inlay_hint_cache.rs index 7d8dd67e78242dc8d760299106a0eef792761402..4754e2a186b8b28c9d9e62d479e45a2f41db1dc7 100644 --- a/crates/editor/src/inlay_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -9,7 +9,7 @@ use gpui::{ModelHandle, Task, ViewContext}; use language::Buffer; use project::{InlayHint, InlayHintKind}; -use collections::{HashMap, HashSet}; +use collections::{hash_map, HashMap, HashSet}; #[derive(Debug, Copy, Clone)] pub enum InlayRefreshReason { @@ -19,16 +19,25 @@ pub enum InlayRefreshReason { } #[derive(Debug, Clone, Default)] -pub struct InlayCache { +pub struct InlayHintCache { inlay_hints: HashMap, - inlays_in_buffers: HashMap, + inlays_in_buffers: HashMap>, allowed_hint_kinds: HashSet>, } #[derive(Clone, Debug, Default)] -struct BufferInlays { +struct BufferInlays { buffer_version: Global, - ordered_by_anchor_inlays: Vec<(Anchor, InlayId)>, + excerpt_inlays: HashMap>, +} + +impl BufferInlays { + fn new(buffer_version: Global) -> Self { + Self { + buffer_version, + excerpt_inlays: HashMap::default(), + } + } } #[derive(Debug, Default)] @@ -44,7 +53,7 @@ pub struct InlayHintQuery { pub excerpt_offset_query_range: Range, } -impl InlayCache { +impl InlayHintCache { pub fn new(inlay_hint_settings: editor_settings::InlayHints) -> Self { Self { allowed_hint_kinds: allowed_inlay_hint_types(inlay_hint_settings), @@ -55,10 +64,9 @@ impl InlayCache { pub fn apply_settings( &mut self, - multi_buffer: ModelHandle, inlay_hint_settings: editor_settings::InlayHints, currently_visible_ranges: Vec<(ModelHandle, Range, ExcerptId)>, - currently_shown_inlays: Vec<(Anchor, InlayId)>, + mut currently_shown_inlay_hints: HashMap>>, cx: &mut ViewContext, ) -> Option { let new_allowed_hint_kinds = allowed_inlay_hint_types(inlay_hint_settings); @@ -69,33 +77,88 @@ impl InlayCache { let mut to_remove = Vec::new(); let mut to_insert = Vec::new(); - let mut considered_inlay_ids = HashSet::default(); - for (_, shown_inlay_id) in currently_shown_inlays { - if let Some(inlay_hint) = self.inlay_hints.get(&shown_inlay_id) { - if !self.allowed_hint_kinds.contains(&inlay_hint.kind) { - to_remove.push(shown_inlay_id); + let mut considered_hints = + HashMap::>>::default(); + for (visible_buffer, _, visible_excerpt_id) in currently_visible_ranges { + let visible_buffer = visible_buffer.read(cx); + let visible_buffer_id = visible_buffer.remote_id(); + match currently_shown_inlay_hints.entry(visible_buffer_id) { + hash_map::Entry::Occupied(mut o) => { + let shown_hints_per_excerpt = o.get_mut(); + for (_, shown_hint_id) in shown_hints_per_excerpt + .remove(&visible_excerpt_id) + .unwrap_or_default() + { + considered_hints + .entry(visible_buffer_id) + .or_default() + .entry(visible_excerpt_id) + .or_default() + .insert(shown_hint_id); + match self.inlay_hints.get(&shown_hint_id) { + Some(shown_hint) => { + if !self.allowed_hint_kinds.contains(&shown_hint.kind) { + to_remove.push(shown_hint_id); + } + } + None => to_remove.push(shown_hint_id), + } + } + if shown_hints_per_excerpt.is_empty() { + o.remove(); + } } - considered_inlay_ids.insert(shown_inlay_id); + hash_map::Entry::Vacant(_) => {} } } - let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); - for (inlay_id, inlay_hint) in &self.inlay_hints { - if self.allowed_hint_kinds.contains(&inlay_hint.kind) - && !considered_inlay_ids.contains(inlay_id) - { - if let Some(hint_to_readd) = currently_visible_ranges.iter() - .filter(|(_, visible_range, _)| visible_range.contains(&inlay_hint.position.offset)) - .find_map(|(_, _, excerpt_id)| { - let Some(anchor) = multi_buffer_snapshot - .find_anchor_in_excerpt(*excerpt_id, inlay_hint.position) else { return None; }; - Some((Some(*inlay_id), anchor, inlay_hint.clone())) - }, - ) { - to_insert.push(hint_to_readd); - } - } - } + let reenabled_hints = self + .inlays_in_buffers + .iter() + .filter_map(|(cached_buffer_id, cached_hints_per_excerpt)| { + let considered_hints_in_excerpts = considered_hints.get(cached_buffer_id)?; + let not_considered_cached_inlays = cached_hints_per_excerpt + .excerpt_inlays + .iter() + .filter_map(|(cached_excerpt_id, cached_hints)| { + let considered_excerpt_hints = + considered_hints_in_excerpts.get(&cached_excerpt_id)?; + let not_considered_cached_inlays = cached_hints + .iter() + .filter(|(_, cached_hint_id)| { + !considered_excerpt_hints.contains(cached_hint_id) + }) + .copied(); + Some(not_considered_cached_inlays) + }) + .flatten(); + Some(not_considered_cached_inlays) + }) + .flatten() + .filter_map(|(cached_anchor, cached_inlay_id)| { + Some(( + cached_anchor, + cached_inlay_id, + self.inlay_hints.get(&cached_inlay_id)?, + )) + }) + .filter(|(_, _, cached_inlay)| self.allowed_hint_kinds.contains(&cached_inlay.kind)) + .map(|(cached_anchor, cached_inlay_id, reenabled_inlay)| { + ( + Some(cached_inlay_id), + cached_anchor, + reenabled_inlay.clone(), + ) + }); + to_insert.extend(reenabled_hints); + + to_remove.extend( + currently_shown_inlay_hints + .into_iter() + .flat_map(|(_, hints_by_excerpt)| hints_by_excerpt) + .flat_map(|(_, excerpt_hints)| excerpt_hints) + .map(|(_, hint_id)| hint_id), + ); Some(InlaySplice { to_remove, @@ -114,13 +177,13 @@ impl InlayCache { &mut self, multi_buffer: ModelHandle, ranges_to_add: impl Iterator, - currently_shown_inlays: Vec<(Anchor, InlayId)>, + currently_shown_inlay_hints: HashMap>>, cx: &mut ViewContext, ) -> Task> { self.fetch_inlays( multi_buffer, ranges_to_add, - currently_shown_inlays, + currently_shown_inlay_hints, false, cx, ) @@ -130,21 +193,52 @@ impl InlayCache { &mut self, multi_buffer: ModelHandle, new_ranges: impl Iterator, - currently_shown_inlays: Vec<(Anchor, InlayId)>, + currently_shown_inlay_hints: HashMap>>, cx: &mut ViewContext, ) -> Task> { - self.fetch_inlays(multi_buffer, new_ranges, currently_shown_inlays, true, cx) + self.fetch_inlays( + multi_buffer, + new_ranges, + currently_shown_inlay_hints, + true, + cx, + ) } fn fetch_inlays( &mut self, multi_buffer: ModelHandle, - inlay_fetch_ranges: impl Iterator, - currently_shown_inlays: Vec<(Anchor, InlayId)>, + inlay_queries: impl Iterator, + mut currently_shown_inlay_hints: HashMap>>, replace_old: bool, cx: &mut ViewContext, ) -> Task> { - // TODO kb + let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx); + let inlay_queries_per_buffer = inlay_queries.fold( + HashMap::>::default(), + |mut queries, new_query| { + let mut buffer_queries = queries + .entry(new_query.buffer_id) + .or_insert_with(|| BufferInlays::new(new_query.buffer_version.clone())); + assert_eq!(buffer_queries.buffer_version, new_query.buffer_version); + let queries = buffer_queries + .excerpt_inlays + .entry(new_query.excerpt_id) + .or_default(); + // let z = multi_buffer_snapshot.anchor_in_excerpt(new_query.excerpt_id, text_anchor); + // .push(new_query); + // match queries + // .binary_search_by(|probe| inlay.position.cmp(&probe.0, &multi_buffer_snapshot)) + // { + // Ok(ix) | Err(ix) => { + // excerpt_hints.insert(ix, (inlay.position, inlay.id)); + // } + // } + // queries + todo!("TODO kb") + }, + ); + todo!("TODO kb") } diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 0a700879454931493bc16362669751163779abe8..d4298efacf1c4f5a051fd94422cbc62d90f2275d 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -2617,15 +2617,6 @@ impl MultiBufferSnapshot { } pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor { - self.find_anchor_in_excerpt(excerpt_id, text_anchor) - .unwrap_or_else(|| panic!("excerpt not found")) - } - - pub fn find_anchor_in_excerpt( - &self, - excerpt_id: ExcerptId, - text_anchor: text::Anchor, - ) -> Option { let locator = self.excerpt_locator_for_id(excerpt_id); let mut cursor = self.excerpts.cursor::>(); cursor.seek(locator, Bias::Left, &()); @@ -2633,15 +2624,15 @@ impl MultiBufferSnapshot { if excerpt.id == excerpt_id { let text_anchor = excerpt.clip_anchor(text_anchor); drop(cursor); - return Some(Anchor { + return Anchor { buffer_id: Some(excerpt.buffer_id), excerpt_id, text_anchor, - }); + }; } } - None + panic!("excerpt not found") } pub fn can_resolve(&self, anchor: &Anchor) -> bool { diff --git a/crates/editor/src/scroll.rs b/crates/editor/src/scroll.rs index e0ca2a1ebf534fc5c2e26ebdbc33c21cea8d52ed..b5343359b58fe7c346a0c35cb48d09460aaba560 100644 --- a/crates/editor/src/scroll.rs +++ b/crates/editor/src/scroll.rs @@ -18,7 +18,7 @@ use workspace::WorkspaceId; use crate::{ display_map::{DisplaySnapshot, ToDisplayPoint}, hover_popover::hide_hover, - inlay_cache::InlayRefreshReason, + inlay_hint_cache::InlayRefreshReason, persistence::DB, Anchor, DisplayPoint, Editor, EditorMode, Event, MultiBufferSnapshot, ToPoint, };