Co-authored-by: Kirill <kirill@zed.dev>
This commit is contained in:
Piotr Osiewicz 2023-11-02 10:59:10 +01:00
parent a9e3d8c9a4
commit 575e193ea3
3 changed files with 435 additions and 361 deletions

View file

@ -1555,6 +1555,106 @@ impl FormatProvider for ModelHandle<Formatter> {
} }
} }
trait InlayHints {
fn refresh_inlay_hints(
&mut self,
reason: InlayHintRefreshReason,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
);
fn is_enabled(&self, cx: &AppContext) -> bool;
}
impl InlayHints for ModelHandle<InlayHintCache> {
fn refresh_inlay_hints(
&mut self,
reason: InlayHintRefreshReason,
editor: &mut Editor,
cx: &mut ViewContext<Editor>,
) {
if editor.mode != EditorMode::Full {
return;
}
let cache_handle = self.clone();
self.update(cx, |this, cx| {
let reason_description = reason.description();
let (invalidate_cache, required_languages) = match reason {
InlayHintRefreshReason::Toggle(enabled) => {
this.enabled = enabled;
if enabled {
(InvalidationStrategy::RefreshRequested, None)
} else {
this.clear();
editor.splice_inlay_hints(
editor
.visible_inlay_hints(cx)
.iter()
.map(|inlay| inlay.id)
.collect(),
Vec::new(),
cx,
);
return;
}
}
InlayHintRefreshReason::SettingsChange(new_settings) => {
match this.update_settings(
&editor.buffer,
new_settings,
editor.visible_inlay_hints(cx),
cx,
) {
ControlFlow::Break(Some(InlaySplice {
to_remove,
to_insert,
})) => {
editor.splice_inlay_hints(to_remove, to_insert, cx);
return;
}
ControlFlow::Break(None) => return,
ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
}
}
InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
if let Some(InlaySplice {
to_remove,
to_insert,
}) = this.remove_excerpts(excerpts_removed)
{
editor.splice_inlay_hints(to_remove, to_insert, cx);
}
return;
}
InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
InlayHintRefreshReason::BufferEdited(buffer_languages) => {
(InvalidationStrategy::BufferEdited, Some(buffer_languages))
}
InlayHintRefreshReason::RefreshRequested => {
(InvalidationStrategy::RefreshRequested, None)
}
};
if let Some(InlaySplice {
to_remove,
to_insert,
}) = this.spawn_hint_refresh(
reason_description,
editor.excerpt_visible_offsets(required_languages.as_ref(), cx),
invalidate_cache,
cache_handle,
cx,
) {
editor.splice_inlay_hints(to_remove, to_insert, cx);
}
});
}
fn is_enabled(&self, cx: &AppContext) -> bool {
self.read(cx).enabled
}
}
pub struct Editor { pub struct Editor {
handle: WeakViewHandle<Self>, handle: WeakViewHandle<Self>,
buffer: ModelHandle<MultiBuffer>, buffer: ModelHandle<MultiBuffer>,
@ -1604,7 +1704,6 @@ pub struct Editor {
gutter_hovered: bool, gutter_hovered: bool,
link_go_to_definition_state: LinkGoToDefinitionState, link_go_to_definition_state: LinkGoToDefinitionState,
copilot_state: CopilotState, copilot_state: CopilotState,
inlay_hint_cache: InlayHintCache,
next_inlay_id: usize, next_inlay_id: usize,
_subscriptions: Vec<Subscription>, _subscriptions: Vec<Subscription>,
pixel_position_of_newest_cursor: Option<Vector2F>, pixel_position_of_newest_cursor: Option<Vector2F>,
@ -1613,6 +1712,7 @@ pub struct Editor {
go_to_definition_provider: Option<Box<dyn GoToDefinitionProvider>>, go_to_definition_provider: Option<Box<dyn GoToDefinitionProvider>>,
rename_provider: Option<Box<dyn RenameProvider>>, rename_provider: Option<Box<dyn RenameProvider>>,
format_provider: Option<Box<dyn FormatProvider>>, format_provider: Option<Box<dyn FormatProvider>>,
inlay_hints: Option<ModelHandle<InlayHintCache>>,
} }
pub struct EditorSnapshot { pub struct EditorSnapshot {
@ -2881,7 +2981,6 @@ impl Editor {
hover_state: Default::default(), hover_state: Default::default(),
link_go_to_definition_state: Default::default(), link_go_to_definition_state: Default::default(),
copilot_state: Default::default(), copilot_state: Default::default(),
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false, gutter_hovered: false,
pixel_position_of_newest_cursor: None, pixel_position_of_newest_cursor: None,
_subscriptions: vec![ _subscriptions: vec![
@ -2906,6 +3005,9 @@ impl Editor {
go_to_definition_provider, go_to_definition_provider,
rename_provider, rename_provider,
format_provider, format_provider,
inlay_hints: project
.clone()
.map(|project, _| InlayHintCache::new(project, inlay_hint_settings)),
}; };
this._subscriptions.extend(project_subscriptions); this._subscriptions.extend(project_subscriptions);
@ -4255,87 +4357,23 @@ impl Editor {
} }
pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) { pub fn toggle_inlay_hints(&mut self, _: &ToggleInlayHints, cx: &mut ViewContext<Self>) {
self.refresh_inlay_hints( if let Some(enabled) = self.inlay_hints.map(|hints| hints.is_enabled(cx)) {
InlayHintRefreshReason::Toggle(!self.inlay_hint_cache.enabled), self.refresh_inlay_hints(InlayHintRefreshReason::Toggle(!enabled), cx);
cx, }
);
} }
pub fn inlay_hints_enabled(&self) -> bool { pub fn inlay_hints_enabled(&self) -> bool {
self.inlay_hint_cache.enabled self.inlay_hints
.map(|hints| hints.is_enabled(cx))
.unwrap_or_default()
} }
fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) { fn refresh_inlay_hints(&mut self, reason: InlayHintRefreshReason, cx: &mut ViewContext<Self>) {
if self.project.is_none() || self.mode != EditorMode::Full { if let Some(inlay_hints) = &self.inlay_hints {
return; cx.spawn(|editor, cx| {
} inlay_hints.refresh_inlay_hints(reason, editor, cx);
})
let reason_description = reason.description(); .detach()
let (invalidate_cache, required_languages) = match reason {
InlayHintRefreshReason::Toggle(enabled) => {
self.inlay_hint_cache.enabled = enabled;
if enabled {
(InvalidationStrategy::RefreshRequested, None)
} else {
self.inlay_hint_cache.clear();
self.splice_inlay_hints(
self.visible_inlay_hints(cx)
.iter()
.map(|inlay| inlay.id)
.collect(),
Vec::new(),
cx,
);
return;
}
}
InlayHintRefreshReason::SettingsChange(new_settings) => {
match self.inlay_hint_cache.update_settings(
&self.buffer,
new_settings,
self.visible_inlay_hints(cx),
cx,
) {
ControlFlow::Break(Some(InlaySplice {
to_remove,
to_insert,
})) => {
self.splice_inlay_hints(to_remove, to_insert, cx);
return;
}
ControlFlow::Break(None) => return,
ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
}
}
InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
if let Some(InlaySplice {
to_remove,
to_insert,
}) = self.inlay_hint_cache.remove_excerpts(excerpts_removed)
{
self.splice_inlay_hints(to_remove, to_insert, cx);
}
return;
}
InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None),
InlayHintRefreshReason::BufferEdited(buffer_languages) => {
(InvalidationStrategy::BufferEdited, Some(buffer_languages))
}
InlayHintRefreshReason::RefreshRequested => {
(InvalidationStrategy::RefreshRequested, None)
}
};
if let Some(InlaySplice {
to_remove,
to_insert,
}) = self.inlay_hint_cache.spawn_hint_refresh(
reason_description,
self.excerpt_visible_offsets(required_languages.as_ref(), cx),
invalidate_cache,
cx,
) {
self.splice_inlay_hints(to_remove, to_insert, cx);
} }
} }
@ -9353,10 +9391,6 @@ impl Editor {
cx.write_to_clipboard(ClipboardItem::new(lines)); cx.write_to_clipboard(ClipboardItem::new(lines));
} }
pub fn inlay_hint_cache(&self) -> &InlayHintCache {
&self.inlay_hint_cache
}
pub fn replay_insert_event( pub fn replay_insert_event(
&mut self, &mut self,
text: &str, text: &str,
@ -9414,6 +9448,24 @@ impl Editor {
}); });
supports supports
} }
#[cfg(any(test, feature = "test-support"))]
fn inlay_hint_cache_version(&self, cx: &mut AppContext) -> usize {
self.inlay_hints
.expect("test code does not have hints enabled")
.update(cx, |hints, _| hints.version)
}
#[cfg(any(test, feature = "test-support"))]
fn inlay_hint_cache_entries(
&self,
excerpt_id: ExcerptId,
cx: &mut AppContext,
) -> Option<&Arc<RwLock<CachedExcerptHints>>> {
self.inlay_hints
.expect("test code does not have hints enabled")
.update(cx, |hints, _| hints.hints.get(&excerpt_id).cloned())
}
} }
pub trait CollaborationHub { pub trait CollaborationHub {
@ -9424,6 +9476,8 @@ pub trait CollaborationHub {
) -> &'a HashMap<u64, ParticipantIndex>; ) -> &'a HashMap<u64, ParticipantIndex>;
} }
// Option<ModelHandle<Project>>
// Option<Box<dyn LspBridge>>
impl CollaborationHub for ModelHandle<Project> { impl CollaborationHub for ModelHandle<Project> {
fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> { fn collaborators<'a>(&self, cx: &'a AppContext) -> &'a HashMap<PeerId, Collaborator> {
self.read(cx).collaborators() self.read(cx).collaborators()

View file

@ -11,10 +11,10 @@ use crate::{
use anyhow::Context; use anyhow::Context;
use clock::Global; use clock::Global;
use futures::future; use futures::future;
use gpui::{ModelContext, ModelHandle, Task, ViewContext}; use gpui::{Entity, ModelContext, ModelHandle, Task, ViewContext};
use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot}; use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
use parking_lot::RwLock; use parking_lot::RwLock;
use project::{InlayHint, ResolveState}; use project::{InlayHint, Project, ResolveState};
use collections::{hash_map, HashMap, HashSet}; use collections::{hash_map, HashMap, HashSet};
use language::language_settings::InlayHintSettings; use language::language_settings::InlayHintSettings;
@ -24,9 +24,11 @@ use text::{ToOffset, ToPoint};
use util::post_inc; use util::post_inc;
pub struct InlayHintCache { pub struct InlayHintCache {
hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>, // TODO kb consider weak handles
project: ModelHandle<Project>,
pub(super) hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
allowed_hint_kinds: HashSet<Option<InlayHintKind>>, allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
version: usize, pub(super) version: usize,
pub(super) enabled: bool, pub(super) enabled: bool,
update_tasks: HashMap<ExcerptId, TasksForRanges>, update_tasks: HashMap<ExcerptId, TasksForRanges>,
lsp_request_limiter: Arc<Semaphore>, lsp_request_limiter: Arc<Semaphore>,
@ -236,9 +238,13 @@ impl TasksForRanges {
} }
} }
impl Entity for InlayHintCache {
type Event = ();
}
impl InlayHintCache { impl InlayHintCache {
pub fn new(inlay_hint_settings: InlayHintSettings) -> Self { pub fn new(project: ModelHandle<Project>, inlay_hint_settings: InlayHintSettings) -> Self {
Self { Self {
project,
allowed_hint_kinds: inlay_hint_settings.enabled_inlay_hint_kinds(), allowed_hint_kinds: inlay_hint_settings.enabled_inlay_hint_kinds(),
enabled: inlay_hint_settings.enabled, enabled: inlay_hint_settings.enabled,
hints: HashMap::default(), hints: HashMap::default(),
@ -304,6 +310,7 @@ impl InlayHintCache {
reason: &'static str, reason: &'static str,
excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>, excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
invalidate: InvalidationStrategy, invalidate: InvalidationStrategy,
cache: ModelHandle<Self>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Option<InlaySplice> { ) -> Option<InlaySplice> {
if !self.enabled { if !self.enabled {
@ -336,6 +343,7 @@ impl InlayHintCache {
excerpts_to_query, excerpts_to_query,
invalidate, invalidate,
cache_version, cache_version,
cache,
cx, cx,
) )
}) })
@ -538,7 +546,7 @@ impl InlayHintCache {
.read(cx) .read(cx)
.buffer(buffer_id) .buffer(buffer_id)
.and_then(|buffer| { .and_then(|buffer| {
let project = editor.project.as_ref()?; let project = self.project;
Some(project.update(cx, |project, cx| { Some(project.update(cx, |project, cx| {
project.resolve_inlay_hint( project.resolve_inlay_hint(
hint_to_resolve, hint_to_resolve,
@ -582,6 +590,7 @@ fn spawn_new_update_tasks(
excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>, excerpts_to_query: HashMap<ExcerptId, (ModelHandle<Buffer>, Global, Range<usize>)>,
invalidate: InvalidationStrategy, invalidate: InvalidationStrategy,
update_cache_version: usize, update_cache_version: usize,
cache: ModelHandle<InlayHintCache>,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, '_, Editor>,
) { ) {
let visible_hints = Arc::new(editor.visible_inlay_hints(cx)); let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
@ -601,7 +610,8 @@ fn spawn_new_update_tasks(
continue; continue;
} }
let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned(); let cached_excerpt_hints =
cache.update(cx, |cache, _| cache.hints.get(&excerpt_id).cloned());
if let Some(cached_excerpt_hints) = &cached_excerpt_hints { if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
let cached_excerpt_hints = cached_excerpt_hints.read(); let cached_excerpt_hints = cached_excerpt_hints.read();
let cached_buffer_version = &cached_excerpt_hints.buffer_version; let cached_buffer_version = &cached_excerpt_hints.buffer_version;
@ -636,35 +646,39 @@ fn spawn_new_update_tasks(
reason, reason,
}; };
let new_update_task = |query_ranges| { let cache_handle = cache.clone();
new_update_task( cache.update(cx, |cache, _| {
query, let new_update_task = |query_ranges| {
query_ranges, new_update_task(
multi_buffer_snapshot, query,
buffer_snapshot.clone(),
Arc::clone(&visible_hints),
cached_excerpt_hints,
Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
cx,
)
};
match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
hash_map::Entry::Occupied(mut o) => {
o.get_mut().update_cached_tasks(
&buffer_snapshot,
query_ranges, query_ranges,
invalidate, multi_buffer_snapshot,
new_update_task, buffer_snapshot.clone(),
); Arc::clone(&visible_hints),
cached_excerpt_hints,
Arc::clone(&cache.lsp_request_limiter),
cache_handle,
cx,
)
};
match cache.update_tasks.entry(excerpt_id) {
hash_map::Entry::Occupied(mut o) => {
o.get_mut().update_cached_tasks(
&buffer_snapshot,
query_ranges,
invalidate,
new_update_task,
);
}
hash_map::Entry::Vacant(v) => {
v.insert(TasksForRanges::new(
query_ranges.clone(),
new_update_task(query_ranges),
));
}
} }
hash_map::Entry::Vacant(v) => { })
v.insert(TasksForRanges::new(
query_ranges.clone(),
new_update_task(query_ranges),
));
}
}
} }
} }
@ -760,6 +774,7 @@ fn new_update_task(
visible_hints: Arc<Vec<Inlay>>, visible_hints: Arc<Vec<Inlay>>,
cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>, cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
lsp_request_limiter: Arc<Semaphore>, lsp_request_limiter: Arc<Semaphore>,
cache: ModelHandle<InlayHintCache>,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, '_, Editor>,
) -> Task<()> { ) -> Task<()> {
cx.spawn(|editor, mut cx| async move { cx.spawn(|editor, mut cx| async move {
@ -775,6 +790,7 @@ fn new_update_task(
invalidate, invalidate,
range, range,
Arc::clone(&lsp_request_limiter), Arc::clone(&lsp_request_limiter),
cache,
closure_cx.clone(), closure_cx.clone(),
) )
}; };
@ -795,13 +811,9 @@ fn new_update_task(
let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| { let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
log::error!("inlay hint update task for range {range:?} failed: {e:#}"); log::error!("inlay hint update task for range {range:?} failed: {e:#}");
editor cache
.update(&mut cx, |editor, _| { .update(&mut cx, |cache, _| {
if let Some(task_ranges) = editor if let Some(task_ranges) = cache.update_tasks.get_mut(&query.excerpt_id) {
.inlay_hint_cache
.update_tasks
.get_mut(&query.excerpt_id)
{
task_ranges.invalidate_range(&buffer_snapshot, &range); task_ranges.invalidate_range(&buffer_snapshot, &range);
} }
}) })
@ -846,6 +858,7 @@ async fn fetch_and_update_hints(
invalidate: bool, invalidate: bool,
fetch_range: Range<language::Anchor>, fetch_range: Range<language::Anchor>,
lsp_request_limiter: Arc<Semaphore>, lsp_request_limiter: Arc<Semaphore>,
cache: ModelHandle<InlayHintCache>,
mut cx: gpui::AsyncAppContext, mut cx: gpui::AsyncAppContext,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() { let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
@ -880,13 +893,14 @@ async fn fetch_and_update_hints(
}; };
if query_not_around_visible_range { if query_not_around_visible_range {
log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping."); log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
if let Some(task_ranges) = editor cache.update(cx, |cache, _| {
.inlay_hint_cache if let Some(task_ranges) = cache
.update_tasks .update_tasks
.get_mut(&query.excerpt_id) .get_mut(&query.excerpt_id)
{ {
task_ranges.invalidate_range(&buffer_snapshot, &fetch_range); task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
} }
});
return None; return None;
} }
} }
@ -895,7 +909,7 @@ async fn fetch_and_update_hints(
.read(cx) .read(cx)
.buffer(query.buffer_id) .buffer(query.buffer_id)
.and_then(|buffer| { .and_then(|buffer| {
let project = editor.project.as_ref()?; let project = cache.read(cx).project;
Some(project.update(cx, |project, cx| { Some(project.update(cx, |project, cx| {
project.inlay_hints(buffer, fetch_range.clone(), cx) project.inlay_hints(buffer, fetch_range.clone(), cx)
})) }))
@ -957,6 +971,7 @@ async fn fetch_and_update_hints(
invalidate, invalidate,
buffer_snapshot, buffer_snapshot,
multi_buffer_snapshot, multi_buffer_snapshot,
cache,
cx, cx,
); );
}) })
@ -1071,13 +1086,11 @@ fn apply_hint_update(
invalidate: bool, invalidate: bool,
buffer_snapshot: BufferSnapshot, buffer_snapshot: BufferSnapshot,
multi_buffer_snapshot: MultiBufferSnapshot, multi_buffer_snapshot: MultiBufferSnapshot,
cache: ModelHandle<InlayHintsCache>,
cx: &mut ViewContext<'_, '_, Editor>, cx: &mut ViewContext<'_, '_, Editor>,
) { ) {
let cached_excerpt_hints = editor cache.update(cx, |cache, cx| {
.inlay_hint_cache let cached_excerpt_hints = cache.hints.entry(new_update.excerpt_id).or_insert_with(|| {
.hints
.entry(new_update.excerpt_id)
.or_insert_with(|| {
Arc::new(RwLock::new(CachedExcerptHints { Arc::new(RwLock::new(CachedExcerptHints {
version: query.cache_version, version: query.cache_version,
buffer_version: buffer_snapshot.version().clone(), buffer_version: buffer_snapshot.version().clone(),
@ -1086,112 +1099,111 @@ fn apply_hint_update(
hints_by_id: HashMap::default(), hints_by_id: HashMap::default(),
})) }))
}); });
let mut cached_excerpt_hints = cached_excerpt_hints.write(); let mut cached_excerpt_hints = cached_excerpt_hints.write();
match query.cache_version.cmp(&cached_excerpt_hints.version) { match query.cache_version.cmp(&cached_excerpt_hints.version) {
cmp::Ordering::Less => return, cmp::Ordering::Less => return,
cmp::Ordering::Greater | cmp::Ordering::Equal => { cmp::Ordering::Greater | cmp::Ordering::Equal => {
cached_excerpt_hints.version = query.cache_version; cached_excerpt_hints.version = query.cache_version;
}
} }
}
let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty(); let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
cached_excerpt_hints cached_excerpt_hints
.ordered_hints
.retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
cached_excerpt_hints
.hints_by_id
.retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
let mut splice = InlaySplice {
to_remove: new_update.remove_from_visible,
to_insert: Vec::new(),
};
for new_hint in new_update.add_to_cache {
let insert_position = match cached_excerpt_hints
.ordered_hints .ordered_hints
.binary_search_by(|probe| { .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
cached_excerpt_hints.hints_by_id[probe] cached_excerpt_hints
.position .hints_by_id
.cmp(&new_hint.position, &buffer_snapshot) .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
}) { let mut splice = InlaySplice {
Ok(i) => { to_remove: new_update.remove_from_visible,
let mut insert_position = Some(i); to_insert: Vec::new(),
for id in &cached_excerpt_hints.ordered_hints[i..] {
let cached_hint = &cached_excerpt_hints.hints_by_id[id];
if new_hint
.position
.cmp(&cached_hint.position, &buffer_snapshot)
.is_gt()
{
break;
}
if cached_hint.text() == new_hint.text() {
insert_position = None;
break;
}
}
insert_position
}
Err(i) => Some(i),
}; };
for new_hint in new_update.add_to_cache {
let insert_position =
match cached_excerpt_hints
.ordered_hints
.binary_search_by(|probe| {
cached_excerpt_hints.hints_by_id[probe]
.position
.cmp(&new_hint.position, &buffer_snapshot)
}) {
Ok(i) => {
let mut insert_position = Some(i);
for id in &cached_excerpt_hints.ordered_hints[i..] {
let cached_hint = &cached_excerpt_hints.hints_by_id[id];
if new_hint
.position
.cmp(&cached_hint.position, &buffer_snapshot)
.is_gt()
{
break;
}
if cached_hint.text() == new_hint.text() {
insert_position = None;
break;
}
}
insert_position
}
Err(i) => Some(i),
};
if let Some(insert_position) = insert_position { if let Some(insert_position) = insert_position {
let new_inlay_id = post_inc(&mut editor.next_inlay_id); let new_inlay_id = post_inc(&mut editor.next_inlay_id);
if editor if cache.allowed_hint_kinds.contains(&new_hint.kind) {
.inlay_hint_cache let new_hint_position = multi_buffer_snapshot
.allowed_hint_kinds .anchor_in_excerpt(query.excerpt_id, new_hint.position);
.contains(&new_hint.kind) splice
{ .to_insert
let new_hint_position = .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position); }
splice let new_id = InlayId::Hint(new_inlay_id);
.to_insert cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
.push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint)); cached_excerpt_hints
} .ordered_hints
let new_id = InlayId::Hint(new_inlay_id); .insert(insert_position, new_id);
cached_excerpt_hints.hints_by_id.insert(new_id, new_hint); cached_inlays_changed = true;
cached_excerpt_hints
.ordered_hints
.insert(insert_position, new_id);
cached_inlays_changed = true;
}
}
cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
drop(cached_excerpt_hints);
if invalidate {
let mut outdated_excerpt_caches = HashSet::default();
for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
let excerpt_hints = excerpt_hints.read();
if excerpt_hints.buffer_id == query.buffer_id
&& excerpt_id != &query.excerpt_id
&& buffer_snapshot
.version()
.changed_since(&excerpt_hints.buffer_version)
{
outdated_excerpt_caches.insert(*excerpt_id);
splice
.to_remove
.extend(excerpt_hints.ordered_hints.iter().copied());
} }
} }
cached_inlays_changed |= !outdated_excerpt_caches.is_empty(); cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
editor drop(cached_excerpt_hints);
.inlay_hint_cache });
.hints
.retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
}
let InlaySplice { cache.update(cx, |cache, _| {
to_remove, if invalidate {
to_insert, let mut outdated_excerpt_caches = HashSet::default();
} = splice; for (excerpt_id, excerpt_hints) in &cache.hints {
let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty(); let excerpt_hints = excerpt_hints.read();
if cached_inlays_changed || displayed_inlays_changed { if excerpt_hints.buffer_id == query.buffer_id
editor.inlay_hint_cache.version += 1; && excerpt_id != &query.excerpt_id
} && buffer_snapshot
if displayed_inlays_changed { .version()
editor.splice_inlay_hints(to_remove, to_insert, cx) .changed_since(&excerpt_hints.buffer_version)
} {
outdated_excerpt_caches.insert(*excerpt_id);
splice
.to_remove
.extend(excerpt_hints.ordered_hints.iter().copied());
}
}
cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
cache
.hints
.retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
}
let InlaySplice {
to_remove,
to_insert,
} = splice;
let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
if cached_inlays_changed || displayed_inlays_changed {
cache.version += 1;
}
if displayed_inlays_changed {
editor.splice_inlay_hints(to_remove, to_insert, cx)
}
})
} }
#[cfg(test)] #[cfg(test)]
@ -1287,7 +1299,8 @@ pub mod tests {
"Cache should use editor settings to get the allowed hint kinds" "Cache should use editor settings to get the allowed hint kinds"
); );
assert_eq!( assert_eq!(
inlay_cache.version, edits_made, editor.inlay_hint_cache_version(),
edits_made,
"The editor update the cache version after every cache/view change" "The editor update the cache version after every cache/view change"
); );
}); });
@ -1312,7 +1325,8 @@ pub mod tests {
"Cache should use editor settings to get the allowed hint kinds" "Cache should use editor settings to get the allowed hint kinds"
); );
assert_eq!( assert_eq!(
inlay_cache.version, edits_made, editor.inlay_hint_cache_version(),
edits_made,
"The editor update the cache version after every cache/view change" "The editor update the cache version after every cache/view change"
); );
}); });
@ -1392,7 +1406,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
edits_made, edits_made,
"The editor update the cache version after every cache/view change" "The editor update the cache version after every cache/view change"
); );
@ -1423,7 +1437,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
edits_made, edits_made,
"Should not update the cache while the work task is running" "Should not update the cache while the work task is running"
); );
@ -1447,7 +1461,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
edits_made, edits_made,
"Cache version should udpate once after the work task is done" "Cache version should udpate once after the work task is done"
); );
@ -1566,7 +1580,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
1, 1,
"Rust editor update the cache version after every cache/view change" "Rust editor update the cache version after every cache/view change"
); );
@ -1623,7 +1637,7 @@ pub mod tests {
"Markdown editor should have a separate verison, repeating Rust editor rules" "Markdown editor should have a separate verison, repeating Rust editor rules"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 1); assert_eq!(editor.inlay_hint_cache_version(), 1);
}); });
rs_editor.update(cx, |editor, cx| { rs_editor.update(cx, |editor, cx| {
@ -1640,7 +1654,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
2, 2,
"Every time hint cache changes, cache version should be incremented" "Every time hint cache changes, cache version should be incremented"
); );
@ -1653,7 +1667,7 @@ pub mod tests {
"Markdown editor should not be affected by Rust editor changes" "Markdown editor should not be affected by Rust editor changes"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 1); assert_eq!(editor.inlay_hint_cache_version(), 1);
}); });
md_editor.update(cx, |editor, cx| { md_editor.update(cx, |editor, cx| {
@ -1669,7 +1683,7 @@ pub mod tests {
"Rust editor should not be affected by Markdown editor changes" "Rust editor should not be affected by Markdown editor changes"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 2); assert_eq!(editor.inlay_hint_cache_version(), 2);
}); });
rs_editor.update(cx, |editor, cx| { rs_editor.update(cx, |editor, cx| {
let expected_hints = vec!["1".to_string()]; let expected_hints = vec!["1".to_string()];
@ -1679,7 +1693,7 @@ pub mod tests {
"Markdown editor should also change independently" "Markdown editor should also change independently"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 2); assert_eq!(editor.inlay_hint_cache_version(), 2);
}); });
} }
@ -1801,7 +1815,7 @@ pub mod tests {
visible_hint_labels(editor, cx) visible_hint_labels(editor, cx)
); );
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
edits_made, edits_made,
"Should not update cache version due to new loaded hints being the same" "Should not update cache version due to new loaded hints being the same"
); );
@ -1936,7 +1950,7 @@ pub mod tests {
assert!(cached_hint_labels(editor).is_empty()); assert!(cached_hint_labels(editor).is_empty());
assert!(visible_hint_labels(editor, cx).is_empty()); assert!(visible_hint_labels(editor, cx).is_empty());
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, edits_made, editor.inlay_hint_cache_version(), edits_made,
"The editor should not update the cache version after /refresh query without updates" "The editor should not update the cache version after /refresh query without updates"
); );
}); });
@ -2007,7 +2021,7 @@ pub mod tests {
vec!["parameter hint".to_string()], vec!["parameter hint".to_string()],
visible_hint_labels(editor, cx), visible_hint_labels(editor, cx),
); );
assert_eq!(editor.inlay_hint_cache().version, edits_made); assert_eq!(editor.inlay_hint_cache_version(), edits_made);
}); });
} }
@ -2086,7 +2100,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, 1, editor.inlay_hint_cache_version(), 1,
"Only one update should be registered in the cache after all cancellations" "Only one update should be registered in the cache after all cancellations"
); );
}); });
@ -2131,7 +2145,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
2, 2,
"Should update the cache version once more, for the new change" "Should update the cache version once more, for the new change"
); );
@ -2301,7 +2315,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range"); assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, requests_count, editor.inlay_hint_cache_version(), requests_count,
"LSP queries should've bumped the cache version" "LSP queries should've bumped the cache version"
); );
}); });
@ -2363,7 +2377,7 @@ pub mod tests {
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
lsp_requests, lsp_requests,
"Should update the cache for every LSP response with hints added" "Should update the cache for every LSP response with hints added"
); );
@ -2427,7 +2441,7 @@ pub mod tests {
assert_eq!(expected_hints, cached_hint_labels(editor), assert_eq!(expected_hints, cached_hint_labels(editor),
"Should have hints from the new LSP response after the edit"); "Should have hints from the new LSP response after the edit");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added"); assert_eq!(editor.inlay_hint_cache_version(), lsp_requests, "Should update the cache for every LSP response with hints added");
}); });
} }
@ -2650,7 +2664,7 @@ pub mod tests {
"When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints" "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison"); assert_eq!(editor.inlay_hint_cache_version(), expected_hints.len(), "Every visible excerpt hints should bump the verison");
}); });
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -2680,7 +2694,7 @@ pub mod tests {
assert_eq!(expected_hints, cached_hint_labels(editor), assert_eq!(expected_hints, cached_hint_labels(editor),
"With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits"); "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), assert_eq!(editor.inlay_hint_cache_version(), expected_hints.len(),
"Due to every excerpt having one hint, we update cache per new excerpt scrolled"); "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
}); });
@ -2711,7 +2725,7 @@ pub mod tests {
assert_eq!(expected_hints, cached_hint_labels(editor), assert_eq!(expected_hints, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, all hints for all excerpts should be fetched"); "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, expected_hints.len()); assert_eq!(editor.inlay_hint_cache_version(), expected_hints.len());
expected_hints.len() expected_hints.len()
}); });
@ -2739,7 +2753,7 @@ pub mod tests {
assert_eq!(expected_hints, cached_hint_labels(editor), assert_eq!(expected_hints, cached_hint_labels(editor),
"After multibuffer was scrolled to the end, further scrolls up should not bring more hints"); "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer"); assert_eq!(editor.inlay_hint_cache_version(), last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
}); });
editor_edited.store(true, Ordering::Release); editor_edited.store(true, Ordering::Release);
@ -2769,7 +2783,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
let current_cache_version = editor.inlay_hint_cache().version; let current_cache_version = editor.inlay_hint_cache_version();
let minimum_expected_version = last_scroll_update_version + expected_hints.len(); let minimum_expected_version = last_scroll_update_version + expected_hints.len();
assert!( assert!(
current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1, current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
@ -2953,7 +2967,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"All hints are disabled and should not be shown despite being present in the cache" "All hints are disabled and should not be shown despite being present in the cache"
); );
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
2, 2,
"Cache should update once per excerpt query" "Cache should update once per excerpt query"
); );
@ -2976,7 +2990,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"All hints are disabled and should not be shown despite being present in the cache" "All hints are disabled and should not be shown despite being present in the cache"
); );
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
3, 3,
"Excerpt removal should trigger a cache update" "Excerpt removal should trigger a cache update"
); );
@ -3004,7 +3018,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"Settings change should make cached hints visible" "Settings change should make cached hints visible"
); );
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
4, 4,
"Settings change should trigger a cache update" "Settings change should trigger a cache update"
); );
@ -3114,7 +3128,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
let expected_hints = vec!["1".to_string()]; let expected_hints = vec!["1".to_string()];
assert_eq!(expected_hints, cached_hint_labels(editor)); assert_eq!(expected_hints, cached_hint_labels(editor));
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 1); assert_eq!(editor.inlay_hint_cache_version(), 1);
}); });
} }
@ -3171,7 +3185,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!( assert_eq!(
editor.inlay_hint_cache().version, editor.inlay_hint_cache_version(),
1, 1,
"First toggle should be cache's first update" "First toggle should be cache's first update"
); );
@ -3187,7 +3201,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"Should clear hints after 2nd toggle" "Should clear hints after 2nd toggle"
); );
assert!(visible_hint_labels(editor, cx).is_empty()); assert!(visible_hint_labels(editor, cx).is_empty());
assert_eq!(editor.inlay_hint_cache().version, 2); assert_eq!(editor.inlay_hint_cache_version(), 2);
}); });
update_test_language_settings(cx, |settings| { update_test_language_settings(cx, |settings| {
@ -3207,7 +3221,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"Should query LSP hints for the 2nd time after enabling hints in settings" "Should query LSP hints for the 2nd time after enabling hints in settings"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 3); assert_eq!(editor.inlay_hint_cache_version(), 3);
}); });
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -3220,7 +3234,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"Should clear hints after enabling in settings and a 3rd toggle" "Should clear hints after enabling in settings and a 3rd toggle"
); );
assert!(visible_hint_labels(editor, cx).is_empty()); assert!(visible_hint_labels(editor, cx).is_empty());
assert_eq!(editor.inlay_hint_cache().version, 4); assert_eq!(editor.inlay_hint_cache_version(), 4);
}); });
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
@ -3235,7 +3249,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
"Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on" "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
); );
assert_eq!(expected_hints, visible_hint_labels(editor, cx)); assert_eq!(expected_hints, visible_hint_labels(editor, cx));
assert_eq!(editor.inlay_hint_cache().version, 5); assert_eq!(editor.inlay_hint_cache_version(), 5);
}); });
} }
@ -3318,7 +3332,7 @@ all hints should be invalidated and requeried for all of its visible excerpts"
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
assert!(cached_hint_labels(editor).is_empty()); assert!(cached_hint_labels(editor).is_empty());
assert!(visible_hint_labels(editor, cx).is_empty()); assert!(visible_hint_labels(editor, cx).is_empty());
assert_eq!(editor.inlay_hint_cache().version, 0); assert_eq!(editor.inlay_hint_cache_version(), 0);
}); });
("/a/main.rs", editor, fake_server) ("/a/main.rs", editor, fake_server)

View file

@ -178,6 +178,9 @@ pub fn update_inlay_link_and_hover_points(
let mut go_to_definition_updated = false; let mut go_to_definition_updated = false;
let mut hover_updated = false; let mut hover_updated = false;
if let Some(hovered_offset) = hovered_offset { if let Some(hovered_offset) = hovered_offset {
let Some(inlay_hint_cache) = editor.inlay_hints.clone() else {
return;
};
let buffer_snapshot = editor.buffer().read(cx).snapshot(cx); let buffer_snapshot = editor.buffer().read(cx).snapshot(cx);
let previous_valid_anchor = buffer_snapshot.anchor_at( let previous_valid_anchor = buffer_snapshot.anchor_at(
point_for_position.previous_valid.to_point(snapshot), point_for_position.previous_valid.to_point(snapshot),
@ -202,82 +205,84 @@ pub fn update_inlay_link_and_hover_points(
}) })
.max_by_key(|hint| hint.id) .max_by_key(|hint| hint.id)
{ {
let inlay_hint_cache = editor.inlay_hint_cache();
let excerpt_id = previous_valid_anchor.excerpt_id; let excerpt_id = previous_valid_anchor.excerpt_id;
if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id) { inlay_hint_cache.update(cx, |inlay_hint_cache, cx| {
match cached_hint.resolve_state { if let Some(cached_hint) = inlay_hint_cache.hint_by_id(excerpt_id, hovered_hint.id)
ResolveState::CanResolve(_, _) => { {
if let Some(buffer_id) = previous_valid_anchor.buffer_id { match cached_hint.resolve_state {
inlay_hint_cache.spawn_hint_resolve( ResolveState::CanResolve(_, _) => {
buffer_id, if let Some(buffer_id) = previous_valid_anchor.buffer_id {
excerpt_id, inlay_hint_cache.spawn_hint_resolve(
hovered_hint.id, buffer_id,
cx, excerpt_id,
); hovered_hint.id,
} cx,
} );
ResolveState::Resolved => {
let mut extra_shift_left = 0;
let mut extra_shift_right = 0;
if cached_hint.padding_left {
extra_shift_left += 1;
extra_shift_right += 1;
}
if cached_hint.padding_right {
extra_shift_right += 1;
}
match cached_hint.label {
project::InlayHintLabel::String(_) => {
if let Some(tooltip) = cached_hint.tooltip {
hover_popover::hover_at_inlay(
editor,
InlayHover {
excerpt: excerpt_id,
tooltip: match tooltip {
InlayHintTooltip::String(text) => HoverBlock {
text,
kind: HoverBlockKind::PlainText,
},
InlayHintTooltip::MarkupContent(content) => {
HoverBlock {
text: content.value,
kind: content.kind,
}
}
},
range: InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: extra_shift_left
..hovered_hint.text.len() + extra_shift_right,
},
},
cx,
);
hover_updated = true;
}
} }
project::InlayHintLabel::LabelParts(label_parts) => { }
let hint_start = ResolveState::Resolved => {
snapshot.anchor_to_inlay_offset(hovered_hint.position); let mut extra_shift_left = 0;
if let Some((hovered_hint_part, part_range)) = let mut extra_shift_right = 0;
hover_popover::find_hovered_hint_part( if cached_hint.padding_left {
label_parts, extra_shift_left += 1;
hint_start, extra_shift_right += 1;
hovered_offset, }
) if cached_hint.padding_right {
{ extra_shift_right += 1;
let highlight_start = }
(part_range.start - hint_start).0 + extra_shift_left; match cached_hint.label {
let highlight_end = project::InlayHintLabel::String(_) => {
(part_range.end - hint_start).0 + extra_shift_right; if let Some(tooltip) = cached_hint.tooltip {
let highlight = InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: highlight_start..highlight_end,
};
if let Some(tooltip) = hovered_hint_part.tooltip {
hover_popover::hover_at_inlay( hover_popover::hover_at_inlay(
editor,
InlayHover {
excerpt: excerpt_id,
tooltip: match tooltip {
InlayHintTooltip::String(text) => HoverBlock {
text,
kind: HoverBlockKind::PlainText,
},
InlayHintTooltip::MarkupContent(content) => {
HoverBlock {
text: content.value,
kind: content.kind,
}
}
},
range: InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: extra_shift_left
..hovered_hint.text.len()
+ extra_shift_right,
},
},
cx,
);
hover_updated = true;
}
}
project::InlayHintLabel::LabelParts(label_parts) => {
let hint_start =
snapshot.anchor_to_inlay_offset(hovered_hint.position);
if let Some((hovered_hint_part, part_range)) =
hover_popover::find_hovered_hint_part(
label_parts,
hint_start,
hovered_offset,
)
{
let highlight_start =
(part_range.start - hint_start).0 + extra_shift_left;
let highlight_end =
(part_range.end - hint_start).0 + extra_shift_right;
let highlight = InlayHighlight {
inlay: hovered_hint.id,
inlay_position: hovered_hint.position,
range: highlight_start..highlight_end,
};
if let Some(tooltip) = hovered_hint_part.tooltip {
hover_popover::hover_at_inlay(
editor, editor,
InlayHover { InlayHover {
excerpt: excerpt_id, excerpt: excerpt_id,
@ -299,31 +304,32 @@ pub fn update_inlay_link_and_hover_points(
}, },
cx, cx,
); );
hover_updated = true; hover_updated = true;
} }
if let Some((language_server_id, location)) = if let Some((language_server_id, location)) =
hovered_hint_part.location hovered_hint_part.location
{ {
go_to_definition_updated = true; go_to_definition_updated = true;
update_go_to_definition_link( update_go_to_definition_link(
editor, editor,
Some(GoToDefinitionTrigger::InlayHint( Some(GoToDefinitionTrigger::InlayHint(
highlight, highlight,
location, location,
language_server_id, language_server_id,
)), )),
cmd_held, cmd_held,
shift_held, shift_held,
cx, cx,
); );
}
} }
} }
} };
}; }
ResolveState::Resolving => {}
} }
ResolveState::Resolving => {}
} }
} })
} }
} }