Implement File > Open menu item

Max Brunsfeld created

Change summary

Cargo.lock                   | 18 +++++-----------
Cargo.toml                   |  6 +++++
gpui/src/platform/mac/app.rs | 39 ++++++++++++++++++++++++++++++++++++-
gpui/src/platform/mod.rs     |  7 ++++++
gpui/src/platform/test.rs    |  4 +++
zed/src/main.rs              | 23 ++++++++++++++++++---
zed/src/menus.rs             | 10 ++++++++
7 files changed, 88 insertions(+), 19 deletions(-)

Detailed changes

Cargo.lock πŸ”—

@@ -399,8 +399,7 @@ dependencies = [
 [[package]]
 name = "cocoa"
 version = "0.24.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 dependencies = [
  "bitflags",
  "block",
@@ -415,8 +414,7 @@ dependencies = [
 [[package]]
 name = "cocoa-foundation"
 version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 dependencies = [
  "bitflags",
  "block",
@@ -445,8 +443,7 @@ checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
 [[package]]
 name = "core-foundation"
 version = "0.9.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 dependencies = [
  "core-foundation-sys",
  "libc",
@@ -455,14 +452,12 @@ dependencies = [
 [[package]]
 name = "core-foundation-sys"
 version = "0.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 
 [[package]]
 name = "core-graphics"
 version = "0.22.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "269f35f69b542b80e736a20a89a05215c0ce80c2c03c514abb2e318b78379d86"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 dependencies = [
  "bitflags",
  "core-foundation",
@@ -474,8 +469,7 @@ dependencies = [
 [[package]]
 name = "core-graphics-types"
 version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
+source = "git+https://github.com/zed-industries/core-foundation-rs?rev=52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b#52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"
 dependencies = [
  "bitflags",
  "core-foundation",

Cargo.toml πŸ”—

@@ -3,3 +3,9 @@ members = ["zed", "gpui"]
 
 [patch.crates-io]
 async-task = {git = "https://github.com/zed-industries/async-task", rev = "341b57d6de98cdfd7b418567b8de2022ca993a6e"}
+
+# TODO - Remove when this is merged: https://github.com/servo/core-foundation-rs/pull/454
+cocoa = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"}
+cocoa-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"}
+core-foundation = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"}
+core-graphics = {git = "https://github.com/zed-industries/core-foundation-rs", rev = "52b7bc0a7026dbbe0da66f5e8de6ce1031476d5b"}

gpui/src/platform/mac/app.rs πŸ”—

@@ -1,9 +1,13 @@
 use super::{BoolExt as _, Dispatcher, FontSystem, Window};
 use crate::{executor, platform};
 use anyhow::Result;
-use cocoa::{appkit::NSApplication, base::nil};
+use cocoa::{
+    appkit::{NSApplication, NSOpenPanel, NSModalResponse},
+    base::nil,
+    foundation::{NSArray, NSString, NSURL},
+};
 use objc::{msg_send, sel, sel_impl};
-use std::{rc::Rc, sync::Arc};
+use std::{path::PathBuf, rc::Rc, sync::Arc};
 
 pub struct App {
     dispatcher: Arc<Dispatcher>,
@@ -39,6 +43,37 @@ impl platform::App for App {
         Ok(Box::new(Window::open(options, executor, self.fonts())?))
     }
 
+    fn prompt_for_paths(
+        &self,
+        options: platform::PathPromptOptions,
+    ) -> Option<Vec<std::path::PathBuf>> {
+        unsafe {
+            let panel = NSOpenPanel::openPanel(nil);
+            panel.setCanChooseDirectories_(options.directories.to_objc());
+            panel.setCanChooseFiles_(options.files.to_objc());
+            panel.setAllowsMultipleSelection_(options.multiple.to_objc());
+            panel.setResolvesAliases_(false.to_objc());
+            let response = panel.runModal();
+            if response == NSModalResponse::NSModalResponseOk {
+                let mut result = Vec::new();
+                let urls = panel.URLs();
+                for i in 0..urls.count() {
+                    let url = urls.objectAtIndex(i);
+                    let string = url.absoluteString();
+                    let string = std::ffi::CStr::from_ptr(string.UTF8String())
+                        .to_string_lossy()
+                        .to_string();
+                    if let Some(path) = string.strip_prefix("file://") {
+                        result.push(PathBuf::from(path));
+                    }
+                }
+                Some(result)
+            } else {
+                None
+            }
+        }
+    }
+
     fn fonts(&self) -> Arc<dyn platform::FontSystem> {
         self.fonts.clone()
     }

gpui/src/platform/mod.rs πŸ”—

@@ -41,6 +41,7 @@ pub trait App {
         options: WindowOptions,
         executor: Rc<executor::Foreground>,
     ) -> Result<Box<dyn Window>>;
+    fn prompt_for_paths(&self, options: PathPromptOptions) -> Option<Vec<PathBuf>>;
     fn fonts(&self) -> Arc<dyn FontSystem>;
     fn quit(&self);
 }
@@ -66,6 +67,12 @@ pub struct WindowOptions<'a> {
     pub title: Option<&'a str>,
 }
 
+pub struct PathPromptOptions {
+    pub files: bool,
+    pub directories: bool,
+    pub multiple: bool,
+}
+
 pub trait FontSystem: Send + Sync {
     fn load_family(&self, name: &str) -> anyhow::Result<Vec<FontId>>;
     fn select_font(

gpui/src/platform/test.rs πŸ”—

@@ -48,6 +48,10 @@ impl super::App for App {
     }
 
     fn quit(&self) {}
+
+    fn prompt_for_paths(&self, _: super::PathPromptOptions) -> Option<Vec<std::path::PathBuf>> {
+        None
+    }
 }
 
 impl Window {

zed/src/main.rs πŸ”—

@@ -1,5 +1,5 @@
 use fs::OpenOptions;
-use gpui::platform::{current as platform, Runner as _};
+use gpui::platform::{current as platform, PathPromptOptions, Runner as _};
 use log::LevelFilter;
 use simplelog::SimpleLogger;
 use std::{fs, path::PathBuf};
@@ -18,9 +18,24 @@ fn main() {
         .set_menus(menus::MENUS)
         .on_menu_command({
             let app = app.clone();
-            move |command| {
-                log::info!("menu command: {}", command);
-                app.dispatch_global_action(command, ())
+            let settings_rx = settings_rx.clone();
+            move |command| match command {
+                "app:open" => {
+                    if let Some(paths) = app.platform().prompt_for_paths(PathPromptOptions {
+                        files: true,
+                        directories: true,
+                        multiple: true,
+                    }) {
+                        app.dispatch_global_action(
+                            "workspace:open_paths",
+                            OpenParams {
+                                paths,
+                                settings: settings_rx.clone(),
+                            },
+                        );
+                    }
+                }
+                _ => app.dispatch_global_action(command, ()),
             }
         })
         .on_finish_launching({

zed/src/menus.rs πŸ”—

@@ -6,7 +6,7 @@ pub const MENUS: &'static [Menu] = &[
         name: "Zed",
         items: &[
             MenuItem::Action {
-                name: "About Zed...",
+                name: "About Zed…",
                 keystroke: None,
                 action: "app:about-zed",
             },
@@ -20,6 +20,14 @@ pub const MENUS: &'static [Menu] = &[
     },
     Menu {
         name: "File",
+        items: &[MenuItem::Action {
+            name: "Open…",
+            keystroke: Some("cmd-o"),
+            action: "app:open",
+        }],
+    },
+    Menu {
+        name: "Edit",
         items: &[
             MenuItem::Action {
                 name: "Undo",