Handle command line arguments and populate worktree

Nathan Sobo created

Change summary

Cargo.lock                   | 10 +++---
gpui/src/app.rs              | 31 +++++++++++---------
zed/Cargo.toml               |  2 
zed/src/lib.rs               |  6 ++--
zed/src/main.rs              | 56 +++++++++++++++++++++++++++++++------
zed/src/worktree/worktree.rs | 31 ++++++++++++--------
6 files changed, 92 insertions(+), 44 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -435,10 +435,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "crossbeam-queue"
-version = "0.3.1"
+name = "crossbeam-channel"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756"
+checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
 dependencies = [
  "cfg-if 1.0.0",
  "crossbeam-utils 0.8.2",
@@ -808,7 +808,7 @@ name = "ignore"
 version = "0.4.11"
 source = "git+https://github.com/zed-industries/ripgrep?rev=1d152118f35b3e3590216709b86277062d79b8a0#1d152118f35b3e3590216709b86277062d79b8a0"
 dependencies = [
- "crossbeam-channel",
+ "crossbeam-channel 0.4.4",
  "crossbeam-utils 0.7.2",
  "globset",
  "lazy_static",
@@ -1647,7 +1647,7 @@ version = "0.1.0"
 dependencies = [
  "anyhow",
  "arrayvec",
- "crossbeam-queue",
+ "crossbeam-channel 0.5.0",
  "dirs",
  "easy-parallel",
  "gpui",

gpui/src/app.rs 🔗

@@ -1,7 +1,8 @@
 use crate::{
     elements::Element,
-    executor,
+    executor::{self},
     keymap::{self, Keystroke},
+    platform::{self, App as _},
     util::post_inc,
 };
 use anyhow::{anyhow, Result};
@@ -66,16 +67,22 @@ pub struct App(Rc<RefCell<MutableAppContext>>);
 
 impl App {
     pub fn test<T, F: Future<Output = T>>(f: impl FnOnce(App) -> F) -> T {
+        let platform = platform::current::app(); // TODO: Make a test platform app
         let foreground = Rc::new(executor::Foreground::test());
-        let app = Self(Rc::new(RefCell::new(
-            MutableAppContext::with_foreground_executor(foreground.clone()),
-        )));
+        let app = Self(Rc::new(RefCell::new(MutableAppContext::new(
+            foreground.clone(),
+            Arc::new(platform),
+        ))));
         app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
         smol::block_on(foreground.run(f(app)))
     }
 
     pub fn new() -> Result<Self> {
-        let app = Self(Rc::new(RefCell::new(MutableAppContext::new()?)));
+        let platform = Arc::new(platform::current::app());
+        let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?);
+        let app = Self(Rc::new(RefCell::new(MutableAppContext::new(
+            foreground, platform,
+        ))));
         app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0));
         Ok(app)
     }
@@ -269,6 +276,7 @@ type ActionCallback =
 type GlobalActionCallback = dyn FnMut(&dyn Any, &mut MutableAppContext);
 
 pub struct MutableAppContext {
+    platform: Arc<dyn platform::App>,
     ctx: AppContext,
     actions: HashMap<TypeId, HashMap<String, Vec<Box<ActionCallback>>>>,
     global_actions: HashMap<String, Vec<Box<GlobalActionCallback>>>,
@@ -292,14 +300,9 @@ pub struct MutableAppContext {
 }
 
 impl MutableAppContext {
-    pub fn new() -> Result<Self> {
-        Ok(Self::with_foreground_executor(Rc::new(
-            executor::Foreground::platform(todo!())?,
-        )))
-    }
-
-    fn with_foreground_executor(foreground: Rc<executor::Foreground>) -> Self {
+    pub fn new(foreground: Rc<executor::Foreground>, platform: Arc<dyn platform::App>) -> Self {
         Self {
+            platform,
             ctx: AppContext {
                 models: HashMap::new(),
                 windows: HashMap::new(),
@@ -1441,8 +1444,8 @@ impl<'a, T: Entity> ModelContext<'a, T> {
         self.app
             .background
             .spawn(async move {
-                if let Err(_) = tx.send(future.await).await {
-                    log::error!("Error sending background task result to main thread",);
+                if let Err(e) = tx.send(future.await).await {
+                    log::error!("error sending background task result to main thread: {}", e);
                 }
             })
             .detach();

zed/Cargo.toml 🔗

@@ -15,7 +15,7 @@ path = "src/main.rs"
 [dependencies]
 anyhow = "1.0.38"
 arrayvec = "0.5.2"
-crossbeam-queue = "0.3.1"
+crossbeam-channel = "0.5.0"
 dirs = "3.0"
 easy-parallel = "3.1.0"
 gpui = {path = "../gpui"}

zed/src/lib.rs 🔗

@@ -1,6 +1,6 @@
-mod editor;
+pub mod editor;
 mod operation_queue;
-mod settings;
+pub mod settings;
 mod sum_tree;
 #[cfg(test)]
 mod test;
@@ -8,5 +8,5 @@ mod time;
 mod timer;
 mod util;
 mod watch;
-mod workspace;
+pub mod workspace;
 mod worktree;

zed/src/main.rs 🔗

@@ -3,10 +3,15 @@ use gpui::{
     executor,
     geometry::{rect::RectF, vector::vec2f},
     platform::{current as platform, App as _, Runner as _, WindowOptions},
+    FontCache,
 };
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 use std::{fs, mem, rc::Rc, sync::Arc};
+use zed::{
+    editor, settings,
+    workspace::{self, OpenParams},
+};
 
 fn main() {
     init_logger();
@@ -18,23 +23,56 @@ fn main() {
             .expect("could not foreground create executor"),
     );
 
+    let font_cache = FontCache::new();
+
+    let (settings_tx, settings_rx) = settings::channel(&font_cache).unwrap();
+
+    let mut app = gpui::App::new().unwrap();
+
     platform::runner()
         .on_finish_launching(move || {
             log::info!("finish launching");
+
+            workspace::init(&mut app);
+            editor::init(&mut app);
+
             if stdout_is_a_pty() {
                 platform.activate(true);
             }
-            let window = platform
-                .open_window(
-                    WindowOptions {
-                        bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
-                        title: Some("Zed"),
+
+            let paths = std::env::args()
+                .skip(1)
+                .filter_map(|arg| match fs::canonicalize(arg) {
+                    Ok(path) => Some(path),
+                    Err(error) => {
+                        log::error!("error parsing path argument: {}", error);
+                        None
+                    }
+                })
+                .collect::<Vec<_>>();
+
+            if !paths.is_empty() {
+                app.dispatch_global_action(
+                    "workspace:open_paths",
+                    OpenParams {
+                        paths,
+                        settings: settings_rx,
                     },
-                    foreground,
-                )
-                .expect("error opening window");
+                );
+                mem::forget(app); // This is here until we hold on to the app for some reason
+            }
+
+            // let window = platform
+            //     .open_window(
+            //         WindowOptions {
+            //             bounds: RectF::new(vec2f(0., 0.), vec2f(1024., 768.)),
+            //             title: Some("Zed"),
+            //         },
+            //         foreground,
+            //     )
+            //     .expect("error opening window");
 
-            mem::forget(window); // Leak window for now so it doesn't close
+            // mem::forget(window); // Leak window for now so it doesn't close
         })
         .run();
 }

zed/src/worktree/worktree.rs 🔗

@@ -5,7 +5,7 @@ use super::{
 };
 use crate::{editor::History, timer, util::post_inc};
 use anyhow::{anyhow, Result};
-use crossbeam_queue::SegQueue;
+use crossbeam_channel as channel;
 use easy_parallel::Parallel;
 use gpui::{AppContext, Entity, ModelContext, ModelHandle};
 use ignore::dir::{Ignore, IgnoreBuilder};
@@ -39,7 +39,7 @@ struct DirToScan {
     path: PathBuf,
     relative_path: PathBuf,
     ignore: Option<Ignore>,
-    dirs_to_scan: Arc<SegQueue<io::Result<DirToScan>>>,
+    dirs_to_scan: channel::Sender<io::Result<DirToScan>>,
 }
 
 impl Worktree {
@@ -61,10 +61,14 @@ impl Worktree {
 
             let tree = tree.clone();
             let (tx, rx) = smol::channel::bounded(1);
-            std::thread::spawn(move || {
-                let _ = smol::block_on(tx.send(tree.scan_dirs()));
-            });
-            let _ = ctx.spawn(async move { rx.recv().await.unwrap() }, Self::done_scanning);
+
+            ctx.background_executor()
+                .spawn(async move {
+                    tx.send(tree.scan_dirs()).await.unwrap();
+                })
+                .detach();
+
+            let _ = ctx.spawn_local(async move { rx.recv().await.unwrap() }, Self::done_scanning);
 
             let _ = ctx.spawn_stream_local(
                 timer::repeat(Duration::from_millis(100)).map(|_| ()),
@@ -95,19 +99,22 @@ impl Worktree {
         if metadata.file_type().is_dir() {
             let is_ignored = is_ignored || name == ".git";
             let id = self.push_dir(None, name, ino, is_symlink, is_ignored);
-            let queue = Arc::new(SegQueue::new());
+            let (tx, rx) = channel::unbounded();
 
-            queue.push(Ok(DirToScan {
+            let tx_ = tx.clone();
+            tx.send(Ok(DirToScan {
                 id,
                 path,
                 relative_path,
                 ignore: Some(ignore),
-                dirs_to_scan: queue.clone(),
-            }));
+                dirs_to_scan: tx_,
+            }))
+            .unwrap();
+            drop(tx);
 
             Parallel::<io::Result<()>>::new()
                 .each(0..16, |_| {
-                    while let Some(result) = queue.pop() {
+                    while let Ok(result) = rx.recv() {
                         self.scan_dir(result?)?;
                     }
                     Ok(())
@@ -150,7 +157,7 @@ impl Worktree {
                 new_children.push(id);
 
                 let dirs_to_scan = to_scan.dirs_to_scan.clone();
-                let _ = to_scan.dirs_to_scan.push(Ok(DirToScan {
+                let _ = to_scan.dirs_to_scan.send(Ok(DirToScan {
                     id,
                     path,
                     relative_path,