From 0f5a3afe94fb8f7557a5deaf0473a47b19fbb040 Mon Sep 17 00:00:00 2001 From: Ryan Hawkins Date: Tue, 18 Mar 2025 22:27:09 -0600 Subject: [PATCH] Support built-in Zed prompts for all platforms (#26201) This pull request does two things: 1. Adds a setting to force Zed to use the built-in prompts, instead of the system provided ones. I've personally found the system prompts on macOS often fail to respond to keyboard input, are slow to render initially, and don't match Zed's style. 2. Makes the previously Linux-only Zed provided prompts available to everybody using the above setting. Release Notes: - Added support for a built-in prompting system, regardless of platform. Use the new `use_system_prompts` setting to control whether to use the system provided prompts or Zed's built-in system. Note that on Linux, this setting has no effect, as Linux doesn't have a system prompting mechanism. --- Cargo.lock | 15 +++++++- Cargo.toml | 2 + assets/keymaps/default-linux.json | 4 +- assets/keymaps/default-macos.json | 10 +++++ assets/settings/default.json | 5 +++ crates/gpui/src/app.rs | 5 +++ crates/ui_prompt/Cargo.toml | 24 ++++++++++++ crates/ui_prompt/LICENSE-GPL | 1 + .../src/ui_prompt.rs} | 38 ++++++++++++------- crates/workspace/src/workspace_settings.rs | 8 ++++ crates/zed/Cargo.toml | 2 +- crates/zed/src/main.rs | 4 +- crates/zed/src/zed.rs | 2 - 13 files changed, 99 insertions(+), 21 deletions(-) create mode 100644 crates/ui_prompt/Cargo.toml create mode 120000 crates/ui_prompt/LICENSE-GPL rename crates/{zed/src/zed/linux_prompts.rs => ui_prompt/src/ui_prompt.rs} (88%) diff --git a/Cargo.lock b/Cargo.lock index 91c14858c4..2e8fa86d77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15022,6 +15022,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ui_prompt" +version = "0.1.0" +dependencies = [ + "gpui", + "markdown", + "menu", + "settings", + "theme", + "ui", + "workspace", +] + [[package]] name = "unicase" version = "2.8.1" @@ -17381,7 +17394,6 @@ dependencies = [ "languages", "libc", "log", - "markdown", "markdown_preview", "menu", "migrator", @@ -17433,6 +17445,7 @@ dependencies = [ "tree-sitter-md", "tree-sitter-rust", "ui", + "ui_prompt", "url", "urlencoding", "util", diff --git a/Cargo.toml b/Cargo.toml index 22e26ff796..d803c3cc1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,6 +160,7 @@ members = [ "crates/ui", "crates/ui_input", "crates/ui_macros", + "crates/ui_prompt", "crates/util", "crates/util_macros", "crates/vim", @@ -362,6 +363,7 @@ toolchain_selector = { path = "crates/toolchain_selector" } ui = { path = "crates/ui" } ui_input = { path = "crates/ui_input" } ui_macros = { path = "crates/ui_macros" } +ui_prompt = { path = "crates/ui_prompt" } util = { path = "crates/util" } util_macros = { path = "crates/util_macros" } vim = { path = "crates/vim" } diff --git a/assets/keymaps/default-linux.json b/assets/keymaps/default-linux.json index de4d371bb4..2d01562413 100644 --- a/assets/keymaps/default-linux.json +++ b/assets/keymaps/default-linux.json @@ -53,7 +53,9 @@ "context": "Prompt", "bindings": { "left": "menu::SelectPrevious", - "right": "menu::SelectNext" + "right": "menu::SelectNext", + "h": "menu::SelectPrevious", + "l": "menu::SelectNext" } }, { diff --git a/assets/keymaps/default-macos.json b/assets/keymaps/default-macos.json index 5a4ce720f3..5d956ab591 100644 --- a/assets/keymaps/default-macos.json +++ b/assets/keymaps/default-macos.json @@ -705,6 +705,16 @@ "ctrl-]": "assistant::CycleNextInlineAssist" } }, + { + "context": "Prompt", + "use_key_equivalents": true, + "bindings": { + "left": "menu::SelectPrevious", + "right": "menu::SelectNext", + "h": "menu::SelectPrevious", + "l": "menu::SelectNext" + } + }, { "context": "ProjectSearchBar && !in_replace", "use_key_equivalents": true, diff --git a/assets/settings/default.json b/assets/settings/default.json index 70d016fb79..0b2d67afa7 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -136,6 +136,11 @@ // Whether to use the system provided dialogs for Open and Save As. // When set to false, Zed will use the built-in keyboard-first pickers. "use_system_path_prompts": true, + // Whether to use the system provided dialogs for prompts, such as confirmation + // prompts. + // When set to false, Zed will use its built-in prompts. Note that on Linux, + // this option is ignored and Zed will always use the built-in prompts. + "use_system_prompts": true, // Whether the cursor blinks in the editor. "cursor_blink": true, // Cursor shape for the default editor. diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2129daae84..04cb010ed6 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1526,6 +1526,11 @@ impl App { self.prompt_builder = Some(PromptBuilder::Custom(Box::new(renderer))) } + /// Reset the prompt builder to the default implementation. + pub fn reset_prompt_builder(&mut self) { + self.prompt_builder = Some(PromptBuilder::Default); + } + /// Remove an asset from GPUI's cache pub fn remove_asset(&mut self, source: &A::Source) { let asset_id = (TypeId::of::(), hash(source)); diff --git a/crates/ui_prompt/Cargo.toml b/crates/ui_prompt/Cargo.toml new file mode 100644 index 0000000000..55a9828843 --- /dev/null +++ b/crates/ui_prompt/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "ui_prompt" +version = "0.1.0" +edition.workspace = true +publish.workspace = true +license = "GPL-3.0-or-later" + +[lints] +workspace = true + +[lib] +path = "src/ui_prompt.rs" + +[features] +default = [] + +[dependencies] +gpui.workspace = true +markdown.workspace = true +menu.workspace = true +settings.workspace = true +theme.workspace = true +ui.workspace = true +workspace.workspace = true diff --git a/crates/ui_prompt/LICENSE-GPL b/crates/ui_prompt/LICENSE-GPL new file mode 120000 index 0000000000..89e542f750 --- /dev/null +++ b/crates/ui_prompt/LICENSE-GPL @@ -0,0 +1 @@ +../../LICENSE-GPL \ No newline at end of file diff --git a/crates/zed/src/zed/linux_prompts.rs b/crates/ui_prompt/src/ui_prompt.rs similarity index 88% rename from crates/zed/src/zed/linux_prompts.rs rename to crates/ui_prompt/src/ui_prompt.rs index 20cd14c444..780a94cff3 100644 --- a/crates/zed/src/zed/linux_prompts.rs +++ b/crates/ui_prompt/src/ui_prompt.rs @@ -4,20 +4,33 @@ use gpui::{ Refineable, Render, RenderablePromptHandle, SharedString, Styled, TextStyleRefinement, Window, }; use markdown::{Markdown, MarkdownStyle}; -use settings::Settings; +use settings::{Settings, SettingsStore}; use theme::ThemeSettings; use ui::{ h_flex, v_flex, ActiveTheme, ButtonCommon, ButtonStyle, Clickable, ElevationIndex, - FluentBuilder, LabelSize, TintColor, + FluentBuilder, LabelSize, StyledExt, TintColor, }; -use workspace::ui::StyledExt; +use workspace::WorkspaceSettings; pub fn init(cx: &mut App) { - cx.set_prompt_builder(fallback_prompt_renderer) + process_settings(cx); + + cx.observe_global::(process_settings) + .detach(); } + +fn process_settings(cx: &mut App) { + let settings = WorkspaceSettings::get_global(cx); + if settings.use_system_prompts && cfg!(not(any(target_os = "linux", target_os = "freebsd"))) { + cx.reset_prompt_builder(); + } else { + cx.set_prompt_builder(zed_prompt_renderer); + } +} + /// Use this function in conjunction with [App::set_prompt_builder] to force -/// GPUI to always use the fallback prompt renderer. -pub fn fallback_prompt_renderer( +/// GPUI to use the internal prompt system. +fn zed_prompt_renderer( level: PromptLevel, message: &str, detail: Option<&str>, @@ -27,7 +40,7 @@ pub fn fallback_prompt_renderer( cx: &mut App, ) -> RenderablePromptHandle { let renderer = cx.new({ - |cx| FallbackPromptRenderer { + |cx| ZedPromptRenderer { _level: level, message: message.to_string(), actions: actions.iter().map(ToString::to_string).collect(), @@ -57,8 +70,7 @@ pub fn fallback_prompt_renderer( handle.with_view(renderer, window, cx) } -/// The default GPUI fallback for rendering prompts, when the platform doesn't support it. -pub struct FallbackPromptRenderer { +pub struct ZedPromptRenderer { _level: PromptLevel, message: String, actions: Vec, @@ -67,7 +79,7 @@ pub struct FallbackPromptRenderer { detail: Option>, } -impl FallbackPromptRenderer { +impl ZedPromptRenderer { fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context) { cx.emit(PromptResponse(self.active_action_id)); } @@ -113,7 +125,7 @@ impl FallbackPromptRenderer { } } -impl Render for FallbackPromptRenderer { +impl Render for ZedPromptRenderer { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let settings = ThemeSettings::get_global(cx); let font_family = settings.ui_font.family.clone(); @@ -181,9 +193,9 @@ impl Render for FallbackPromptRenderer { } } -impl EventEmitter for FallbackPromptRenderer {} +impl EventEmitter for ZedPromptRenderer {} -impl Focusable for FallbackPromptRenderer { +impl Focusable for ZedPromptRenderer { fn focus_handle(&self, _: &crate::App) -> FocusHandle { self.focus.clone() } diff --git a/crates/workspace/src/workspace_settings.rs b/crates/workspace/src/workspace_settings.rs index 3a86667bd8..8d444ad48f 100644 --- a/crates/workspace/src/workspace_settings.rs +++ b/crates/workspace/src/workspace_settings.rs @@ -19,6 +19,7 @@ pub struct WorkspaceSettings { pub restore_on_startup: RestoreOnStartupBehavior, pub drop_target_size: f32, pub use_system_path_prompts: bool, + pub use_system_prompts: bool, pub command_aliases: HashMap, pub show_user_picture: bool, pub max_tabs: Option, @@ -147,6 +148,13 @@ pub struct WorkspaceSettingsContent { /// /// Default: true pub use_system_path_prompts: Option, + /// Whether to use the system provided prompts. + /// When set to false, Zed will use the built-in prompts. + /// Note that this setting has no effect on Linux, where Zed will always + /// use the built-in prompts. + /// + /// Default: true + pub use_system_prompts: Option, /// Aliases for the command palette. When you type a key in this map, /// it will be assumed to equal the value. /// diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index a7d9e2021a..2a24131d69 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -75,7 +75,6 @@ language_tools.workspace = true languages = { workspace = true, features = ["load-grammars"] } libc.workspace = true log.workspace = true -markdown.workspace = true markdown_preview.workspace = true menu.workspace = true migrator.workspace = true @@ -125,6 +124,7 @@ theme_selector.workspace = true time.workspace = true toolchain_selector.workspace = true ui.workspace = true +ui_prompt.workspace = true url.workspace = true urlencoding.workspace = true util.workspace = true diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3080943d7f..ed2ea1d1bf 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -487,9 +487,6 @@ fn main() { load_embedded_fonts(cx); - #[cfg(any(target_os = "linux", target_os = "freebsd"))] - crate::zed::linux_prompts::init(cx); - app_state.languages.set_theme(cx.theme().clone()); editor::init(cx); image_viewer::init(cx); @@ -498,6 +495,7 @@ fn main() { audio::init(Assets, cx); workspace::init(app_state.clone(), cx); + ui_prompt::init(cx); go_to_line::init(cx); file_finder::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 152cc90f7a..99bfe4992d 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,7 +1,5 @@ mod app_menus; pub mod inline_completion_registry; -#[cfg(any(target_os = "linux", target_os = "freebsd"))] -pub(crate) mod linux_prompts; #[cfg(target_os = "macos")] pub(crate) mod mac_only_instance; mod migrate;