Stub initial hint requests
This commit is contained in:
parent
8a3b515f56
commit
79b97f9e75
4 changed files with 122 additions and 28 deletions
|
@ -70,7 +70,10 @@ pub use multi_buffer::{
|
||||||
};
|
};
|
||||||
use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
|
use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
|
||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use project::{FormatTrigger, Location, LocationLink, Project, ProjectPath, ProjectTransaction};
|
use parking_lot::RwLock;
|
||||||
|
use project::{
|
||||||
|
FormatTrigger, InlayHint, Location, LocationLink, Project, ProjectPath, ProjectTransaction,
|
||||||
|
};
|
||||||
use scroll::{
|
use scroll::{
|
||||||
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
|
autoscroll::Autoscroll, OngoingScroll, ScrollAnchor, ScrollManager, ScrollbarAutoHide,
|
||||||
};
|
};
|
||||||
|
@ -87,7 +90,10 @@ use std::{
|
||||||
num::NonZeroU32,
|
num::NonZeroU32,
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Deref, DerefMut, Range},
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::Arc,
|
sync::{
|
||||||
|
atomic::{self, AtomicUsize},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
pub use sum_tree::Bias;
|
pub use sum_tree::Bias;
|
||||||
|
@ -535,6 +541,7 @@ 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_hints: Arc<InlayHintState>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,6 +1158,47 @@ impl CopilotState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct InlayHintState {
|
||||||
|
hints: RwLock<Vec<InlayHint>>,
|
||||||
|
last_updated_timestamp: AtomicUsize,
|
||||||
|
hints_generation: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InlayHintState {
|
||||||
|
pub fn new_timestamp(&self) -> usize {
|
||||||
|
self.hints_generation
|
||||||
|
.fetch_add(1, atomic::Ordering::Release)
|
||||||
|
+ 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(&self) -> Vec<InlayHint> {
|
||||||
|
self.hints.read().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_if_newer(&self, new_hints: Vec<InlayHint>, new_timestamp: usize) {
|
||||||
|
let last_updated_timestamp = self.last_updated_timestamp.load(atomic::Ordering::Acquire);
|
||||||
|
if last_updated_timestamp < new_timestamp {
|
||||||
|
let mut guard = self.hints.write();
|
||||||
|
match self.last_updated_timestamp.compare_exchange(
|
||||||
|
last_updated_timestamp,
|
||||||
|
new_timestamp,
|
||||||
|
atomic::Ordering::AcqRel,
|
||||||
|
atomic::Ordering::Acquire,
|
||||||
|
) {
|
||||||
|
Ok(_) => *guard = new_hints,
|
||||||
|
Err(other_value) => {
|
||||||
|
if other_value < new_timestamp {
|
||||||
|
self.last_updated_timestamp
|
||||||
|
.store(new_timestamp, atomic::Ordering::Release);
|
||||||
|
*guard = new_hints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct ActiveDiagnosticGroup {
|
struct ActiveDiagnosticGroup {
|
||||||
primary_range: Range<Anchor>,
|
primary_range: Range<Anchor>,
|
||||||
|
@ -1340,6 +1388,7 @@ 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_hints: Arc::new(InlayHintState::default()),
|
||||||
gutter_hovered: false,
|
gutter_hovered: false,
|
||||||
_subscriptions: vec![
|
_subscriptions: vec![
|
||||||
cx.observe(&buffer, Self::on_buffer_changed),
|
cx.observe(&buffer, Self::on_buffer_changed),
|
||||||
|
@ -1366,6 +1415,8 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.report_editor_event("open", None, cx);
|
this.report_editor_event("open", None, cx);
|
||||||
|
// this.update_inlay_hints(cx);
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2151,10 +2202,6 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(hints_task) = this.request_inlay_hints(cx) {
|
|
||||||
hints_task.detach_and_log_err(cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
if had_active_copilot_suggestion {
|
if had_active_copilot_suggestion {
|
||||||
this.refresh_copilot_suggestions(true, cx);
|
this.refresh_copilot_suggestions(true, cx);
|
||||||
if !this.has_active_copilot_suggestion(cx) {
|
if !this.has_active_copilot_suggestion(cx) {
|
||||||
|
@ -2581,25 +2628,45 @@ impl Editor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO kb proper inlay hints handling
|
fn update_inlay_hints(&self, cx: &mut ViewContext<Self>) {
|
||||||
fn request_inlay_hints(&self, cx: &mut ViewContext<Self>) -> Option<Task<Result<()>>> {
|
if self.mode != EditorMode::Full {
|
||||||
let project = self.project.as_ref()?;
|
return;
|
||||||
|
}
|
||||||
let position = self.selections.newest_anchor().head();
|
let position = self.selections.newest_anchor().head();
|
||||||
let (buffer, _) = self
|
let Some((buffer, _)) = self
|
||||||
.buffer
|
.buffer
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.text_anchor_for_position(position.clone(), cx)?;
|
.text_anchor_for_position(position.clone(), cx) else { return };
|
||||||
|
|
||||||
let end = buffer.read(cx).len();
|
let generator_buffer = buffer.clone();
|
||||||
let inlay_hints_task = project.update(cx, |project, cx| {
|
let inlay_hints_storage = Arc::clone(&self.inlay_hints);
|
||||||
project.inlay_hints(buffer.clone(), 0..end, cx)
|
// TODO kb should this come from external things like transaction counter instead?
|
||||||
});
|
// This way we can reuse tasks result for the same timestamp? The counter has to be global among all buffer changes & other reloads.
|
||||||
|
let new_timestamp = self.inlay_hints.new_timestamp();
|
||||||
|
|
||||||
Some(cx.spawn(|_, _| async move {
|
// TODO kb this would not work until the language server is ready, how to wait for it?
|
||||||
let inlay_hints = inlay_hints_task.await?;
|
// TODO kb waiting before the server starts and handling workspace/inlayHint/refresh commands is kind of orthogonal?
|
||||||
dbg!(inlay_hints);
|
// need to be able to not to start new tasks, if current one is running on the same state already.
|
||||||
Ok(())
|
cx.spawn(|editor, mut cx| async move {
|
||||||
}))
|
let task = editor.update(&mut cx, |editor, cx| {
|
||||||
|
editor.project.as_ref().map(|project| {
|
||||||
|
project.update(cx, |project, cx| {
|
||||||
|
// TODO kb use visible_lines as a range instead?
|
||||||
|
let end = generator_buffer.read(cx).len();
|
||||||
|
project.inlay_hints(generator_buffer, 0..end, cx)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if let Some(task) = task {
|
||||||
|
// TODO kb contexts everywhere
|
||||||
|
let new_hints = task.await?;
|
||||||
|
inlay_hints_storage.update_if_newer(new_hints, new_timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
anyhow::Ok(())
|
||||||
|
})
|
||||||
|
.detach_and_log_err(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trigger_on_type_formatting(
|
fn trigger_on_type_formatting(
|
||||||
|
@ -6640,7 +6707,10 @@ impl Editor {
|
||||||
) -> Option<TransactionId> {
|
) -> Option<TransactionId> {
|
||||||
self.start_transaction_at(Instant::now(), cx);
|
self.start_transaction_at(Instant::now(), cx);
|
||||||
update(self, cx);
|
update(self, cx);
|
||||||
self.end_transaction_at(Instant::now(), cx)
|
let transaction_id = self.end_transaction_at(Instant::now(), cx);
|
||||||
|
// TODO kb is this the right idea? Maybe instead we should react on `BufferEvent::Edited`?
|
||||||
|
self.update_inlay_hints(cx);
|
||||||
|
transaction_id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
|
fn start_transaction_at(&mut self, now: Instant, cx: &mut ViewContext<Self>) {
|
||||||
|
|
|
@ -879,6 +879,7 @@ impl EditorElement {
|
||||||
for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
|
for (ix, line_with_invisibles) in layout.position_map.line_layouts.iter().enumerate() {
|
||||||
let row = start_row + ix as u32;
|
let row = start_row + ix as u32;
|
||||||
line_with_invisibles.draw(
|
line_with_invisibles.draw(
|
||||||
|
editor,
|
||||||
layout,
|
layout,
|
||||||
row,
|
row,
|
||||||
scroll_top,
|
scroll_top,
|
||||||
|
@ -1794,6 +1795,7 @@ impl LineWithInvisibles {
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
|
editor: &mut Editor,
|
||||||
layout: &LayoutState,
|
layout: &LayoutState,
|
||||||
row: u32,
|
row: u32,
|
||||||
scroll_top: f32,
|
scroll_top: f32,
|
||||||
|
@ -1817,6 +1819,10 @@ impl LineWithInvisibles {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// TODO kb bad: cloning happens very frequently, check the timestamp first
|
||||||
|
let new_hints = editor.inlay_hints.read();
|
||||||
|
// dbg!(new_hints);
|
||||||
|
|
||||||
self.draw_invisibles(
|
self.draw_invisibles(
|
||||||
&selection_ranges,
|
&selection_ranges,
|
||||||
layout,
|
layout,
|
||||||
|
|
|
@ -389,7 +389,7 @@ impl LanguageServer {
|
||||||
..WorkspaceSymbolClientCapabilities::default()
|
..WorkspaceSymbolClientCapabilities::default()
|
||||||
}),
|
}),
|
||||||
inlay_hint: Some(InlayHintWorkspaceClientCapabilities {
|
inlay_hint: Some(InlayHintWorkspaceClientCapabilities {
|
||||||
refresh_support: Default::default(),
|
refresh_support: Some(true),
|
||||||
}),
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -325,7 +325,7 @@ pub struct Location {
|
||||||
pub range: Range<language::Anchor>,
|
pub range: Range<language::Anchor>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InlayHint {
|
pub struct InlayHint {
|
||||||
pub position: Anchor,
|
pub position: Anchor,
|
||||||
pub label: InlayHintLabel,
|
pub label: InlayHintLabel,
|
||||||
|
@ -333,32 +333,32 @@ pub struct InlayHint {
|
||||||
pub tooltip: Option<InlayHintTooltip>,
|
pub tooltip: Option<InlayHintTooltip>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InlayHintLabel {
|
pub enum InlayHintLabel {
|
||||||
String(String),
|
String(String),
|
||||||
LabelParts(Vec<InlayHintLabelPart>),
|
LabelParts(Vec<InlayHintLabelPart>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InlayHintLabelPart {
|
pub struct InlayHintLabelPart {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub tooltip: Option<InlayHintLabelPartTooltip>,
|
pub tooltip: Option<InlayHintLabelPartTooltip>,
|
||||||
pub location: Option<Location>,
|
pub location: Option<Location>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InlayHintTooltip {
|
pub enum InlayHintTooltip {
|
||||||
String(String),
|
String(String),
|
||||||
MarkupContent(MarkupContent),
|
MarkupContent(MarkupContent),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum InlayHintLabelPartTooltip {
|
pub enum InlayHintLabelPartTooltip {
|
||||||
String(String),
|
String(String),
|
||||||
MarkupContent(MarkupContent),
|
MarkupContent(MarkupContent),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct MarkupContent {
|
pub struct MarkupContent {
|
||||||
pub kind: String,
|
pub kind: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
|
@ -2810,6 +2810,24 @@ impl Project {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
language_server
|
||||||
|
.on_request::<lsp::request::InlayHintRefreshRequest, _, _>({
|
||||||
|
dbg!("!!!!!!!!!!!!!!");
|
||||||
|
let this = this.downgrade();
|
||||||
|
move |params, cx| async move {
|
||||||
|
// TODO kb: trigger an event, to call on every open editor
|
||||||
|
// TODO kb does not get called now, why?
|
||||||
|
dbg!("@@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||||
|
|
||||||
|
let _this = this
|
||||||
|
.upgrade(&cx)
|
||||||
|
.ok_or_else(|| anyhow!("project dropped"))?;
|
||||||
|
dbg!(params);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
|
||||||
let disk_based_diagnostics_progress_token =
|
let disk_based_diagnostics_progress_token =
|
||||||
adapter.disk_based_diagnostics_progress_token.clone();
|
adapter.disk_based_diagnostics_progress_token.clone();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue