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.
This commit is contained in:
Ryan Hawkins 2025-03-18 22:27:09 -06:00 committed by GitHub
parent 382f9f6151
commit 0f5a3afe94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 99 additions and 21 deletions

15
Cargo.lock generated
View file

@ -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",

View file

@ -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" }

View file

@ -53,7 +53,9 @@
"context": "Prompt",
"bindings": {
"left": "menu::SelectPrevious",
"right": "menu::SelectNext"
"right": "menu::SelectNext",
"h": "menu::SelectPrevious",
"l": "menu::SelectNext"
}
},
{

View file

@ -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,

View file

@ -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.

View file

@ -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<A: Asset>(&mut self, source: &A::Source) {
let asset_id = (TypeId::of::<A>(), hash(source));

View file

@ -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

View file

@ -0,0 +1 @@
../../LICENSE-GPL

View file

@ -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::<SettingsStore>(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<String>,
@ -67,7 +79,7 @@ pub struct FallbackPromptRenderer {
detail: Option<Entity<Markdown>>,
}
impl FallbackPromptRenderer {
impl ZedPromptRenderer {
fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
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<Self>) -> 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<PromptResponse> for FallbackPromptRenderer {}
impl EventEmitter<PromptResponse> for ZedPromptRenderer {}
impl Focusable for FallbackPromptRenderer {
impl Focusable for ZedPromptRenderer {
fn focus_handle(&self, _: &crate::App) -> FocusHandle {
self.focus.clone()
}

View file

@ -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<String, String>,
pub show_user_picture: bool,
pub max_tabs: Option<NonZeroUsize>,
@ -147,6 +148,13 @@ pub struct WorkspaceSettingsContent {
///
/// Default: true
pub use_system_path_prompts: Option<bool>,
/// 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<bool>,
/// Aliases for the command palette. When you type a key in this map,
/// it will be assumed to equal the value.
///

View file

@ -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

View file

@ -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);

View file

@ -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;