Map the editor-frontend API
Co-authored-by: Lukas Wirth <lukas@zed.dev>
This commit is contained in:
parent
5cf3dcfdfd
commit
44b1d5db2d
2 changed files with 204 additions and 235 deletions
|
@ -150,18 +150,18 @@ use project::{
|
||||||
BreakpointWithPosition, CodeAction, Completion, CompletionIntent, CompletionResponse,
|
BreakpointWithPosition, CodeAction, Completion, CompletionIntent, CompletionResponse,
|
||||||
CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, Location, LocationLink,
|
CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, Location, LocationLink,
|
||||||
PrepareRenameResponse, Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
|
PrepareRenameResponse, Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind,
|
||||||
debugger::breakpoint_store::Breakpoint,
|
|
||||||
debugger::{
|
debugger::{
|
||||||
breakpoint_store::{
|
breakpoint_store::{
|
||||||
BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore,
|
Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState,
|
||||||
BreakpointStoreEvent,
|
BreakpointStore, BreakpointStoreEvent,
|
||||||
},
|
},
|
||||||
session::{Session, SessionEvent},
|
session::{Session, SessionEvent},
|
||||||
},
|
},
|
||||||
git_store::{GitStoreEvent, RepositoryEvent},
|
git_store::{GitStoreEvent, RepositoryEvent},
|
||||||
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
|
lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle},
|
||||||
project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter},
|
project_settings::{
|
||||||
project_settings::{GitGutterSetting, ProjectSettings},
|
DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
|
use rpc::{ErrorCode, ErrorExt, proto::PeerId};
|
||||||
|
@ -1189,10 +1189,9 @@ struct LspInlayHintData {
|
||||||
allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
|
allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
|
||||||
invalidate_debounce: Option<Duration>,
|
invalidate_debounce: Option<Duration>,
|
||||||
append_debounce: Option<Duration>,
|
append_debounce: Option<Duration>,
|
||||||
known_cache_version: Option<usize>,
|
inlays_for_version: Option<clock::Global>,
|
||||||
// TODO kb use these for removing all hints in splices instead of accessing display_map entries.
|
inlays: HashMap<BufferId, Vec<InlayId>>,
|
||||||
inlays: Vec<InlayId>,
|
inlay_tasks: HashMap<BufferId, HashMap<Range<BufferRow>, Task<()>>>,
|
||||||
inlay_tasks: HashMap<BufferId, BTreeMap<Range<BufferRow>, Task<()>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LspInlayHintData {
|
impl LspInlayHintData {
|
||||||
|
@ -1201,12 +1200,12 @@ impl LspInlayHintData {
|
||||||
modifiers_override: false,
|
modifiers_override: false,
|
||||||
enabled: settings.enabled,
|
enabled: settings.enabled,
|
||||||
enabled_in_settings: settings.enabled,
|
enabled_in_settings: settings.enabled,
|
||||||
inlays: Vec::new(),
|
inlays: HashMap::default(),
|
||||||
|
inlays_for_version: None,
|
||||||
inlay_tasks: HashMap::default(),
|
inlay_tasks: HashMap::default(),
|
||||||
invalidate_debounce: debounce_value(settings.edit_debounce_ms),
|
invalidate_debounce: debounce_value(settings.edit_debounce_ms),
|
||||||
append_debounce: debounce_value(settings.scroll_debounce_ms),
|
append_debounce: debounce_value(settings.scroll_debounce_ms),
|
||||||
allowed_hint_kinds: settings.enabled_inlay_hint_kinds(),
|
allowed_hint_kinds: settings.enabled_inlay_hint_kinds(),
|
||||||
known_cache_version: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1237,8 +1236,8 @@ impl LspInlayHintData {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
self.known_cache_version = None;
|
|
||||||
self.inlay_tasks.clear();
|
self.inlay_tasks.clear();
|
||||||
|
// TODO kb splice!?
|
||||||
self.inlays.clear();
|
self.inlays.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1277,16 +1276,6 @@ impl LspInlayHintData {
|
||||||
ControlFlow::Break(None)
|
ControlFlow::Break(None)
|
||||||
} else {
|
} else {
|
||||||
todo!("TODO kb")
|
todo!("TODO kb")
|
||||||
// let new_splice = self.new_allowed_hint_kinds_splice(
|
|
||||||
// multi_buffer,
|
|
||||||
// &visible_hints,
|
|
||||||
// &new_allowed_hint_kinds,
|
|
||||||
// cx,
|
|
||||||
// );
|
|
||||||
// if new_splice.is_some() {
|
|
||||||
// self.allowed_hint_kinds = new_allowed_hint_kinds;
|
|
||||||
// }
|
|
||||||
// ControlFlow::Break(new_splice)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
|
@ -1309,117 +1298,6 @@ impl LspInlayHintData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn new_allowed_hint_kinds_splice(
|
|
||||||
// &self,
|
|
||||||
// multi_buffer: &Entity<MultiBuffer>,
|
|
||||||
// visible_hints: &[Inlay],
|
|
||||||
// new_kinds: &HashSet<Option<InlayHintKind>>,
|
|
||||||
// cx: &mut App,
|
|
||||||
// ) -> Option<InlaySplice> {
|
|
||||||
// let old_kinds = &self.allowed_hint_kinds;
|
|
||||||
// if new_kinds == old_kinds {
|
|
||||||
// return None;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let mut to_remove = Vec::new();
|
|
||||||
// let mut to_insert = Vec::new();
|
|
||||||
// let mut shown_hints_to_remove = visible_hints.iter().fold(
|
|
||||||
// HashMap::<ExcerptId, Vec<(Anchor, InlayId)>>::default(),
|
|
||||||
// |mut current_hints, inlay| {
|
|
||||||
// current_hints
|
|
||||||
// .entry(inlay.position.excerpt_id)
|
|
||||||
// .or_default()
|
|
||||||
// .push((inlay.position, inlay.id));
|
|
||||||
// current_hints
|
|
||||||
// },
|
|
||||||
// );
|
|
||||||
|
|
||||||
// let multi_buffer = multi_buffer.read(cx);
|
|
||||||
// let multi_buffer_snapshot = multi_buffer.snapshot(cx);
|
|
||||||
|
|
||||||
// for (excerpt_id, excerpt_cached_hints) in &self.hints {
|
|
||||||
// let shown_excerpt_hints_to_remove =
|
|
||||||
// shown_hints_to_remove.entry(*excerpt_id).or_default();
|
|
||||||
// let excerpt_cached_hints = excerpt_cached_hints.read();
|
|
||||||
// let mut excerpt_cache = excerpt_cached_hints.ordered_hints.iter().fuse().peekable();
|
|
||||||
// shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
|
|
||||||
// let Some(buffer) = shown_anchor
|
|
||||||
// .buffer_id
|
|
||||||
// .and_then(|buffer_id| multi_buffer.buffer(buffer_id))
|
|
||||||
// else {
|
|
||||||
// return false;
|
|
||||||
// };
|
|
||||||
// let buffer_snapshot = buffer.read(cx).snapshot();
|
|
||||||
// loop {
|
|
||||||
// match excerpt_cache.peek() {
|
|
||||||
// Some(&cached_hint_id) => {
|
|
||||||
// let cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
|
|
||||||
// if cached_hint_id == shown_hint_id {
|
|
||||||
// excerpt_cache.next();
|
|
||||||
// return !new_kinds.contains(&cached_hint.kind);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// match cached_hint
|
|
||||||
// .position
|
|
||||||
// .cmp(&shown_anchor.text_anchor, &buffer_snapshot)
|
|
||||||
// {
|
|
||||||
// cmp::Ordering::Less | cmp::Ordering::Equal => {
|
|
||||||
// if !old_kinds.contains(&cached_hint.kind)
|
|
||||||
// && new_kinds.contains(&cached_hint.kind)
|
|
||||||
// {
|
|
||||||
// if let Some(anchor) = multi_buffer_snapshot
|
|
||||||
// .anchor_in_excerpt(*excerpt_id, cached_hint.position)
|
|
||||||
// {
|
|
||||||
// to_insert.push(Inlay::hint(
|
|
||||||
// cached_hint_id.id(),
|
|
||||||
// anchor,
|
|
||||||
// cached_hint,
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// excerpt_cache.next();
|
|
||||||
// }
|
|
||||||
// cmp::Ordering::Greater => return true,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// None => return true,
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// for cached_hint_id in excerpt_cache {
|
|
||||||
// let maybe_missed_cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
|
|
||||||
// let cached_hint_kind = maybe_missed_cached_hint.kind;
|
|
||||||
// if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) {
|
|
||||||
// if let Some(anchor) = multi_buffer_snapshot
|
|
||||||
// .anchor_in_excerpt(*excerpt_id, maybe_missed_cached_hint.position)
|
|
||||||
// {
|
|
||||||
// to_insert.push(Inlay::hint(
|
|
||||||
// cached_hint_id.id(),
|
|
||||||
// anchor,
|
|
||||||
// maybe_missed_cached_hint,
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// to_remove.extend(
|
|
||||||
// shown_hints_to_remove
|
|
||||||
// .into_values()
|
|
||||||
// .flatten()
|
|
||||||
// .map(|(_, hint_id)| hint_id),
|
|
||||||
// );
|
|
||||||
// if to_remove.is_empty() && to_insert.is_empty() {
|
|
||||||
// None
|
|
||||||
// } else {
|
|
||||||
// Some(InlaySplice {
|
|
||||||
// to_remove,
|
|
||||||
// to_insert,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debounce_value(debounce_ms: u64) -> Option<Duration> {
|
fn debounce_value(debounce_ms: u64) -> Option<Duration> {
|
||||||
|
@ -5504,14 +5382,12 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_inlay_hints_2(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
|
fn refresh_inlay_hints_2(&mut self, reason: InlayHintRefreshReason, cx: &mut Context<Self>) {
|
||||||
if !self.mode.is_full() {
|
if !self.mode.is_full() || self.semantics_provider.is_none() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let Some((inlay_hints, semantics_provider)) = self
|
|
||||||
.inlay_hints
|
{
|
||||||
.as_mut()
|
let Some(inlay_hints) = self.inlay_hints.as_mut() else {
|
||||||
.zip(self.semantics_provider.as_ref())
|
|
||||||
else {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5566,24 +5442,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InlayHintRefreshReason::SettingsChange(new_settings) => {
|
InlayHintRefreshReason::SettingsChange(new_settings) => {
|
||||||
// TODO kb
|
|
||||||
return;
|
return;
|
||||||
// match inlay_hints.update_settings(
|
|
||||||
// &self.buffer,
|
|
||||||
// new_settings,
|
|
||||||
// self.visible_inlay_hints(cx),
|
|
||||||
// cx,
|
|
||||||
// ) {
|
|
||||||
// ControlFlow::Break(Some(InlaySplice {
|
|
||||||
// to_remove,
|
|
||||||
// to_insert,
|
|
||||||
// })) => {
|
|
||||||
// self.splice_inlays(&to_remove, to_insert, cx);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// ControlFlow::Break(None) => return,
|
|
||||||
// ControlFlow::Continue(()) => (InvalidationStrategy::RefreshRequested, None),
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
|
InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => {
|
||||||
if let Some(InlaySplice {
|
if let Some(InlaySplice {
|
||||||
|
@ -5606,20 +5465,123 @@ impl Editor {
|
||||||
(InvalidationStrategy::RefreshRequested, None)
|
(InvalidationStrategy::RefreshRequested, None)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// TODO kb
|
let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx);
|
||||||
// if let Some(InlaySplice {
|
let Some(semantics_provider) = self.semantics_provider.clone() else {
|
||||||
// to_remove,
|
return;
|
||||||
// to_insert,
|
};
|
||||||
// }) = self.inlay_hint_cache.spawn_hint_refresh(
|
for (excerpt_id, (buffer, buffer_version, range)) in self.visible_excerpts(None, cx) {
|
||||||
// reason_description,
|
let Some(excerpt_text_anchor_range) =
|
||||||
// self.visible_excerpts(required_languages.as_ref(), cx),
|
multi_buffer_snapshot.context_range_for_excerpt(excerpt_id)
|
||||||
// invalidate_cache,
|
else {
|
||||||
// ignore_debounce,
|
continue;
|
||||||
// cx,
|
};
|
||||||
// ) {
|
let buffer_id = buffer.read(cx).remote_id();
|
||||||
// self.splice_inlays(&to_remove, to_insert, cx);
|
let buffer_snapshot = buffer.read(cx).snapshot();
|
||||||
// }
|
let buffer_anchor_range =
|
||||||
|
buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_after(range.end);
|
||||||
|
|
||||||
|
let new_hints =
|
||||||
|
semantics_provider.inlay_hints_2(buffer, buffer_anchor_range.clone(), cx);
|
||||||
|
let Some(inlay_hints) = self.inlay_hints.as_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some((hints_range, new_hints)) = new_hints {
|
||||||
|
let buffer_hints = inlay_hints.inlay_tasks.entry(buffer_id).or_default();
|
||||||
|
buffer_hints.insert(
|
||||||
|
hints_range.clone(),
|
||||||
|
cx.spawn(async move |editor, cx| {
|
||||||
|
let new_hints = new_hints.await;
|
||||||
|
editor
|
||||||
|
.update(cx, |editor, cx| {
|
||||||
|
let multi_buffer_snapshot = editor.buffer.read(cx).snapshot(cx);
|
||||||
|
let Some(buffer_snapshot) =
|
||||||
|
multi_buffer_snapshot.buffer_for_excerpt(excerpt_id)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut update_data = None;
|
||||||
|
if let Some(inlay_hints) = editor.inlay_hints.as_mut() {
|
||||||
|
let inlay_tasks =
|
||||||
|
inlay_hints.inlay_tasks.entry(buffer_id).or_default();
|
||||||
|
match new_hints {
|
||||||
|
Ok(new_hints) => {
|
||||||
|
if inlay_hints.inlays_for_version.as_ref().is_some_and(
|
||||||
|
|inlays_for_version| {
|
||||||
|
!inlays_for_version
|
||||||
|
.changed_since(&buffer_version)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
let hints_to_remove = if inlay_hints
|
||||||
|
.inlays_for_version
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|inlays_for_version| {
|
||||||
|
buffer_version
|
||||||
|
.changed_since(&inlays_for_version)
|
||||||
|
}) {
|
||||||
|
inlay_hints
|
||||||
|
.inlays
|
||||||
|
.remove(&buffer_id)
|
||||||
|
.unwrap_or_default()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
let hints_to_insert = new_hints
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|lsp_hint| {
|
||||||
|
if buffer_anchor_range
|
||||||
|
.start
|
||||||
|
.cmp(
|
||||||
|
&lsp_hint.position,
|
||||||
|
buffer_snapshot,
|
||||||
|
)
|
||||||
|
.is_ge()
|
||||||
|
&& buffer_anchor_range
|
||||||
|
.end
|
||||||
|
.cmp(
|
||||||
|
&lsp_hint.position,
|
||||||
|
buffer_snapshot,
|
||||||
|
)
|
||||||
|
.is_le()
|
||||||
|
{
|
||||||
|
let position = multi_buffer_snapshot
|
||||||
|
.anchor_in_excerpt(
|
||||||
|
excerpt_id,
|
||||||
|
lsp_hint.position,
|
||||||
|
)?;
|
||||||
|
return Some(Inlay::hint(
|
||||||
|
post_inc(&mut editor.next_inlay_id),
|
||||||
|
position,
|
||||||
|
&lsp_hint,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
update_data =
|
||||||
|
Some((hints_to_remove, hints_to_insert));
|
||||||
|
inlay_hints.inlays_for_version =
|
||||||
|
Some(buffer_version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO kb who should log and clean up the errored state? Could we do that with `lsp_store_cx.spawn`?
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
inlay_tasks.remove(&hints_range);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((hints_to_remove, hints_to_insert)) = update_data {
|
||||||
|
editor.splice_inlays(&hints_to_remove, hints_to_insert, cx);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
|
fn visible_inlay_hints(&self, cx: &Context<Editor>) -> Vec<Inlay> {
|
||||||
|
@ -22248,6 +22210,18 @@ pub trait SemanticsProvider {
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
|
) -> Option<Task<anyhow::Result<Vec<InlayHint>>>>;
|
||||||
|
|
||||||
|
fn inlay_hints_2(
|
||||||
|
&self,
|
||||||
|
buffer: Entity<Buffer>,
|
||||||
|
range: Range<text::Anchor>,
|
||||||
|
cx: &mut App,
|
||||||
|
) -> Option<(
|
||||||
|
Range<BufferRow>,
|
||||||
|
Shared<Task<Result<Vec<InlayHint>, Arc<anyhow::Error>>>>,
|
||||||
|
)> {
|
||||||
|
todo!("TODO kb")
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_inlay_hint(
|
fn resolve_inlay_hint(
|
||||||
&self,
|
&self,
|
||||||
hint: InlayHint,
|
hint: InlayHint,
|
||||||
|
|
|
@ -24,12 +24,7 @@ pub struct BufferInlayHints {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct HintChunks {
|
struct HintChunks {
|
||||||
hints_by_chunks: BTreeMap<Range<BufferRow>, Option<Vec<InlayHintId>>>,
|
hints_by_chunks: BTreeMap<Range<BufferRow>, Option<Vec<InlayHintId>>>,
|
||||||
chunk_updates: HashMap<Range<BufferRow>, Shared<Task<InlayHints>>>,
|
chunk_updates: HashMap<Range<BufferRow>, Shared<Task<Vec<InlayHint>>>>,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct InlayHints {
|
|
||||||
pub cache_version: usize,
|
|
||||||
pub hints: Vec<InlayHint>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
@ -54,7 +49,7 @@ impl BufferInlayHints {
|
||||||
strategy: HintFetchStrategy,
|
strategy: HintFetchStrategy,
|
||||||
range: impl text::ToOffset,
|
range: impl text::ToOffset,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> Option<(Range<BufferRow>, Shared<Task<InlayHints>>)> {
|
) -> Option<(Range<BufferRow>, Shared<Task<Vec<InlayHint>>>)> {
|
||||||
todo!("TODO kb")
|
todo!("TODO kb")
|
||||||
}
|
}
|
||||||
// we want to store the cache version outbound, so they can query with it: we can return nothing (`Option`) if the version matches
|
// we want to store the cache version outbound, so they can query with it: we can return nothing (`Option`) if the version matches
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue