logiguard fork v2: patched GPUI on verified 8c74db0 tree
Rebuilt from the exact zed-industries/zed @ 8c74db00c4053d6c0a9037863d7bd048658da4ca tree (previous orphan was based on a stale/wrong fetch). Same 3-file patch: - serial.rs: add SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; add latest_serial_of() - window.rs: activate() uses keyboard-enter serial (Mutter focus gate) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
//! A source of tasks, based on a static configuration, deserialized from the tasks config file, and related infrastructure for tracking changes to the file.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use futures::{StreamExt, channel::mpsc::UnboundedSender};
|
||||
use gpui::{App, AppContext, TaskExt};
|
||||
use parking_lot::RwLock;
|
||||
use serde::Deserialize;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::TaskTemplates;
|
||||
use futures::channel::mpsc::UnboundedReceiver;
|
||||
|
||||
/// The source of tasks defined in a tasks config file.
|
||||
pub struct StaticSource {
|
||||
tasks: TrackedFile<TaskTemplates>,
|
||||
}
|
||||
|
||||
/// A Wrapper around deserializable T that keeps track of its contents
|
||||
/// via a provided channel.
|
||||
pub struct TrackedFile<T> {
|
||||
parsed_contents: Arc<RwLock<T>>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq + 'static + Sync> TrackedFile<T> {
|
||||
/// Initializes new [`TrackedFile`] with a type that's deserializable.
|
||||
pub fn new(
|
||||
mut tracker: UnboundedReceiver<String>,
|
||||
notification_outlet: UnboundedSender<()>,
|
||||
cx: &App,
|
||||
) -> Self
|
||||
where
|
||||
T: for<'a> Deserialize<'a> + Default + Send,
|
||||
{
|
||||
let parsed_contents: Arc<RwLock<T>> = Arc::default();
|
||||
cx.background_spawn({
|
||||
let parsed_contents = parsed_contents.clone();
|
||||
async move {
|
||||
while let Some(new_contents) = tracker.next().await {
|
||||
if Arc::strong_count(&parsed_contents) == 1 {
|
||||
// We're no longer being observed. Stop polling.
|
||||
break;
|
||||
}
|
||||
if !new_contents.trim().is_empty() {
|
||||
let Some(new_contents) =
|
||||
serde_json_lenient::from_str::<T>(&new_contents).log_err()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let mut contents = parsed_contents.write();
|
||||
if *contents != new_contents {
|
||||
*contents = new_contents;
|
||||
if notification_outlet.unbounded_send(()).is_err() {
|
||||
// Whoever cared about contents is not around anymore.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow::Ok(())
|
||||
}
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
Self { parsed_contents }
|
||||
}
|
||||
|
||||
/// Initializes new [`TrackedFile`] with a type that's convertible from another deserializable type.
|
||||
pub fn new_convertible<U: for<'a> Deserialize<'a> + TryInto<T, Error = anyhow::Error>>(
|
||||
mut tracker: UnboundedReceiver<String>,
|
||||
notification_outlet: UnboundedSender<()>,
|
||||
cx: &App,
|
||||
) -> Self
|
||||
where
|
||||
T: Default + Send,
|
||||
{
|
||||
let parsed_contents: Arc<RwLock<T>> = Arc::default();
|
||||
cx.background_spawn({
|
||||
async move {
|
||||
while let Some(new_contents) = tracker.next().await {
|
||||
if Arc::strong_count(&parsed_contents) == 1 {
|
||||
// We're no longer being observed. Stop polling.
|
||||
break;
|
||||
}
|
||||
|
||||
if !new_contents.trim().is_empty() {
|
||||
let Some(new_contents) =
|
||||
serde_json_lenient::from_str::<U>(&new_contents).log_err()
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let Some(new_contents) = new_contents.try_into().log_err() else {
|
||||
continue;
|
||||
};
|
||||
let mut contents = parsed_contents.write();
|
||||
if *contents != new_contents {
|
||||
*contents = new_contents;
|
||||
if notification_outlet.unbounded_send(()).is_err() {
|
||||
// Whoever cared about contents is not around anymore.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
anyhow::Ok(())
|
||||
}
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
Self {
|
||||
parsed_contents: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticSource {
|
||||
/// Initializes the static source, reacting on tasks config changes.
|
||||
pub fn new(tasks: TrackedFile<TaskTemplates>) -> Self {
|
||||
Self { tasks }
|
||||
}
|
||||
/// Returns current list of tasks
|
||||
pub fn tasks_to_schedule(&self) -> TaskTemplates {
|
||||
self.tasks.parsed_contents.read().clone()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user