Detailed changes
@@ -993,7 +993,7 @@ mod tests {
use super::*;
use crate::display_map::inlay_map::InlayMap;
use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
- use gpui::{div, font, px, Element, Platform as _};
+ use gpui::{div, font, px, Element};
use multi_buffer::MultiBuffer;
use rand::prelude::*;
use settings::SettingsStore;
@@ -1185,11 +1185,7 @@ mod tests {
fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
cx.update(|cx| init_test(cx));
- let font_id = cx
- .test_platform
- .text_system()
- .font_id(&font("Helvetica"))
- .unwrap();
+ let font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
let text = "one two three\nfour five six\nseven eight";
@@ -1032,7 +1032,7 @@ mod tests {
display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
MultiBuffer,
};
- use gpui::{font, px, test::observe, Platform};
+ use gpui::{font, px, test::observe};
use rand::prelude::*;
use settings::SettingsStore;
use smol::stream::StreamExt;
@@ -12,7 +12,7 @@ use futures::StreamExt;
use gpui::{
div,
serde_json::{self, json},
- Div, Flatten, Platform, TestAppContext, VisualTestContext, WindowBounds, WindowOptions,
+ Div, Flatten, TestAppContext, VisualTestContext, WindowBounds, WindowOptions,
};
use indoc::indoc;
use language::{
@@ -3238,9 +3238,7 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) {
the lazy dog"});
cx.update_editor(|e, cx| e.copy(&Copy, cx));
assert_eq!(
- cx.test_platform
- .read_from_clipboard()
- .map(|item| item.text().to_owned()),
+ cx.read_from_clipboard().map(|item| item.text().to_owned()),
Some("fox jumps over\n".to_owned())
);
@@ -15,10 +15,10 @@ use smol::future::FutureExt;
pub use test_context::*;
use crate::{
- current_platform, image_cache::ImageCache, Action, ActionRegistry, Any, AnyView,
- AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
+ current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
+ AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
- ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform,
+ ForegroundExecutor, KeyBinding, Keymap, LayoutId, Menu, PathPromptOptions, Pixels, Platform,
PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext,
WindowHandle, WindowId,
@@ -278,6 +278,8 @@ impl AppContext {
}),
});
+ init_app_menus(platform.as_ref(), &mut *app.borrow_mut());
+
platform.on_quit(Box::new({
let cx = app.clone();
move || {
@@ -1059,6 +1061,19 @@ impl AppContext {
.contains_key(&action.as_any().type_id())
}
+ pub fn set_menus(&mut self, menus: Vec<Menu>) {
+ if let Some(active_window) = self.active_window() {
+ active_window
+ .update(self, |_, cx| {
+ cx.platform
+ .set_menus(menus, Some(&cx.window.current_frame.dispatch_tree));
+ })
+ .ok();
+ } else {
+ self.platform.set_menus(menus, None);
+ }
+ }
+
pub fn dispatch_action(&mut self, action: &dyn Action) {
self.propagate_event = true;
@@ -1,9 +1,10 @@
use crate::{
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
- BackgroundExecutor, Bounds, Context, Div, Entity, EventEmitter, ForegroundExecutor, InputEvent,
- KeyDownEvent, Keystroke, Model, ModelContext, Pixels, PlatformWindow, Point, Render, Result,
- Size, Task, TestDispatcher, TestPlatform, TestWindow, TestWindowHandlers, View, ViewContext,
- VisualContext, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+ BackgroundExecutor, Bounds, ClipboardItem, Context, Div, Entity, EventEmitter,
+ ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext, Pixels, Platform,
+ PlatformWindow, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
+ TestWindowHandlers, TextSystem, View, ViewContext, VisualContext, WindowBounds, WindowContext,
+ WindowHandle, WindowOptions,
};
use anyhow::{anyhow, bail};
use futures::{Stream, StreamExt};
@@ -16,6 +17,7 @@ pub struct TestAppContext {
pub foreground_executor: ForegroundExecutor,
pub dispatcher: TestDispatcher,
pub test_platform: Rc<TestPlatform>,
+ text_system: Arc<TextSystem>,
}
impl Context for TestAppContext {
@@ -82,6 +84,7 @@ impl TestAppContext {
let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone());
let asset_source = Arc::new(());
let http_client = util::http::FakeHttpClient::with_404_response();
+ let text_system = Arc::new(TextSystem::new(platform.text_system()));
Self {
app: AppContext::new(platform.clone(), asset_source, http_client),
@@ -89,6 +92,7 @@ impl TestAppContext {
foreground_executor,
dispatcher: dispatcher.clone(),
test_platform: platform,
+ text_system,
}
}
@@ -155,6 +159,18 @@ impl TestAppContext {
(view, Box::leak(cx))
}
+ pub fn text_system(&self) -> &Arc<TextSystem> {
+ &self.text_system
+ }
+
+ pub fn write_to_clipboard(&self, item: ClipboardItem) {
+ self.test_platform.write_to_clipboard(item)
+ }
+
+ pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
+ self.test_platform.read_from_clipboard()
+ }
+
pub fn simulate_new_path_selection(
&self,
select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
@@ -6,10 +6,10 @@ mod mac;
mod test;
use crate::{
- point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId,
- FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, LineLayout,
- Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene,
- SharedString, Size, TaskLabel,
+ point, size, Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, DispatchTree,
+ Font, FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent,
+ LineLayout, Pixels, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
+ Scene, SharedString, Size, TaskLabel,
};
use anyhow::{anyhow, bail};
use async_task::Runnable;
@@ -46,7 +46,7 @@ pub(crate) fn current_platform() -> Rc<dyn Platform> {
Rc::new(MacPlatform::new())
}
-pub trait Platform: 'static {
+pub(crate) trait Platform: 'static {
fn background_executor(&self) -> BackgroundExecutor;
fn foreground_executor(&self) -> ForegroundExecutor;
fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
@@ -92,6 +92,7 @@ pub trait Platform: 'static {
fn on_reopen(&self, callback: Box<dyn FnMut()>);
fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
+ fn set_menus(&self, menus: Vec<Menu>, dispatch_tree: Option<&DispatchTree>);
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>);
fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>);
@@ -52,7 +52,7 @@ pub enum OsAction {
Redo,
}
-pub(crate) fn init(platform: &dyn Platform, cx: &mut AppContext) {
+pub(crate) fn init_app_menus(platform: &dyn Platform, cx: &mut AppContext) {
platform.on_will_open_app_menu(Box::new({
let cx = cx.to_async();
move || {
@@ -1,8 +1,8 @@
-use super::BoolExt;
+use super::{events::key_to_native, BoolExt};
use crate::{
- Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
- ForegroundExecutor, InputEvent, KeystrokeMatcher, MacDispatcher, MacDisplay, MacDisplayLinker,
- MacTextSystem, MacWindow, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
+ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DispatchTree,
+ DisplayId, ForegroundExecutor, InputEvent, MacDispatcher, MacDisplay, MacDisplayLinker,
+ MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
};
use anyhow::anyhow;
@@ -10,10 +10,10 @@ use block::ConcreteBlock;
use cocoa::{
appkit::{
NSApplication, NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
- NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard, NSPasteboardTypeString,
- NSSavePanel, NSWindow,
+ NSEventModifierFlags, NSMenu, NSMenuItem, NSModalResponse, NSOpenPanel, NSPasteboard,
+ NSPasteboardTypeString, NSSavePanel, NSWindow,
},
- base::{id, nil, BOOL, YES},
+ base::{id, nil, selector, BOOL, YES},
foundation::{
NSArray, NSAutoreleasePool, NSBundle, NSData, NSInteger, NSProcessInfo, NSString,
NSUInteger, NSURL,
@@ -201,151 +201,155 @@ impl MacPlatform {
}
}
- // unsafe fn create_menu_bar(
- // &self,
- // menus: Vec<Menu>,
- // delegate: id,
- // actions: &mut Vec<Box<dyn Action>>,
- // keystroke_matcher: &KeymapMatcher,
- // ) -> id {
- // let application_menu = NSMenu::new(nil).autorelease();
- // application_menu.setDelegate_(delegate);
-
- // for menu_config in menus {
- // let menu = NSMenu::new(nil).autorelease();
- // menu.setTitle_(ns_string(menu_config.name));
- // menu.setDelegate_(delegate);
-
- // for item_config in menu_config.items {
- // menu.addItem_(self.create_menu_item(
- // item_config,
- // delegate,
- // actions,
- // keystroke_matcher,
- // ));
- // }
-
- // let menu_item = NSMenuItem::new(nil).autorelease();
- // menu_item.setSubmenu_(menu);
- // application_menu.addItem_(menu_item);
-
- // if menu_config.name == "Window" {
- // let app: id = msg_send![APP_CLASS, sharedApplication];
- // app.setWindowsMenu_(menu);
- // }
- // }
-
- // application_menu
- // }
+ unsafe fn create_menu_bar(
+ &self,
+ menus: Vec<Menu>,
+ delegate: id,
+ actions: &mut Vec<Box<dyn Action>>,
+ dispatch_tree: Option<&DispatchTree>,
+ ) -> id {
+ let application_menu = NSMenu::new(nil).autorelease();
+ application_menu.setDelegate_(delegate);
+
+ for menu_config in menus {
+ let menu = NSMenu::new(nil).autorelease();
+ menu.setTitle_(ns_string(menu_config.name));
+ menu.setDelegate_(delegate);
+
+ for item_config in menu_config.items {
+ menu.addItem_(self.create_menu_item(item_config, delegate, actions, dispatch_tree));
+ }
+
+ let menu_item = NSMenuItem::new(nil).autorelease();
+ menu_item.setSubmenu_(menu);
+ application_menu.addItem_(menu_item);
+
+ if menu_config.name == "Window" {
+ let app: id = msg_send![APP_CLASS, sharedApplication];
+ app.setWindowsMenu_(menu);
+ }
+ }
+
+ application_menu
+ }
unsafe fn create_menu_item(
&self,
item: MenuItem,
delegate: id,
actions: &mut Vec<Box<dyn Action>>,
- keystroke_matcher: &KeystrokeMatcher,
+ dispatch_tree: Option<&DispatchTree>,
) -> id {
- todo!()
- // match item {
- // MenuItem::Separator => NSMenuItem::separatorItem(nil),
- // MenuItem::Action {
- // name,
- // action,
- // os_action,
- // } => {
- // // TODO
- // let keystrokes = keystroke_matcher
- // .bindings_for_action(action.id())
- // .find(|binding| binding.action().eq(action.as_ref()))
- // .map(|binding| binding.keystrokes());
- // let selector = match os_action {
- // Some(crate::OsAction::Cut) => selector("cut:"),
- // Some(crate::OsAction::Copy) => selector("copy:"),
- // Some(crate::OsAction::Paste) => selector("paste:"),
- // Some(crate::OsAction::SelectAll) => selector("selectAll:"),
- // Some(crate::OsAction::Undo) => selector("undo:"),
- // Some(crate::OsAction::Redo) => selector("redo:"),
- // None => selector("handleGPUIMenuItem:"),
- // };
-
- // let item;
- // if let Some(keystrokes) = keystrokes {
- // if keystrokes.len() == 1 {
- // let keystroke = &keystrokes[0];
- // let mut mask = NSEventModifierFlags::empty();
- // for (modifier, flag) in &[
- // (keystroke.cmd, NSEventModifierFlags::NSCommandKeyMask),
- // (keystroke.ctrl, NSEventModifierFlags::NSControlKeyMask),
- // (keystroke.alt, NSEventModifierFlags::NSAlternateKeyMask),
- // (keystroke.shift, NSEventModifierFlags::NSShiftKeyMask),
- // ] {
- // if *modifier {
- // mask |= *flag;
- // }
- // }
-
- // item = NSMenuItem::alloc(nil)
- // .initWithTitle_action_keyEquivalent_(
- // ns_string(name),
- // selector,
- // ns_string(key_to_native(&keystroke.key).as_ref()),
- // )
- // .autorelease();
- // item.setKeyEquivalentModifierMask_(mask);
- // }
- // // For multi-keystroke bindings, render the keystroke as part of the title.
- // else {
- // use std::fmt::Write;
-
- // let mut name = format!("{name} [");
- // for (i, keystroke) in keystrokes.iter().enumerate() {
- // if i > 0 {
- // name.push(' ');
- // }
- // write!(&mut name, "{}", keystroke).unwrap();
- // }
- // name.push(']');
-
- // item = NSMenuItem::alloc(nil)
- // .initWithTitle_action_keyEquivalent_(
- // ns_string(&name),
- // selector,
- // ns_string(""),
- // )
- // .autorelease();
- // }
- // } else {
- // item = NSMenuItem::alloc(nil)
- // .initWithTitle_action_keyEquivalent_(
- // ns_string(name),
- // selector,
- // ns_string(""),
- // )
- // .autorelease();
- // }
-
- // let tag = actions.len() as NSInteger;
- // let _: () = msg_send![item, setTag: tag];
- // actions.push(action);
- // item
- // }
- // MenuItem::Submenu(Menu { name, items }) => {
- // let item = NSMenuItem::new(nil).autorelease();
- // let submenu = NSMenu::new(nil).autorelease();
- // submenu.setDelegate_(delegate);
- // for item in items {
- // submenu.addItem_(self.create_menu_item(
- // item,
- // delegate,
- // actions,
- // keystroke_matcher,
- // ));
- // }
- // item.setSubmenu_(submenu);
- // item.setTitle_(ns_string(name));
- // item
- // }
- // }
+ match item {
+ MenuItem::Separator => NSMenuItem::separatorItem(nil),
+ MenuItem::Action {
+ name,
+ action,
+ os_action,
+ } => {
+ let bindings = dispatch_tree
+ .map(|tree| tree.bindings_for_action(action.as_ref(), &tree.context_stack))
+ .unwrap_or_default();
+ let keystrokes = bindings
+ .iter()
+ .find(|binding| binding.action().partial_eq(action.as_ref()))
+ .map(|binding| binding.keystrokes());
+
+ let selector = match os_action {
+ Some(crate::OsAction::Cut) => selector("cut:"),
+ Some(crate::OsAction::Copy) => selector("copy:"),
+ Some(crate::OsAction::Paste) => selector("paste:"),
+ Some(crate::OsAction::SelectAll) => selector("selectAll:"),
+ Some(crate::OsAction::Undo) => selector("undo:"),
+ Some(crate::OsAction::Redo) => selector("redo:"),
+ None => selector("handleGPUIMenuItem:"),
+ };
+
+ let item;
+ if let Some(keystrokes) = keystrokes {
+ if keystrokes.len() == 1 {
+ let keystroke = &keystrokes[0];
+ let mut mask = NSEventModifierFlags::empty();
+ for (modifier, flag) in &[
+ (
+ keystroke.modifiers.command,
+ NSEventModifierFlags::NSCommandKeyMask,
+ ),
+ (
+ keystroke.modifiers.control,
+ NSEventModifierFlags::NSControlKeyMask,
+ ),
+ (
+ keystroke.modifiers.alt,
+ NSEventModifierFlags::NSAlternateKeyMask,
+ ),
+ (
+ keystroke.modifiers.shift,
+ NSEventModifierFlags::NSShiftKeyMask,
+ ),
+ ] {
+ if *modifier {
+ mask |= *flag;
+ }
+ }
+
+ item = NSMenuItem::alloc(nil)
+ .initWithTitle_action_keyEquivalent_(
+ ns_string(name),
+ selector,
+ ns_string(key_to_native(&keystroke.key).as_ref()),
+ )
+ .autorelease();
+ item.setKeyEquivalentModifierMask_(mask);
+ }
+ // For multi-keystroke bindings, render the keystroke as part of the title.
+ else {
+ use std::fmt::Write;
+
+ let mut name = format!("{name} [");
+ for (i, keystroke) in keystrokes.iter().enumerate() {
+ if i > 0 {
+ name.push(' ');
+ }
+ write!(&mut name, "{}", keystroke).unwrap();
+ }
+ name.push(']');
+
+ item = NSMenuItem::alloc(nil)
+ .initWithTitle_action_keyEquivalent_(
+ ns_string(&name),
+ selector,
+ ns_string(""),
+ )
+ .autorelease();
+ }
+ } else {
+ item = NSMenuItem::alloc(nil)
+ .initWithTitle_action_keyEquivalent_(
+ ns_string(name),
+ selector,
+ ns_string(""),
+ )
+ .autorelease();
+ }
+
+ let tag = actions.len() as NSInteger;
+ let _: () = msg_send![item, setTag: tag];
+ actions.push(action);
+ item
+ }
+ MenuItem::Submenu(Menu { name, items }) => {
+ let item = NSMenuItem::new(nil).autorelease();
+ let submenu = NSMenu::new(nil).autorelease();
+ submenu.setDelegate_(delegate);
+ for item in items {
+ submenu.addItem_(self.create_menu_item(item, delegate, actions, dispatch_tree));
+ }
+ item.setSubmenu_(submenu);
+ item.setTitle_(ns_string(name));
+ item
+ }
+ }
}
}
@@ -633,6 +637,18 @@ impl Platform for MacPlatform {
self.0.lock().event = Some(callback);
}
+ fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
+ self.0.lock().menu_command = Some(callback);
+ }
+
+ fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
+ self.0.lock().will_open_menu = Some(callback);
+ }
+
+ fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+ self.0.lock().validate_menu_command = Some(callback);
+ }
+
fn os_name(&self) -> &'static str {
"macOS"
}
@@ -675,6 +691,15 @@ impl Platform for MacPlatform {
}
}
+ fn set_menus(&self, menus: Vec<Menu>, dispatch_tree: Option<&DispatchTree>) {
+ unsafe {
+ let app: id = msg_send![APP_CLASS, sharedApplication];
+ let mut state = self.0.lock();
+ let actions = &mut state.menu_actions;
+ app.setMainMenu_(self.create_menu_bar(menus, app.delegate(), actions, dispatch_tree));
+ }
+ }
+
fn local_timezone(&self) -> UtcOffset {
unsafe {
let local_timezone: id = msg_send![class!(NSTimeZone), localTimeZone];
@@ -683,32 +708,6 @@ impl Platform for MacPlatform {
}
}
- fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
- self.0.lock().menu_command = Some(callback);
- }
-
- fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
- self.0.lock().will_open_menu = Some(callback);
- }
-
- fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
- self.0.lock().validate_menu_command = Some(callback);
- }
-
- // fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
- // unsafe {
- // let app: id = msg_send![APP_CLASS, sharedApplication];
- // let mut state = self.0.lock();
- // let actions = &mut state.menu_actions;
- // app.setMainMenu_(self.create_menu_bar(
- // menus,
- // app.delegate(),
- // actions,
- // keystroke_matcher,
- // ));
- // }
- // }
-
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
unsafe {
let bundle: id = NSBundle::mainBundle();
@@ -1,6 +1,7 @@
use crate::{
- AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor,
- Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
+ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DispatchTree, DisplayId,
+ ForegroundExecutor, Platform, PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow,
+ WindowOptions,
};
use anyhow::{anyhow, Result};
use collections::VecDeque;
@@ -205,6 +206,10 @@ impl Platform for TestPlatform {
unimplemented!()
}
+ fn set_menus(&self, _menus: Vec<crate::Menu>, _dispatch_tree: Option<&DispatchTree>) {
+ unimplemented!()
+ }
+
fn on_app_menu_action(&self, _callback: Box<dyn FnMut(&dyn crate::Action)>) {
unimplemented!()
}
@@ -132,39 +132,3 @@ pub fn load_default_keymap(cx: &mut AppContext) {
// KeymapFile::load_asset(asset_path, cx).unwrap();
// }
}
-
-pub fn handle_keymap_file_changes(
- mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
- cx: &mut AppContext,
-) {
- cx.spawn(move |cx| async move {
- // let mut settings_subscription = None;
- while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
- if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() {
- cx.update(|cx| reload_keymaps(cx, &keymap_content)).ok();
-
- // todo!()
- // let mut old_base_keymap = cx.read(|cx| *settings::get::<BaseKeymap>(cx));
- // drop(settings_subscription);
- // settings_subscription = Some(cx.update(|cx| {
- // cx.observe_global::<SettingsStore, _>(move |cx| {
- // let new_base_keymap = *settings::get::<BaseKeymap>(cx);
- // if new_base_keymap != old_base_keymap {
- // old_base_keymap = new_base_keymap.clone();
- // reload_keymaps(cx, &keymap_content);
- // }
- // })
- // }));
- }
- }
- })
- .detach();
-}
-
-fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) {
- // todo!()
- // cx.clear_bindings();
- load_default_keymap(cx);
- keymap_content.clone().add_to_cx(cx).log_err();
- // cx.set_menus(menus::menus());
-}
@@ -0,0 +1,175 @@
+use gpui::{Menu, MenuItem, OsAction};
+
+#[cfg(target_os = "macos")]
+pub fn app_menus() -> Vec<Menu<'static>> {
+ vec![
+ Menu {
+ name: "Zed",
+ items: vec![
+ MenuItem::action("About Zedβ¦", super::About),
+ MenuItem::action("Check for Updates", auto_update::Check),
+ MenuItem::separator(),
+ MenuItem::submenu(Menu {
+ name: "Preferences",
+ items: vec![
+ MenuItem::action("Open Settings", super::OpenSettings),
+ MenuItem::action("Open Key Bindings", super::OpenKeymap),
+ MenuItem::action("Open Default Settings", super::OpenDefaultSettings),
+ MenuItem::action("Open Default Key Bindings", super::OpenDefaultKeymap),
+ MenuItem::action("Open Local Settings", super::OpenLocalSettings),
+ MenuItem::action("Select Theme", theme_selector::Toggle),
+ ],
+ }),
+ MenuItem::action("Install CLI", install_cli::Install),
+ MenuItem::separator(),
+ MenuItem::action("Hide Zed", super::Hide),
+ MenuItem::action("Hide Others", super::HideOthers),
+ MenuItem::action("Show All", super::ShowAll),
+ MenuItem::action("Quit", super::Quit),
+ ],
+ },
+ Menu {
+ name: "File",
+ items: vec![
+ MenuItem::action("New", workspace::NewFile),
+ MenuItem::action("New Window", workspace::NewWindow),
+ MenuItem::separator(),
+ MenuItem::action("Openβ¦", workspace::Open),
+ // MenuItem::action("Open Recent...", recent_projects::OpenRecent),
+ MenuItem::separator(),
+ MenuItem::action("Add Folder to Projectβ¦", workspace::AddFolderToProject),
+ MenuItem::action("Save", workspace::Save { save_intent: None }),
+ MenuItem::action("Save Asβ¦", workspace::SaveAs),
+ MenuItem::action("Save All", workspace::SaveAll { save_intent: None }),
+ MenuItem::action(
+ "Close Editor",
+ workspace::CloseActiveItem { save_intent: None },
+ ),
+ MenuItem::action("Close Window", workspace::CloseWindow),
+ ],
+ },
+ Menu {
+ name: "Edit",
+ items: vec![
+ MenuItem::os_action("Undo", editor::Undo, OsAction::Undo),
+ MenuItem::os_action("Redo", editor::Redo, OsAction::Redo),
+ MenuItem::separator(),
+ MenuItem::os_action("Cut", editor::Cut, OsAction::Cut),
+ MenuItem::os_action("Copy", editor::Copy, OsAction::Copy),
+ MenuItem::os_action("Paste", editor::Paste, OsAction::Paste),
+ MenuItem::separator(),
+ MenuItem::action("Find", search::buffer_search::Deploy { focus: true }),
+ MenuItem::action("Find In Project", workspace::NewSearch),
+ MenuItem::separator(),
+ MenuItem::action("Toggle Line Comment", editor::ToggleComments::default()),
+ MenuItem::action("Emoji & Symbols", editor::ShowCharacterPalette),
+ ],
+ },
+ Menu {
+ name: "Selection",
+ items: vec![
+ MenuItem::os_action("Select All", editor::SelectAll, OsAction::SelectAll),
+ MenuItem::action("Expand Selection", editor::SelectLargerSyntaxNode),
+ MenuItem::action("Shrink Selection", editor::SelectSmallerSyntaxNode),
+ MenuItem::separator(),
+ MenuItem::action("Add Cursor Above", editor::AddSelectionAbove),
+ MenuItem::action("Add Cursor Below", editor::AddSelectionBelow),
+ MenuItem::action(
+ "Select Next Occurrence",
+ editor::SelectNext {
+ replace_newest: false,
+ },
+ ),
+ MenuItem::separator(),
+ MenuItem::action("Move Line Up", editor::MoveLineUp),
+ MenuItem::action("Move Line Down", editor::MoveLineDown),
+ MenuItem::action("Duplicate Selection", editor::DuplicateLine),
+ ],
+ },
+ Menu {
+ name: "View",
+ items: vec![
+ MenuItem::action("Zoom In", super::IncreaseBufferFontSize),
+ MenuItem::action("Zoom Out", super::DecreaseBufferFontSize),
+ MenuItem::action("Reset Zoom", super::ResetBufferFontSize),
+ MenuItem::separator(),
+ MenuItem::action("Toggle Left Dock", workspace::ToggleLeftDock),
+ MenuItem::action("Toggle Right Dock", workspace::ToggleRightDock),
+ MenuItem::action("Toggle Bottom Dock", workspace::ToggleBottomDock),
+ MenuItem::action("Close All Docks", workspace::CloseAllDocks),
+ MenuItem::submenu(Menu {
+ name: "Editor Layout",
+ items: vec![
+ MenuItem::action("Split Up", workspace::SplitUp),
+ MenuItem::action("Split Down", workspace::SplitDown),
+ MenuItem::action("Split Left", workspace::SplitLeft),
+ MenuItem::action("Split Right", workspace::SplitRight),
+ ],
+ }),
+ MenuItem::separator(),
+ MenuItem::action("Project Panel", project_panel::ToggleFocus),
+ MenuItem::action("Command Palette", command_palette::Toggle),
+ MenuItem::action("Diagnostics", diagnostics::Deploy),
+ MenuItem::separator(),
+ ],
+ },
+ Menu {
+ name: "Go",
+ items: vec![
+ MenuItem::action("Back", workspace::GoBack),
+ MenuItem::action("Forward", workspace::GoForward),
+ MenuItem::separator(),
+ MenuItem::action("Go to File", file_finder::Toggle),
+ // MenuItem::action("Go to Symbol in Project", project_symbols::Toggle),
+ MenuItem::action("Go to Symbol in Editor", outline::Toggle),
+ MenuItem::action("Go to Definition", editor::GoToDefinition),
+ MenuItem::action("Go to Type Definition", editor::GoToTypeDefinition),
+ MenuItem::action("Find All References", editor::FindAllReferences),
+ MenuItem::action("Go to Line/Column", go_to_line::Toggle),
+ MenuItem::separator(),
+ MenuItem::action("Next Problem", editor::GoToDiagnostic),
+ MenuItem::action("Previous Problem", editor::GoToPrevDiagnostic),
+ ],
+ },
+ Menu {
+ name: "Window",
+ items: vec![
+ MenuItem::action("Minimize", super::Minimize),
+ MenuItem::action("Zoom", super::Zoom),
+ MenuItem::separator(),
+ ],
+ },
+ Menu {
+ name: "Help",
+ items: vec![
+ MenuItem::action("Command Palette", command_palette::Toggle),
+ MenuItem::separator(),
+ MenuItem::action("View Telemetry", crate::OpenTelemetryLog),
+ MenuItem::action("View Dependency Licenses", crate::OpenLicenses),
+ MenuItem::action("Show Welcome", workspace::Welcome),
+ MenuItem::separator(),
+ // todo!(): Needs `feedback2` crate.
+ // MenuItem::action("Give us feedback", feedback::feedback_editor::GiveFeedback),
+ // MenuItem::action(
+ // "Copy System Specs Into Clipboard",
+ // feedback::CopySystemSpecsIntoClipboard,
+ // ),
+ // MenuItem::action("File Bug Report", feedback::FileBugReport),
+ // MenuItem::action("Request Feature", feedback::RequestFeature),
+ MenuItem::separator(),
+ MenuItem::action(
+ "Documentation",
+ crate::OpenBrowser {
+ url: "https://zed.dev/docs".into(),
+ },
+ ),
+ MenuItem::action(
+ "Zed Twitter",
+ crate::OpenBrowser {
+ url: "https://twitter.com/zeddotdev".into(),
+ },
+ ),
+ ],
+ },
+ ]
+}
@@ -22,8 +22,7 @@ use node_runtime::RealNodeRuntime;
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use settings::{
- default_settings, handle_keymap_file_changes, handle_settings_file_changes, watch_config_file,
- Settings, SettingsStore,
+ default_settings, handle_settings_file_changes, watch_config_file, Settings, SettingsStore,
};
use simplelog::ConfigBuilder;
use smol::process::Command;
@@ -51,8 +50,9 @@ use uuid::Uuid;
use welcome::{show_welcome_experience, FIRST_OPEN};
use workspace::{AppState, WorkspaceStore};
use zed2::{
- build_window_options, ensure_only_instance, handle_cli_connection, initialize_workspace,
- languages, Assets, IsOnlyInstance, OpenListener, OpenRequest,
+ app_menus, build_window_options, ensure_only_instance, handle_cli_connection,
+ handle_keymap_file_changes, initialize_workspace, languages, Assets, IsOnlyInstance,
+ OpenListener, OpenRequest,
};
mod open_listener;
@@ -224,7 +224,7 @@ fn main() {
// feedback::init(cx);
welcome::init(cx);
- // cx.set_menus(menus::menus());
+ cx.set_menus(app_menus());
initialize_workspace(app_state.clone(), cx);
if stdout_is_a_pty() {
@@ -1,11 +1,13 @@
#![allow(unused_variables, unused_mut)]
//todo!()
+mod app_menus;
mod assets;
pub mod languages;
mod only_instance;
mod open_listener;
+pub use app_menus::*;
pub use assets::*;
use breadcrumbs::Breadcrumbs;
use collections::VecDeque;
@@ -18,8 +20,9 @@ pub use only_instance::*;
pub use open_listener::*;
use anyhow::{anyhow, Context as _};
+use futures::{channel::mpsc, StreamExt};
use project_panel::ProjectPanel;
-use settings::{initial_local_settings_content, Settings};
+use settings::{initial_local_settings_content, load_default_keymap, KeymapFile, Settings};
use std::{borrow::Cow, ops::Deref, sync::Arc};
use terminal_view::terminal_panel::TerminalPanel;
use util::{
@@ -561,6 +564,42 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
.detach();
}
+pub fn handle_keymap_file_changes(
+ mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
+ cx: &mut AppContext,
+) {
+ cx.spawn(move |cx| async move {
+ // let mut settings_subscription = None;
+ while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
+ if let Some(keymap_content) = KeymapFile::parse(&user_keymap_content).log_err() {
+ cx.update(|cx| reload_keymaps(cx, &keymap_content)).ok();
+
+ // todo!()
+ // let mut old_base_keymap = cx.read(|cx| *settings::get::<BaseKeymap>(cx));
+ // drop(settings_subscription);
+ // settings_subscription = Some(cx.update(|cx| {
+ // cx.observe_global::<SettingsStore, _>(move |cx| {
+ // let new_base_keymap = *settings::get::<BaseKeymap>(cx);
+ // if new_base_keymap != old_base_keymap {
+ // old_base_keymap = new_base_keymap.clone();
+ // reload_keymaps(cx, &keymap_content);
+ // }
+ // })
+ // }));
+ }
+ }
+ })
+ .detach();
+}
+
+fn reload_keymaps(cx: &mut AppContext, keymap_content: &KeymapFile) {
+ // todo!()
+ // cx.clear_bindings();
+ load_default_keymap(cx);
+ keymap_content.clone().add_to_cx(cx).log_err();
+ cx.set_menus(app_menus());
+}
+
fn open_local_settings_file(
workspace: &mut Workspace,
_: &OpenLocalSettings,