use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use lsp_types::{ProgressParams, ProgressParamsValue, ProgressToken, WorkDoneProgress};
pub(crate) struct ProgressDelivery {
previous: Option<futures_channel::oneshot::Receiver<()>>,
done: Option<futures_channel::oneshot::Sender<()>>,
}
pub(crate) struct ProgressDeliveryGuard {
_done: Option<futures_channel::oneshot::Sender<()>>,
}
impl ProgressDelivery {
fn queued(entry: &mut ProgressEntry) -> Self {
let previous = entry.tail.take();
let (done, tail) = futures_channel::oneshot::channel();
entry.tail = Some(tail);
Self {
previous,
done: Some(done),
}
}
fn terminal(mut entry: ProgressEntry) -> Self {
Self {
previous: entry.tail.take(),
done: None,
}
}
pub(crate) async fn wait(mut self) -> ProgressDeliveryGuard {
if let Some(previous) = self.previous.take() {
let _ = previous.await;
}
ProgressDeliveryGuard {
_done: self.done.take(),
}
}
}
#[derive(Clone, Copy)]
pub(crate) enum CreateOutcome {
Succeeded,
Failed,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ProgressPhase {
Creating,
Created,
Begun,
}
struct ProgressEntry {
phase: ProgressPhase,
tail: Option<futures_channel::oneshot::Receiver<()>>,
}
#[derive(Clone, Default)]
pub(crate) struct ClientProgressRegistry {
inner: Arc<Mutex<HashMap<ProgressToken, ProgressEntry>>>,
}
impl ClientProgressRegistry {
pub(crate) fn try_reserve_create(&self, token: ProgressToken) -> bool {
self.try_insert(token, ProgressPhase::Creating)
}
pub(crate) fn try_register_request(&self, token: ProgressToken) -> bool {
self.try_insert(token, ProgressPhase::Created)
}
fn try_insert(&self, token: ProgressToken, phase: ProgressPhase) -> bool {
let mut inner = self.inner.lock().unwrap();
if inner.contains_key(&token) {
return false;
}
inner.insert(token, ProgressEntry { phase, tail: None });
true
}
pub(crate) fn remove(&self, token: &ProgressToken) {
self.inner.lock().unwrap().remove(token);
}
pub(crate) fn finish_create(&self, token: &ProgressToken, outcome: CreateOutcome) {
let mut inner = self.inner.lock().unwrap();
match (inner.get(token).map(|entry| entry.phase), outcome) {
(Some(ProgressPhase::Creating), CreateOutcome::Succeeded) => {
inner.get_mut(token).unwrap().phase = ProgressPhase::Created;
}
(Some(ProgressPhase::Creating), CreateOutcome::Failed) => {
inner.remove(token);
}
_ => {}
}
}
pub(crate) fn accept(&self, params: &ProgressParams) -> Option<ProgressDelivery> {
let mut inner = self.inner.lock().unwrap();
let phase = inner.get(¶ms.token).map(|entry| entry.phase);
match (¶ms.value, phase) {
(
ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(_)),
Some(ProgressPhase::Created),
) => {
let entry = inner.get_mut(¶ms.token).unwrap();
entry.phase = ProgressPhase::Begun;
Some(ProgressDelivery::queued(entry))
}
(
ProgressParamsValue::WorkDone(WorkDoneProgress::Report(_)),
Some(ProgressPhase::Begun),
) => Some(ProgressDelivery::queued(
inner.get_mut(¶ms.token).unwrap(),
)),
(
ProgressParamsValue::WorkDone(WorkDoneProgress::End(_)),
Some(ProgressPhase::Begun),
) => Some(ProgressDelivery::terminal(
inner.remove(¶ms.token).unwrap(),
)),
_ => None,
}
}
}