Render pending language server work in status bar

This commit is contained in:
Antonio Scandurra 2022-03-10 16:09:47 +01:00
parent 4bbf5ed0b9
commit 4243f0c339
4 changed files with 151 additions and 54 deletions

View file

@ -51,6 +51,8 @@ pub struct Project {
languages: Arc<LanguageRegistry>,
language_servers: HashMap<(WorktreeId, Arc<str>), Arc<LanguageServer>>,
started_language_servers: HashMap<(WorktreeId, Arc<str>), Task<Option<Arc<LanguageServer>>>>,
pending_language_server_work: HashMap<(usize, String), LspWorkProgress>,
next_language_server_id: usize,
client: Arc<client::Client>,
user_store: ModelHandle<UserStore>,
fs: Arc<dyn Fs>,
@ -120,8 +122,7 @@ enum LspEvent {
},
WorkProgress {
token: String,
message: Option<String>,
percentage: Option<usize>,
progress: LspWorkProgress,
},
WorkEnd {
token: String,
@ -129,6 +130,12 @@ enum LspEvent {
DiagnosticsUpdate(lsp::PublishDiagnosticsParams),
}
#[derive(Clone, Default)]
pub struct LspWorkProgress {
pub message: Option<String>,
pub percentage: Option<usize>,
}
#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
pub struct ProjectPath {
pub worktree_id: WorktreeId,
@ -317,6 +324,8 @@ impl Project {
language_servers_with_diagnostics_running: 0,
language_servers: Default::default(),
started_language_servers: Default::default(),
pending_language_server_work: Default::default(),
next_language_server_id: 0,
nonce: StdRng::from_entropy().gen(),
}
})
@ -386,6 +395,8 @@ impl Project {
language_servers_with_diagnostics_running: 0,
language_servers: Default::default(),
started_language_servers: Default::default(),
pending_language_server_work: Default::default(),
next_language_server_id: 0,
opened_buffers: Default::default(),
buffer_snapshots: Default::default(),
nonce: StdRng::from_entropy().gen(),
@ -1172,6 +1183,7 @@ impl Project {
self.started_language_servers
.entry(key.clone())
.or_insert_with(|| {
let server_id = post_inc(&mut self.next_language_server_id);
let language_server = self.languages.start_language_server(
language.clone(),
worktree_path,
@ -1213,8 +1225,12 @@ impl Project {
lsp_events_tx
.try_send(LspEvent::WorkProgress {
token,
progress: LspWorkProgress {
message: report.message,
percentage: report.percentage.map(|p| p as usize),
percentage: report
.percentage
.map(|p| p as usize),
},
})
.ok();
}
@ -1233,7 +1249,7 @@ impl Project {
while let Ok(event) = lsp_events_rx.recv().await {
let this = this.upgrade(&cx)?;
this.update(&mut cx, |this, cx| {
this.on_local_lsp_event(event, &language, cx)
this.on_local_lsp_event(server_id, event, &language, cx)
});
}
Some(())
@ -1309,6 +1325,7 @@ impl Project {
fn on_local_lsp_event(
&mut self,
language_server_id: usize,
event: LspEvent,
language: &Arc<Language>,
cx: &mut ModelContext<Self>,
@ -1318,43 +1335,53 @@ impl Project {
LspEvent::WorkStart { token } => {
if Some(&token) == disk_diagnostics_token {
self.disk_based_diagnostics_started(cx);
self.send_lsp_event(proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
proto::LspDiskBasedDiagnosticsUpdating {},
));
),
);
} else {
self.on_lsp_work_start(token.clone(), cx);
self.send_lsp_event(proto::lsp_event::Variant::WorkStart(
proto::LspWorkStart { token },
));
self.on_lsp_work_start(language_server_id, token.clone(), cx);
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::WorkStart(proto::LspWorkStart { token }),
);
}
}
LspEvent::WorkProgress {
token,
message,
percentage,
} => {
LspEvent::WorkProgress { token, progress } => {
if Some(&token) != disk_diagnostics_token {
self.on_lsp_work_progress(token.clone(), message.clone(), percentage, cx);
self.send_lsp_event(proto::lsp_event::Variant::WorkProgress(
proto::LspWorkProgress {
self.on_lsp_work_progress(
language_server_id,
token.clone(),
progress.clone(),
cx,
);
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::WorkProgress(proto::LspWorkProgress {
token,
message,
percentage: percentage.map(|p| p as u32),
},
));
message: progress.message,
percentage: progress.percentage.map(|p| p as u32),
}),
);
}
}
LspEvent::WorkEnd { token } => {
if Some(&token) == disk_diagnostics_token {
self.disk_based_diagnostics_finished(cx);
self.send_lsp_event(proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
proto::LspDiskBasedDiagnosticsUpdated {},
));
),
);
} else {
self.on_lsp_work_end(token.clone(), cx);
self.send_lsp_event(proto::lsp_event::Variant::WorkEnd(proto::LspWorkEnd {
token,
}));
self.on_lsp_work_end(language_server_id, token.clone(), cx);
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::WorkEnd(proto::LspWorkEnd { token }),
);
}
}
LspEvent::DiagnosticsUpdate(mut params) => {
@ -1362,9 +1389,12 @@ impl Project {
if disk_diagnostics_token.is_none() {
self.disk_based_diagnostics_started(cx);
self.send_lsp_event(proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(
proto::LspDiskBasedDiagnosticsUpdating {},
));
),
);
}
self.update_diagnostics(
params,
@ -1376,38 +1406,74 @@ impl Project {
.log_err();
if disk_diagnostics_token.is_none() {
self.disk_based_diagnostics_finished(cx);
self.send_lsp_event(proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
self.broadcast_lsp_event(
language_server_id,
proto::lsp_event::Variant::DiskBasedDiagnosticsUpdated(
proto::LspDiskBasedDiagnosticsUpdated {},
));
),
);
}
}
}
}
fn on_lsp_work_start(&mut self, token: String, cx: &mut ModelContext<Self>) {}
fn on_lsp_work_start(
&mut self,
language_server_id: usize,
token: String,
cx: &mut ModelContext<Self>,
) {
self.pending_language_server_work.insert(
(language_server_id, token),
LspWorkProgress {
message: None,
percentage: None,
},
);
cx.notify();
}
fn on_lsp_work_progress(
&mut self,
language_server_id: usize,
token: String,
message: Option<String>,
percentage: Option<usize>,
progress: LspWorkProgress,
cx: &mut ModelContext<Self>,
) {
self.pending_language_server_work
.insert((language_server_id, token), progress);
cx.notify();
}
fn on_lsp_work_end(&mut self, token: String, cx: &mut ModelContext<Self>) {}
fn on_lsp_work_end(
&mut self,
language_server_id: usize,
token: String,
cx: &mut ModelContext<Self>,
) {
self.pending_language_server_work
.remove(&(language_server_id, token));
cx.notify();
}
fn send_lsp_event(&self, event: proto::lsp_event::Variant) {
fn broadcast_lsp_event(&self, language_server_id: usize, event: proto::lsp_event::Variant) {
if let Some(project_id) = self.remote_id() {
self.client
.send(proto::LspEvent {
project_id,
language_server_id: language_server_id as u64,
variant: Some(event),
})
.log_err();
}
}
pub fn pending_language_server_work(&self) -> impl Iterator<Item = (&str, &LspWorkProgress)> {
self.pending_language_server_work
.iter()
.map(|((_, token), progress)| (token.as_str(), progress))
}
pub fn update_diagnostics(
&mut self,
params: lsp::PublishDiagnosticsParams,
@ -3152,24 +3218,28 @@ impl Project {
_: Arc<Client>,
mut cx: AsyncAppContext,
) -> Result<()> {
let language_server_id = envelope.payload.language_server_id as usize;
match envelope
.payload
.variant
.ok_or_else(|| anyhow!("invalid variant"))?
{
proto::lsp_event::Variant::WorkStart(payload) => this.update(&mut cx, |this, cx| {
this.on_lsp_work_start(payload.token, cx);
this.on_lsp_work_start(language_server_id, payload.token, cx);
}),
proto::lsp_event::Variant::WorkProgress(payload) => this.update(&mut cx, |this, cx| {
this.on_lsp_work_progress(
language_server_id,
payload.token,
payload.message,
payload.percentage.map(|p| p as usize),
LspWorkProgress {
message: payload.message,
percentage: payload.percentage.map(|p| p as usize),
},
cx,
);
}),
proto::lsp_event::Variant::WorkEnd(payload) => this.update(&mut cx, |this, cx| {
this.on_lsp_work_end(payload.token, cx);
this.on_lsp_work_end(language_server_id, payload.token, cx);
}),
proto::lsp_event::Variant::DiskBasedDiagnosticsUpdating(_) => {
this.update(&mut cx, |this, cx| {

View file

@ -425,12 +425,13 @@ message DiagnosticSummary {
message LspEvent {
uint64 project_id = 1;
uint64 language_server_id = 2;
oneof variant {
LspWorkStart work_start = 2;
LspWorkProgress work_progress = 3;
LspWorkEnd work_end = 4;
LspDiskBasedDiagnosticsUpdating disk_based_diagnostics_updating = 5;
LspDiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 6;
LspWorkStart work_start = 3;
LspWorkProgress work_progress = 4;
LspWorkEnd work_end = 5;
LspDiskBasedDiagnosticsUpdating disk_based_diagnostics_updating = 6;
LspDiskBasedDiagnosticsUpdated disk_based_diagnostics_updated = 7;
}
}

View file

@ -1,11 +1,13 @@
use crate::{ItemViewHandle, Settings, StatusItemView};
use futures::StreamExt;
use gpui::{
action, elements::*, platform::CursorStyle, Entity, MutableAppContext, RenderContext, View,
ViewContext,
action, elements::*, platform::CursorStyle, Entity, ModelHandle, MutableAppContext,
RenderContext, View, ViewContext,
};
use language::{LanguageRegistry, LanguageServerBinaryStatus};
use postage::watch;
use project::Project;
use std::fmt::Write;
use std::sync::Arc;
action!(DismissErrorMessage);
@ -15,6 +17,7 @@ pub struct LspStatus {
checking_for_update: Vec<String>,
downloading: Vec<String>,
failed: Vec<String>,
project: ModelHandle<Project>,
}
pub fn init(cx: &mut MutableAppContext) {
@ -23,6 +26,7 @@ pub fn init(cx: &mut MutableAppContext) {
impl LspStatus {
pub fn new(
project: &ModelHandle<Project>,
languages: Arc<LanguageRegistry>,
settings_rx: watch::Receiver<Settings>,
cx: &mut ViewContext<Self>,
@ -62,11 +66,14 @@ impl LspStatus {
}
})
.detach();
cx.observe(project, |_, _, cx| cx.notify()).detach();
Self {
settings_rx,
checking_for_update: Default::default(),
downloading: Default::default(),
failed: Default::default(),
project: project.clone(),
}
}
@ -87,7 +94,24 @@ impl View for LspStatus {
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
let theme = &self.settings_rx.borrow().theme;
if !self.downloading.is_empty() {
let mut pending_work = self.project.read(cx).pending_language_server_work();
if let Some((progress_token, progress)) = pending_work.next() {
let mut message = progress
.message
.clone()
.unwrap_or_else(|| progress_token.to_string());
if let Some(percentage) = progress.percentage {
write!(&mut message, " ({}%)", percentage).unwrap();
}
let additional_work_count = pending_work.count();
if additional_work_count > 0 {
write!(&mut message, " + {} more", additional_work_count).unwrap();
}
Label::new(message, theme.workspace.status_bar.lsp_message.clone()).boxed()
} else if !self.downloading.is_empty() {
Label::new(
format!(
"Downloading {} language server{}...",
@ -112,6 +136,7 @@ impl View for LspStatus {
)
.boxed()
} else if !self.failed.is_empty() {
drop(pending_work);
MouseEventHandler::new::<Self, _, _>(0, cx, |_, _| {
Label::new(
format!(

View file

@ -101,6 +101,7 @@ pub fn build_workspace(
});
let lsp_status = cx.add_view(|cx| {
workspace::lsp_status::LspStatus::new(
workspace.project(),
app_state.languages.clone(),
app_state.settings.clone(),
cx,