Detailed changes
@@ -1,9 +1,10 @@
use crate::{
- AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, ViewContext, WindowContext,
+ AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, Task, ViewContext,
+ WindowContext,
};
use anyhow::anyhow;
use parking_lot::Mutex;
-use std::sync::Weak;
+use std::{future::Future, sync::Weak};
#[derive(Clone)]
pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
@@ -99,6 +100,22 @@ impl AsyncAppContext {
let mut app_context = app.lock();
app_context.update_window(handle.id, update)
}
+
+ pub fn spawn<Fut, R>(
+ &self,
+ f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static,
+ ) -> Result<Task<R>>
+ where
+ Fut: Future<Output = R> + Send + 'static,
+ R: Send + 'static,
+ {
+ let app = self
+ .0
+ .upgrade()
+ .ok_or_else(|| anyhow!("app was released"))?;
+ let app_context = app.lock();
+ Ok(app_context.spawn(f))
+ }
}
#[derive(Clone)]
@@ -1,7 +1,10 @@
use crate::{AppContext, PlatformDispatcher};
+use futures::channel::mpsc;
use smol::prelude::*;
use std::{
fmt::Debug,
+ marker::PhantomData,
+ mem,
pin::Pin,
sync::Arc,
task::{Context, Poll},
@@ -133,7 +136,73 @@ impl Executor {
futures::executor::block_on(future)
}
+ pub async fn scoped<'scope, F>(&self, scheduler: F)
+ where
+ F: FnOnce(&mut Scope<'scope>),
+ {
+ let mut scope = Scope::new(self.clone());
+ (scheduler)(&mut scope);
+ let spawned = mem::take(&mut scope.futures)
+ .into_iter()
+ .map(|f| self.spawn(f))
+ .collect::<Vec<_>>();
+ for task in spawned {
+ task.await;
+ }
+ }
+
pub fn is_main_thread(&self) -> bool {
self.dispatcher.is_main_thread()
}
}
+
+pub struct Scope<'a> {
+ executor: Executor,
+ futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
+ tx: Option<mpsc::Sender<()>>,
+ rx: mpsc::Receiver<()>,
+ lifetime: PhantomData<&'a ()>,
+}
+
+impl<'a> Scope<'a> {
+ fn new(executor: Executor) -> Self {
+ let (tx, rx) = mpsc::channel(1);
+ Self {
+ executor,
+ tx: Some(tx),
+ rx,
+ futures: Default::default(),
+ lifetime: PhantomData,
+ }
+ }
+
+ pub fn spawn<F>(&mut self, f: F)
+ where
+ F: Future<Output = ()> + Send + 'a,
+ {
+ let tx = self.tx.clone().unwrap();
+
+ // Safety: The 'a lifetime is guaranteed to outlive any of these futures because
+ // dropping this `Scope` blocks until all of the futures have resolved.
+ let f = unsafe {
+ mem::transmute::<
+ Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
+ Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
+ >(Box::pin(async move {
+ f.await;
+ drop(tx);
+ }))
+ };
+ self.futures.push(f);
+ }
+}
+
+impl<'a> Drop for Scope<'a> {
+ fn drop(&mut self) {
+ self.tx.take().unwrap();
+
+ // Wait until the channel is closed, which means that all of the spawned
+ // futures have resolved.
+ self.executor.block(self.rx.next());
+ }
+}
@@ -9,7 +9,7 @@ use cli::{
};
use fs::RealFs;
use futures::{channel::mpsc, SinkExt, StreamExt};
-use gpui2::{App, AsyncAppContext, Task};
+use gpui2::{App, AssetSource, AsyncAppContext, Task};
use log::LevelFilter;
use parking_lot::Mutex;
@@ -29,7 +29,10 @@ use std::{
},
thread,
};
-use util::{channel::RELEASE_CHANNEL, http, paths, ResultExt};
+use util::{
+ channel::{parse_zed_link, RELEASE_CHANNEL},
+ http, paths, ResultExt,
+};
use zed2::{ensure_only_instance, AppState, Assets, IsOnlyInstance};
// use zed2::{
// assets::Assets,
@@ -59,8 +62,8 @@ fn main() {
let fs = Arc::new(RealFs);
let user_settings_file_rx =
- watch_config_file(app.executor(), fs.clone(), paths::SETTINGS.clone());
- let user_keymap_file_rx = watch_config_file(app.executor(), fs.clone(), paths::KEYMAP.clone());
+ watch_config_file(&app.executor(), fs.clone(), paths::SETTINGS.clone());
+ let user_keymap_file_rx = watch_config_file(&app.executor(), fs.clone(), paths::KEYMAP.clone());
let login_shell_env_loaded = if stdout_is_a_pty() {
Task::ready(())
@@ -194,7 +197,7 @@ fn main() {
listener.open_urls(urls)
}
} else {
- upload_previous_panics(http.clone(), cx);
+ // upload_previous_panics(http.clone(), cx);
// TODO Development mode that forces the CLI mode usually runs Zed binary as is instead
// of an *app, hence gets no specific callbacks run. Emulate them here, if needed.
@@ -248,8 +251,15 @@ fn main() {
// .detach();
}
OpenRequest::CliConnection { connection } => {
- cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
- .detach();
+ if cx
+ .spawn(|cx| {
+ handle_cli_connection(connection, app_state.clone(), cx)
+ })
+ .map(Task::detach)
+ .is_err()
+ {
+ break;
+ }
}
OpenRequest::JoinChannel { channel_id } => {
// cx
@@ -626,9 +636,10 @@ fn collect_url_args() -> Vec<String> {
}
fn load_embedded_fonts(app: &App) {
- let font_paths = Assets.list("fonts");
+ let font_paths = Assets.list(&"fonts".into()).unwrap();
let embedded_fonts = Mutex::new(Vec::new());
- smol::block_on(app.background().scoped(|scope| {
+ let executor = app.executor();
+ executor.block(executor.scoped(|scope| {
for font_path in &font_paths {
if !font_path.ends_with(".ttf") {
continue;
@@ -755,7 +766,7 @@ async fn handle_cli_connection(
if let Some(request) = requests.next().await {
match request {
CliRequest::Open { paths, wait } => {
- let mut caret_positions = HashMap::new();
+ // let mut caret_positions = HashMap::new();
// todo!("workspace")
// let paths = if paths.is_empty() {