Detailed changes
@@ -14,7 +14,6 @@ dependencies = [
"buffer_diff",
"chrono",
"collections",
- "editor",
"env_logger 0.11.8",
"file_icons",
"futures 0.3.31",
@@ -25,6 +24,7 @@ dependencies = [
"language",
"language_model",
"markdown",
+ "multi_buffer",
"parking_lot",
"portable-pty",
"project",
@@ -38,6 +38,7 @@ dependencies = [
"telemetry",
"tempfile",
"terminal",
+ "text",
"ui",
"url",
"urlencoding",
@@ -272,17 +273,18 @@ dependencies = [
"chrono",
"client",
"collections",
+ "credentials_provider",
"env_logger 0.11.8",
"feature_flags",
"fs",
"futures 0.3.31",
+ "google_ai",
"gpui",
"gpui_tokio",
"http_client",
"indoc",
"language",
"language_model",
- "language_models",
"libc",
"log",
"nix 0.29.0",
@@ -3370,6 +3372,7 @@ dependencies = [
"uuid",
"workspace",
"worktree",
+ "zed_actions",
"zlog",
]
@@ -5519,6 +5522,7 @@ dependencies = [
"tree-sitter-typescript",
"tree-sitter-yaml",
"ui",
+ "ui_input",
"unicode-script",
"unicode-segmentation",
"unicode-width",
@@ -6224,11 +6228,10 @@ dependencies = [
"gpui",
"language",
"menu",
+ "open_path_prompt",
"picker",
"pretty_assertions",
"project",
- "schemars",
- "search",
"serde",
"serde_json",
"settings",
@@ -6237,6 +6240,7 @@ dependencies = [
"ui",
"util",
"workspace",
+ "zed_actions",
"zlog",
]
@@ -7271,6 +7275,7 @@ dependencies = [
"time_format",
"tracing",
"ui",
+ "ui_input",
"unindent",
"util",
"watch",
@@ -8432,11 +8437,11 @@ dependencies = [
"fuzzy",
"gpui",
"language",
+ "platform_title_bar",
"project",
"serde_json",
"serde_json_lenient",
"theme",
- "title_bar",
"ui",
"util",
"util_macros",
@@ -9123,11 +9128,11 @@ version = "0.1.0"
dependencies = [
"anyhow",
"editor",
- "file_finder",
"file_icons",
"fuzzy",
"gpui",
"language",
+ "open_path_prompt",
"picker",
"project",
"settings",
@@ -11136,6 +11141,27 @@ dependencies = [
"thiserror 2.0.17",
]
+[[package]]
+name = "open_path_prompt"
+version = "0.1.0"
+dependencies = [
+ "editor",
+ "file_icons",
+ "futures 0.3.31",
+ "fuzzy",
+ "gpui",
+ "picker",
+ "project",
+ "schemars",
+ "serde",
+ "serde_json",
+ "settings",
+ "theme",
+ "ui",
+ "util",
+ "workspace",
+]
+
[[package]]
name = "open_router"
version = "0.1.0"
@@ -12159,7 +12185,9 @@ dependencies = [
"serde_json",
"theme",
"ui",
+ "ui_input",
"workspace",
+ "zed_actions",
]
[[package]]
@@ -12254,6 +12282,20 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+[[package]]
+name = "platform_title_bar"
+version = "0.1.0"
+dependencies = [
+ "gpui",
+ "settings",
+ "smallvec",
+ "theme",
+ "ui",
+ "windows 0.61.3",
+ "workspace",
+ "zed_actions",
+]
+
[[package]]
name = "plist"
version = "1.8.0"
@@ -13389,7 +13431,6 @@ dependencies = [
"editor",
"extension",
"extension_host",
- "file_finder",
"fs",
"futures 0.3.31",
"fuzzy",
@@ -13401,6 +13442,7 @@ dependencies = [
"markdown",
"menu",
"node_runtime",
+ "open_path_prompt",
"ordered-float 2.10.1",
"paths",
"picker",
@@ -14057,14 +14099,15 @@ dependencies = [
"log",
"menu",
"picker",
+ "platform_title_bar",
"prompt_store",
"release_channel",
"rope",
"serde",
"settings",
"theme",
- "title_bar",
"ui",
+ "ui_input",
"util",
"workspace",
"zed_actions",
@@ -15060,6 +15103,7 @@ dependencies = [
"assets",
"bm25",
"client",
+ "component",
"copilot_ui",
"edit_prediction",
"editor",
@@ -15077,6 +15121,7 @@ dependencies = [
"node_runtime",
"paths",
"picker",
+ "platform_title_bar",
"pretty_assertions",
"project",
"recent_projects",
@@ -15092,7 +15137,6 @@ dependencies = [
"theme",
"title_bar",
"ui",
- "ui_input",
"util",
"workspace",
"zed_actions",
@@ -15389,11 +15433,11 @@ dependencies = [
name = "snippets_ui"
version = "0.1.0"
dependencies = [
- "file_finder",
"file_icons",
"fuzzy",
"gpui",
"language",
+ "open_path_prompt",
"paths",
"picker",
"settings",
@@ -17051,6 +17095,7 @@ dependencies = [
"http_client",
"menu",
"notifications",
+ "platform_title_bar",
"pretty_assertions",
"project",
"recent_projects",
@@ -17325,12 +17370,12 @@ dependencies = [
"anyhow",
"convert_case 0.8.0",
"editor",
- "file_finder",
"futures 0.3.31",
"fuzzy",
"gpui",
"language",
"menu",
+ "open_path_prompt",
"picker",
"project",
"ui",
@@ -18000,11 +18045,7 @@ name = "ui_input"
version = "0.1.0"
dependencies = [
"component",
- "editor",
"gpui",
- "menu",
- "settings",
- "theme",
"ui",
]
@@ -125,6 +125,7 @@ members = [
"crates/ollama",
"crates/onboarding",
"crates/open_ai",
+ "crates/open_path_prompt",
"crates/open_router",
"crates/outline",
"crates/outline_panel",
@@ -187,6 +188,7 @@ members = [
"crates/theme_importer",
"crates/theme_selector",
"crates/time_format",
+ "crates/platform_title_bar",
"crates/title_bar",
"crates/toolchain_selector",
"crates/ui",
@@ -363,6 +365,7 @@ notifications = { path = "crates/notifications" }
ollama = { path = "crates/ollama" }
onboarding = { path = "crates/onboarding" }
open_ai = { path = "crates/open_ai" }
+open_path_prompt = { path = "crates/open_path_prompt" }
open_router = { path = "crates/open_router", features = ["schemars"] }
outline = { path = "crates/outline" }
outline_panel = { path = "crates/outline_panel" }
@@ -420,6 +423,7 @@ theme = { path = "crates/theme" }
theme_extension = { path = "crates/theme_extension" }
theme_selector = { path = "crates/theme_selector" }
time_format = { path = "crates/time_format" }
+platform_title_bar = { path = "crates/platform_title_bar" }
title_bar = { path = "crates/title_bar" }
toolchain_selector = { path = "crates/toolchain_selector" }
ui = { path = "crates/ui" }
@@ -24,7 +24,7 @@ anyhow.workspace = true
buffer_diff.workspace = true
chrono.workspace = true
collections.workspace = true
-editor.workspace = true
+multi_buffer.workspace = true
file_icons.workspace = true
futures.workspace = true
gpui.workspace = true
@@ -44,6 +44,7 @@ smol.workspace = true
task.workspace = true
telemetry.workspace = true
terminal.workspace = true
+text.workspace = true
ui.workspace = true
url.workspace = true
util.workspace = true
@@ -52,7 +53,6 @@ watch.workspace = true
urlencoding.workspace = true
[dev-dependencies]
-editor = { workspace = true, features = ["test-support"] }
env_logger.workspace = true
gpui = { workspace = true, "features" = ["test-support"] }
indoc.workspace = true
@@ -39,7 +39,6 @@ pub use terminal::*;
use action_log::{ActionLog, ActionLogTelemetry};
use agent_client_protocol::{self as acp};
use anyhow::{Context as _, Result, anyhow};
-use editor::Bias;
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
use gpui::{AppContext, AsyncApp, Context, Entity, EventEmitter, SharedString, Task, WeakEntity};
use itertools::Itertools;
@@ -54,6 +53,7 @@ use std::process::ExitStatus;
use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{fmt::Display, mem, path::PathBuf, sync::Arc};
+use text::Bias;
use ui::App;
use util::{ResultExt, get_default_system_shell_preferring_bash, paths::PathStyle};
use uuid::Uuid;
@@ -1,11 +1,11 @@
use anyhow::Result;
use buffer_diff::BufferDiff;
-use editor::{MultiBuffer, PathKey, multibuffer_context_lines};
use gpui::{App, AppContext, AsyncApp, Context, Entity, Subscription, Task};
use itertools::Itertools;
use language::{
Anchor, Buffer, Capability, LanguageRegistry, OffsetRangeExt as _, Point, TextBuffer,
};
+use multi_buffer::{MultiBuffer, PathKey, excerpt_context_lines};
use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc};
use util::ResultExt;
@@ -63,7 +63,7 @@ impl Diff {
PathKey::for_buffer(&buffer, cx),
buffer.clone(),
hunk_ranges,
- multibuffer_context_lines(cx),
+ excerpt_context_lines(cx),
cx,
);
multibuffer.add_diff(diff, cx);
@@ -300,7 +300,7 @@ impl PendingDiff {
path_key,
buffer,
ranges,
- multibuffer_context_lines(cx),
+ excerpt_context_lines(cx),
cx,
);
multibuffer.add_diff(buffer_diff.clone(), cx);
@@ -326,7 +326,7 @@ impl PendingDiff {
PathKey::for_buffer(&self.new_buffer, cx),
self.new_buffer.clone(),
ranges,
- multibuffer_context_lines(cx),
+ excerpt_context_lines(cx),
cx,
);
let end = multibuffer.len(cx);
@@ -32,10 +32,11 @@ fs.workspace = true
futures.workspace = true
gpui.workspace = true
gpui_tokio = { workspace = true, optional = true }
+credentials_provider.workspace = true
+google_ai.workspace = true
http_client.workspace = true
indoc.workspace = true
language_model.workspace = true
-language_models.workspace = true
log.workspace = true
project.workspace = true
release_channel.workspace = true
@@ -2,7 +2,7 @@ use crate::{AgentServer, AgentServerDelegate};
use acp_thread::{AcpThread, AgentThreadEntry, ToolCall, ToolCallStatus};
use agent_client_protocol as acp;
use futures::{FutureExt, StreamExt, channel::mpsc, select};
-use gpui::{AppContext, Entity, TestAppContext};
+use gpui::{Entity, TestAppContext};
use indoc::indoc;
#[cfg(test)]
use project::agent_server_store::BuiltinAgentServerSettings;
@@ -410,9 +410,7 @@ pub async fn init_test(cx: &mut TestAppContext) -> Arc<FakeFs> {
let http_client = reqwest_client::ReqwestClient::user_agent("agent tests").unwrap();
cx.set_http_client(Arc::new(http_client));
let client = client::Client::production(cx);
- let user_store = cx.new(|cx| client::UserStore::new(client.clone(), cx));
- language_model::init(client.clone(), cx);
- language_models::init(user_store, client, cx);
+ language_model::init(client, cx);
#[cfg(test)]
project::agent_server_store::AllAgentServersSettings::override_global(
@@ -4,11 +4,33 @@ use std::{any::Any, path::Path};
use crate::{AgentServer, AgentServerDelegate, load_proxy_env};
use acp_thread::AgentConnection;
use anyhow::{Context as _, Result};
+use credentials_provider::CredentialsProvider;
use gpui::{App, AppContext as _, SharedString, Task};
-use language_models::provider::google::GoogleLanguageModelProvider;
+use language_model::{ApiKey, EnvVar};
use project::agent_server_store::{AllAgentServersSettings, GEMINI_NAME};
use settings::SettingsStore;
+const GEMINI_API_KEY_VAR_NAME: &str = "GEMINI_API_KEY";
+const GOOGLE_AI_API_KEY_VAR_NAME: &str = "GOOGLE_AI_API_KEY";
+
+fn api_key_for_gemini_cli(cx: &mut App) -> Task<Result<String>> {
+ let env_var = EnvVar::new(GEMINI_API_KEY_VAR_NAME.into())
+ .or(EnvVar::new(GOOGLE_AI_API_KEY_VAR_NAME.into()));
+ if let Some(key) = env_var.value {
+ return Task::ready(Ok(key));
+ }
+ let credentials_provider = <dyn CredentialsProvider>::global(cx);
+ let api_url = google_ai::API_URL.to_string();
+ cx.spawn(async move |cx| {
+ Ok(
+ ApiKey::load_from_system_keychain(&api_url, credentials_provider.as_ref(), cx)
+ .await?
+ .key()
+ .to_string(),
+ )
+ })
+}
+
#[derive(Clone)]
pub struct Gemini;
@@ -46,11 +68,7 @@ impl AgentServer for Gemini {
cx.spawn(async move |cx| {
extra_env.insert("SURFACE".to_owned(), "zed".to_owned());
- if let Some(api_key) = cx
- .update(GoogleLanguageModelProvider::api_key_for_gemini_cli)
- .await
- .ok()
- {
+ if let Some(api_key) = cx.update(api_key_for_gemini_cli).await.ok() {
extra_env.insert("GEMINI_API_KEY".into(), api_key);
}
let (command, root_dir, login) = store
@@ -23,7 +23,6 @@ test-support = [
"reqwest_client",
"workspace/test-support",
"agent/test-support",
- "rules_library/test-support"
]
unit-eval = []
@@ -128,7 +127,6 @@ editor = { workspace = true, features = ["test-support"] }
eval_utils.workspace = true
gpui = { workspace = true, "features" = ["test-support"] }
git_ui = { workspace = true, features = ["test-support"] }
-rules_library = { workspace = true, features = ["test-support"] }
indoc.workspace = true
language = { workspace = true, "features" = ["test-support"] }
languages = { workspace = true, features = ["test-support"] }
@@ -9539,6 +9539,7 @@ pub(crate) mod tests {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
theme::init(theme::LoadThemes::JustBase, cx);
+ editor::init(cx);
release_channel::init(semver::Version::new(0, 0, 0), cx);
prompt_store::init(cx)
});
@@ -18,7 +18,7 @@ use workspace::{ModalView, Workspace};
fn single_line_input(
label: impl Into<SharedString>,
- placeholder: impl Into<SharedString>,
+ placeholder: &str,
text: Option<&str>,
tab_index: isize,
window: &mut Window,
@@ -31,9 +31,7 @@ fn single_line_input(
.tab_stop(true);
if let Some(text) = text {
- input
- .editor()
- .update(cx, |editor, cx| editor.set_text(text, window, cx));
+ input.set_text(text, window, cx);
}
input
})
@@ -721,9 +719,7 @@ mod tests {
cx.update(|window, cx| {
let model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
- input.editor().update(cx, |editor, cx| {
- editor.set_text("somemodel", window, cx);
- });
+ input.set_text("somemodel", window, cx);
});
assert_eq!(
model_input.capabilities.supports_tools,
@@ -762,9 +758,7 @@ mod tests {
cx.update(|window, cx| {
let mut model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
- input.editor().update(cx, |editor, cx| {
- editor.set_text("somemodel", window, cx);
- });
+ input.set_text("somemodel", window, cx);
});
model_input.capabilities.supports_tools = ToggleState::Unselected;
@@ -789,9 +783,7 @@ mod tests {
cx.update(|window, cx| {
let mut model_input = ModelInput::new(0, window, cx);
model_input.name.update(cx, |input, cx| {
- input.editor().update(cx, |editor, cx| {
- editor.set_text("somemodel", window, cx);
- });
+ input.set_text("somemodel", window, cx);
});
model_input.capabilities.supports_tools = ToggleState::Selected;
@@ -817,6 +809,7 @@ mod tests {
theme::init(theme::LoadThemes::JustBase, cx);
language_model::init_settings(cx);
+ editor::init(cx);
});
let fs = FakeFs::new(cx.executor());
@@ -837,9 +830,7 @@ mod tests {
) -> Option<SharedString> {
fn set_text(input: &Entity<InputField>, text: &str, window: &mut Window, cx: &mut App) {
input.update(cx, |input, cx| {
- input.editor().update(cx, |editor, cx| {
- editor.set_text(text, window, cx);
- });
+ input.set_text(text, window, cx);
});
}
@@ -8,7 +8,6 @@ use editor::display_map::{CreaseId, EditorMargins};
use editor::{AnchorRangeExt as _, MultiBufferOffset, ToOffset as _};
use editor::{
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer,
- actions::{MoveDown, MoveUp},
};
use fs::Fs;
use gpui::{
@@ -31,7 +30,10 @@ use ui::{IconButtonShape, KeyBinding, PopoverMenuHandle, Tooltip, prelude::*};
use uuid::Uuid;
use workspace::notifications::NotificationId;
use workspace::{Toast, Workspace};
-use zed_actions::agent::ToggleModelSelector;
+use zed_actions::{
+ agent::ToggleModelSelector,
+ editor::{MoveDown, MoveUp},
+};
use crate::agent_model_selector::AgentModelSelector;
use crate::buffer_codegen::{BufferCodegen, CodegenAlternative};
@@ -3542,6 +3542,7 @@ mod tests {
fn init_test(cx: &mut App) {
let settings_store = SettingsStore::test(cx);
prompt_store::init(cx);
+ editor::init(cx);
LanguageModelRegistry::test(cx);
cx.set_global(settings_store);
@@ -130,4 +130,5 @@ unindent.workspace = true
util.workspace = true
workspace = { workspace = true, features = ["test-support"] }
worktree = { workspace = true, features = ["test-support"] }
+zed_actions.workspace = true
zlog.workspace = true
@@ -933,8 +933,8 @@ async fn test_updated_breakpoints_send_to_dap(
.unwrap();
editor_b.update_in(remote_cx, |editor, window, cx| {
- editor.move_down(&editor::actions::MoveDown, window, cx);
- editor.move_down(&editor::actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -972,8 +972,8 @@ async fn test_updated_breakpoints_send_to_dap(
// remove the breakpoint that client B added
editor_a.update_in(host_cx, |editor, window, cx| {
- editor.move_down(&editor::actions::MoveDown, window, cx);
- editor.move_down(&editor::actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -1029,7 +1029,7 @@ async fn test_updated_breakpoints_send_to_dap(
// Add our own breakpoint now
editor_a.update_in(host_cx, |editor, window, cx| {
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
- editor.move_up(&editor::actions::MoveUp, window, cx);
+ editor.move_up(&zed_actions::editor::MoveUp, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -1751,9 +1751,9 @@ async fn test_ignore_breakpoints(
// .unwrap();
// local_editor.update_in(host_cx, |editor, window, cx| {
- // editor.move_down(&editor::actions::MoveDown, window, cx);
+ // editor.move_down(&zed_actions::editor::MoveDown, window, cx);
// editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx); // Line 2
- // editor.move_down(&editor::actions::MoveDown, window, cx);
+ // editor.move_down(&zed_actions::editor::MoveDown, window, cx);
// editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
// // Line 3
// });
@@ -4185,8 +4185,8 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
// Client B adds breakpoint on line(2)
editor_b.update_in(cx_b, |editor, window, cx| {
- editor.move_down(&editor::actions::MoveDown, window, cx);
- editor.move_down(&editor::actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -4214,8 +4214,8 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
// Client A removes last added breakpoint from client B
editor_a.update_in(cx_a, |editor, window, cx| {
- editor.move_down(&editor::actions::MoveDown, window, cx);
- editor.move_down(&editor::actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -4243,8 +4243,8 @@ async fn test_add_breakpoints(cx_a: &mut TestAppContext, cx_b: &mut TestAppConte
// Client B removes first added breakpoint by client A
editor_b.update_in(cx_b, |editor, window, cx| {
- editor.move_up(&editor::actions::MoveUp, window, cx);
- editor.move_up(&editor::actions::MoveUp, window, cx);
+ editor.move_up(&zed_actions::editor::MoveUp, window, cx);
+ editor.move_up(&zed_actions::editor::MoveUp, window, cx);
editor.toggle_breakpoint(&editor::actions::ToggleBreakpoint, window, cx);
});
@@ -28,7 +28,7 @@ impl ContactFinder {
pub fn set_query(&mut self, query: String, window: &mut Window, cx: &mut Context<Self>) {
self.picker.update(cx, |picker, cx| {
- picker.set_query(query, window, cx);
+ picker.set_query(&query, window, cx);
});
}
}
@@ -583,7 +583,7 @@ impl Render for ComponentPreview {
if input.is_empty(cx) {
String::new()
} else {
- input.editor().read(cx).text(cx)
+ input.text(cx)
}
});
@@ -848,7 +848,7 @@ impl ConfigureMode {
fn load(&mut self, cwd: PathBuf, window: &mut Window, cx: &mut App) {
self.cwd.update(cx, |input_field, cx| {
if input_field.is_empty(cx) {
- input_field.set_text(cwd.to_string_lossy(), window, cx);
+ input_field.set_text(&cwd.to_string_lossy(), window, cx);
}
});
}
@@ -1180,7 +1180,7 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
});
editor.update_in(cx, |editor, window, cx| {
- editor.move_down(&actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
});
@@ -1218,7 +1218,7 @@ async fn test_send_breakpoints_when_editor_has_been_saved(
});
editor.update_in(cx, |editor, window, cx| {
- editor.move_up(&actions::MoveUp, window, cx);
+ editor.move_up(&zed_actions::editor::MoveUp, window, cx);
editor.insert("new text\n", window, cx);
});
@@ -1312,18 +1312,18 @@ async fn test_unsetting_breakpoints_on_clear_breakpoint_action(
});
first_editor.update_in(cx, |editor, window, cx| {
- editor.move_down(&actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
- editor.move_down(&actions::MoveDown, window, cx);
- editor.move_down(&actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
});
second_editor.update_in(cx, |editor, window, cx| {
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
- editor.move_down(&actions::MoveDown, window, cx);
- editor.move_down(&actions::MoveDown, window, cx);
- editor.move_down(&actions::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
+ editor.move_down(&zed_actions::editor::MoveDown, window, cx);
editor.toggle_breakpoint(&actions::ToggleBreakpoint, window, cx);
});
@@ -90,6 +90,7 @@ unicode-segmentation.workspace = true
unicode-script.workspace = true
unindent = { workspace = true, optional = true }
ui.workspace = true
+ui_input.workspace = true
url.workspace = true
util.workspace = true
uuid.workspace = true
@@ -602,8 +602,6 @@ actions!(
LineDown,
/// Moves cursor up one line.
LineUp,
- /// Moves cursor down.
- MoveDown,
/// Moves cursor left.
MoveLeft,
/// Moves the current line down.
@@ -642,8 +640,6 @@ actions!(
MoveToStartOfLargerSyntaxNode,
/// Moves cursor to the end of the next larger syntax node.
MoveToEndOfLargerSyntaxNode,
- /// Moves cursor up.
- MoveUp,
/// Inserts a new line and moves cursor to it.
Newline,
/// Inserts a new line above the current line.
@@ -108,7 +108,7 @@ use gpui::{
Action, Animation, AnimationExt, AnyElement, App, AppContext, AsyncWindowContext,
AvailableSpace, Background, Bounds, ClickEvent, ClipboardEntry, ClipboardItem, Context,
DispatchPhase, Edges, Entity, EntityInputHandler, EventEmitter, FocusHandle, FocusOutEvent,
- Focusable, FontId, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
+ Focusable, FontId, FontStyle, FontWeight, Global, HighlightStyle, Hsla, KeyContext, Modifiers,
MouseButton, MouseDownEvent, MouseMoveEvent, PaintQuad, ParentElement, Pixels, PressureStage,
Render, ScrollHandle, SharedString, SharedUri, Size, Stateful, Styled, Subscription, Task,
TextRun, TextStyle, TextStyleRefinement, UTF16Selection, UnderlineStyle,
@@ -203,6 +203,7 @@ use ui::{
Avatar, ButtonSize, ButtonStyle, ContextMenu, Disclosure, IconButton, IconButtonShape,
IconName, IconSize, Indicator, Key, Tooltip, h_flex, prelude::*, scrollbars::ScrollbarAutoHide,
};
+use ui_input::ErasedEditor;
use util::{RangeExt, ResultExt, TryFutureExt, maybe, post_inc};
use workspace::{
CollaboratorId, Item as WorkspaceItem, ItemId, ItemNavHistory, NavigationEntry, OpenInTerminal,
@@ -212,6 +213,7 @@ use workspace::{
notifications::{DetachAndPromptErr, NotificationId, NotifyTaskExt},
searchable::{CollapseDirection, SearchEvent},
};
+use zed_actions::editor::{MoveDown, MoveUp};
use crate::{
code_context_menus::CompletionsMenuSource,
@@ -372,6 +374,12 @@ pub fn init(cx: &mut App) {
.detach();
}
});
+ _ = ui_input::ERASED_EDITOR_FACTORY.set(|window, cx| {
+ Arc::new(ErasedEditorImpl(
+ cx.new(|cx| Editor::single_line(window, cx)),
+ )) as Arc<dyn ErasedEditor>
+ });
+ _ = multi_buffer::EXCERPT_CONTEXT_LINES.set(multibuffer_context_lines);
}
pub fn set_blame_renderer(renderer: impl BlameRenderer + 'static, cx: &mut App) {
@@ -27717,6 +27725,90 @@ impl<T: InvalidationRegion> InvalidationStack<T> {
}
}
+#[derive(Clone)]
+struct ErasedEditorImpl(Entity<Editor>);
+
+impl ui_input::ErasedEditor for ErasedEditorImpl {
+ fn text(&self, cx: &App) -> String {
+ self.0.read(cx).text(cx)
+ }
+
+ fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
+ self.0.update(cx, |this, cx| {
+ this.set_text(text, window, cx);
+ })
+ }
+
+ fn clear(&self, window: &mut Window, cx: &mut App) {
+ self.0.update(cx, |this, cx| this.clear(window, cx));
+ }
+
+ fn set_placeholder_text(&self, text: &str, window: &mut Window, cx: &mut App) {
+ self.0.update(cx, |this, cx| {
+ this.set_placeholder_text(text, window, cx);
+ });
+ }
+
+ fn focus_handle(&self, cx: &App) -> FocusHandle {
+ self.0.read(cx).focus_handle(cx)
+ }
+
+ fn render(&self, _: &mut Window, cx: &App) -> AnyElement {
+ let settings = ThemeSettings::get_global(cx);
+ let theme_color = cx.theme().colors();
+
+ let text_style = TextStyle {
+ font_family: settings.ui_font.family.clone(),
+ font_features: settings.ui_font.features.clone(),
+ font_size: rems(0.875).into(),
+ font_weight: settings.buffer_font.weight,
+ font_style: FontStyle::Normal,
+ line_height: relative(1.2),
+ color: theme_color.text,
+ ..Default::default()
+ };
+ let editor_style = EditorStyle {
+ background: theme_color.ghost_element_background,
+ local_player: cx.theme().players().local(),
+ syntax: cx.theme().syntax().clone(),
+ text: text_style,
+ ..Default::default()
+ };
+ EditorElement::new(&self.0, editor_style).into_any()
+ }
+
+ fn as_any(&self) -> &dyn Any {
+ &self.0
+ }
+
+ fn move_selection_to_end(&self, window: &mut Window, cx: &mut App) {
+ self.0.update(cx, |editor, cx| {
+ let editor_offset = editor.buffer().read(cx).len(cx);
+ editor.change_selections(
+ SelectionEffects::scroll(Autoscroll::Next),
+ window,
+ cx,
+ |s| s.select_ranges(Some(editor_offset..editor_offset)),
+ );
+ });
+ }
+
+ fn subscribe(
+ &self,
+ mut callback: Box<dyn FnMut(ui_input::ErasedEditorEvent, &mut Window, &mut App) + 'static>,
+ window: &mut Window,
+ cx: &mut App,
+ ) -> Subscription {
+ window.subscribe(&self.0, cx, move |_, event: &EditorEvent, window, cx| {
+ let event = match event {
+ EditorEvent::BufferEdited => ui_input::ErasedEditorEvent::BufferEdited,
+ EditorEvent::Blurred => ui_input::ErasedEditorEvent::Blurred,
+ _ => return,
+ };
+ (callback)(event, window, cx);
+ })
+ }
+}
impl<T> Default for InvalidationStack<T> {
fn default() -> Self {
Self(Default::default())
@@ -21,10 +21,9 @@ futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
menu.workspace = true
+open_path_prompt.workspace = true
picker.workspace = true
project.workspace = true
-schemars.workspace = true
-search.workspace = true
settings.workspace = true
serde.workspace = true
text.workspace = true
@@ -32,6 +31,7 @@ theme.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
+zed_actions.workspace = true
[dev-dependencies]
ctor.workspace = true
@@ -1,17 +1,11 @@
#[cfg(test)]
mod file_finder_tests;
-#[cfg(test)]
-mod open_path_prompt_tests;
-
-pub mod file_finder_settings;
-mod open_path_prompt;
use futures::future::join_all;
pub use open_path_prompt::OpenPathDelegate;
use collections::HashMap;
use editor::Editor;
-use file_finder_settings::{FileFinderSettings, FileFinderWidth};
use file_icons::FileIcons;
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{
@@ -19,12 +13,14 @@ use gpui::{
KeyContext, Modifiers, ModifiersChangedEvent, ParentElement, Render, Styled, Task, WeakEntity,
Window, actions, rems,
};
-use open_path_prompt::OpenPathPrompt;
+use open_path_prompt::{
+ OpenPathPrompt,
+ file_finder_settings::{FileFinderSettings, FileFinderWidth},
+};
use picker::{Picker, PickerDelegate};
use project::{
PathMatchCandidateSet, Project, ProjectPath, WorktreeId, worktree_store::WorktreeStore,
};
-use search::ToggleIncludeIgnored;
use settings::Settings;
use std::{
borrow::Cow,
@@ -51,6 +47,7 @@ use workspace::{
ModalView, OpenOptions, OpenVisible, SplitDirection, Workspace, item::PreviewTabsSettings,
notifications::NotifyResultExt, pane,
};
+use zed_actions::search::ToggleIncludeIgnored;
actions!(
file_finder,
@@ -57,6 +57,7 @@ theme.workspace = true
time.workspace = true
time_format.workspace = true
ui.workspace = true
+ui_input.workspace = true
util.workspace = true
watch.workspace = true
workspace.workspace = true
@@ -20,6 +20,7 @@ use ui::{
Divider, HighlightedLabel, KeyBinding, ListHeader, ListItem, ListItemSpacing, Tooltip,
prelude::*,
};
+use ui_input::ErasedEditor;
use util::ResultExt;
use workspace::notifications::DetachAndPromptErr;
use workspace::{ModalView, Workspace};
@@ -611,11 +612,12 @@ impl PickerDelegate for BranchListDelegate {
fn render_editor(
&self,
- editor: &Entity<Editor>,
+ editor: &Arc<dyn ErasedEditor>,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> Div {
let focus_handle = self.focus_handle.clone();
+ let editor = editor.as_any().downcast_ref::<Entity<Editor>>().unwrap();
v_flex()
.when(
@@ -1325,6 +1327,7 @@ mod tests {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
theme::init(theme::LoadThemes::JustBase, cx);
+ editor::init(cx);
});
}
@@ -4271,10 +4271,10 @@ impl GitPanel {
.child(
div()
.pr_2p5()
- .on_action(|&editor::actions::MoveUp, _, cx| {
+ .on_action(|&zed_actions::editor::MoveUp, _, cx| {
cx.stop_propagation();
})
- .on_action(|&editor::actions::MoveDown, _, cx| {
+ .on_action(|&zed_actions::editor::MoveDown, _, cx| {
cx.stop_propagation();
})
.child(EditorElement::new(&self.commit_editor, panel_editor_style)),
@@ -18,11 +18,11 @@ editor.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
+platform_title_bar.workspace = true
project.workspace = true
serde_json.workspace = true
serde_json_lenient.workspace = true
theme.workspace = true
-title_bar.workspace = true
ui.workspace = true
util.workspace = true
util_macros.workspace = true
@@ -1,7 +1,7 @@
use anyhow::{Context as _, anyhow};
use gpui::{App, DivInspectorState, Inspector, InspectorElementId, IntoElement, Window};
+use platform_title_bar::PlatformTitleBar;
use std::{cell::OnceCell, path::Path, sync::Arc};
-use title_bar::platform_title_bar::PlatformTitleBar;
use ui::{Label, Tooltip, prelude::*};
use util::{ResultExt as _, command::new_smol_command};
use workspace::AppState;
@@ -2301,12 +2301,15 @@ impl KeybindingEditorModal {
.context()
.and_then(KeybindContextString::local)
{
- input.editor().update(cx, |editor, cx| {
- editor.set_text(context.clone(), window, cx);
- });
+ input.set_text(&context, window, cx);
}
- let editor_entity = input.editor().clone();
+ let editor_entity = input.editor();
+ let editor_entity = editor_entity
+ .as_any()
+ .downcast_ref::<Entity<Editor>>()
+ .unwrap()
+ .clone();
let workspace = workspace.clone();
cx.spawn(async move |_input_handle, cx| {
let contexts = cx
@@ -2350,7 +2353,12 @@ impl KeybindingEditorModal {
.label("Action")
.label_size(LabelSize::Default);
- input.editor().update(cx, |editor, _cx| {
+ let editor_entity = input.editor();
+ let editor_entity = editor_entity
+ .as_any()
+ .downcast_ref::<Entity<Editor>>()
+ .unwrap();
+ editor_entity.update(cx, |editor, _cx| {
editor.set_completion_provider(Some(std::rc::Rc::new(
ActionCompletionProvider::new(actions, humanized_names),
)));
@@ -2417,7 +2425,7 @@ impl KeybindingEditorModal {
return;
};
- let action_name_str = action_editor.read(cx).editor.read(cx).text(cx);
+ let action_name_str = action_editor.read(cx).text(cx);
let current_action = self.action_name_to_static.get(&action_name_str).copied();
if current_action == self.selected_action_name {
@@ -2498,7 +2506,7 @@ impl KeybindingEditorModal {
fn get_selected_action_name(&self, cx: &App) -> anyhow::Result<&'static str> {
if let Some(selector) = self.action_editor.as_ref() {
- let action_name_str = selector.read(cx).editor.read(cx).text(cx);
+ let action_name_str = selector.read(cx).text(cx);
if action_name_str.is_empty() {
anyhow::bail!("Action name is required");
@@ -2544,7 +2552,7 @@ impl KeybindingEditorModal {
fn validate_context(&self, cx: &App) -> anyhow::Result<Option<String>> {
let new_context = self
.context_editor
- .read_with(cx, |input, cx| input.editor().read(cx).text(cx));
+ .read_with(cx, |input, cx| input.text(cx));
let Some(context) = new_context.is_empty().not().then_some(new_context) else {
return Ok(None);
};
@@ -2717,10 +2725,18 @@ impl KeybindingEditorModal {
self.action_editor.as_ref().is_some_and(|action_editor| {
let focus_handle = action_editor.read(cx).focus_handle(cx);
let editor_entity = action_editor.read(cx).editor();
+ let editor_entity = editor_entity
+ .as_any()
+ .downcast_ref::<Entity<Editor>>()
+ .unwrap();
is_editor_showing_completions(&focus_handle, editor_entity)
}) || {
let focus_handle = self.context_editor.read(cx).focus_handle(cx);
let editor_entity = self.context_editor.read(cx).editor();
+ let editor_entity = editor_entity
+ .as_any()
+ .downcast_ref::<Entity<Editor>>()
+ .unwrap();
is_editor_showing_completions(&focus_handle, editor_entity)
} || self
.action_arguments_editor
@@ -1,6 +1,5 @@
use anyhow::{Context as _, Result};
use collections::BTreeMap;
-use credentials_provider::CredentialsProvider;
use futures::{FutureExt, Stream, StreamExt, future::BoxFuture};
use google_ai::{
FunctionDeclaration, GenerateContentResponse, GoogleModelMode, Part, SystemInstruction,
@@ -32,7 +31,7 @@ use ui::{ButtonLink, ConfiguredApiCard, List, ListBulletItem, prelude::*};
use ui_input::InputField;
use util::ResultExt;
-use language_model::{ApiKey, ApiKeyState};
+use language_model::ApiKeyState;
const PROVIDER_ID: LanguageModelProviderId = language_model::GOOGLE_PROVIDER_ID;
const PROVIDER_NAME: LanguageModelProviderName = language_model::GOOGLE_PROVIDER_NAME;
@@ -117,22 +116,6 @@ impl GoogleLanguageModelProvider {
})
}
- pub fn api_key_for_gemini_cli(cx: &mut App) -> Task<Result<String>> {
- if let Some(key) = API_KEY_ENV_VAR.value.clone() {
- return Task::ready(Ok(key));
- }
- let credentials_provider = <dyn CredentialsProvider>::global(cx);
- let api_url = Self::api_url(cx).to_string();
- cx.spawn(async move |cx| {
- Ok(
- ApiKey::load_from_system_keychain(&api_url, credentials_provider.as_ref(), cx)
- .await?
- .key()
- .to_string(),
- )
- })
- }
-
fn settings(cx: &App) -> &GoogleSettings {
&crate::AllLanguageModelSettings::get_global(cx).google
}
@@ -613,7 +613,7 @@ impl ConfigurationView {
let api_url_editor = cx.new(|cx| {
let input = InputField::new(window, cx, OLLAMA_API_URL).label("API URL");
- input.set_text(OllamaLanguageModelProvider::api_url(cx), window, cx);
+ input.set_text(&OllamaLanguageModelProvider::api_url(cx), window, cx);
input
});
@@ -15,11 +15,11 @@ doctest = false
[dependencies]
anyhow.workspace = true
editor.workspace = true
-file_finder.workspace = true
file_icons.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
+open_path_prompt.workspace = true
picker.workspace = true
project.workspace = true
settings.workspace = true
@@ -3,14 +3,13 @@ mod active_buffer_language;
pub use active_buffer_language::ActiveBufferLanguage;
use anyhow::Context as _;
use editor::Editor;
-use file_finder::file_finder_settings::FileFinderSettings;
-use file_icons::FileIcons;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{
App, Context, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, ParentElement,
Render, Styled, WeakEntity, Window, actions,
};
use language::{Buffer, LanguageMatcher, LanguageName, LanguageRegistry};
+use open_path_prompt::file_finder_settings::FileFinderSettings;
use picker::{Picker, PickerDelegate};
use project::Project;
use settings::Settings;
@@ -176,7 +175,7 @@ impl LanguageSelectorDelegate {
matcher
.path_suffixes
.iter()
- .find_map(|extension| FileIcons::get_icon(Path::new(extension), cx))
+ .find_map(|extension| file_icons::FileIcons::get_icon(Path::new(extension), cx))
.map(Icon::from_path)
.map(|icon| icon.color(Color::Muted))
}
@@ -44,7 +44,7 @@ use std::{
ops::{self, AddAssign, ControlFlow, Range, RangeBounds, Sub, SubAssign},
rc::Rc,
str,
- sync::Arc,
+ sync::{Arc, OnceLock},
time::Duration,
};
use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap};
@@ -59,6 +59,12 @@ use ztracing::instrument;
pub use self::path_key::PathKey;
+pub static EXCERPT_CONTEXT_LINES: OnceLock<fn(&App) -> u32> = OnceLock::new();
+
+pub fn excerpt_context_lines(cx: &App) -> u32 {
+ EXCERPT_CONTEXT_LINES.get().map(|f| f(cx)).unwrap_or(2)
+}
+
#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ExcerptId(u32);
@@ -0,0 +1,33 @@
+[package]
+name = "open_path_prompt"
+version = "0.1.0"
+publish.workspace = true
+edition.workspace = true
+license = "GPL-3.0-or-later"
+
+[dependencies]
+file_icons.workspace = true
+futures.workspace = true
+fuzzy.workspace = true
+gpui.workspace = true
+picker.workspace = true
+project.workspace = true
+schemars.workspace = true
+serde.workspace = true
+settings.workspace = true
+ui.workspace = true
+util.workspace = true
+workspace.workspace = true
+
+[dev-dependencies]
+editor = {workspace = true, features = ["test-support"]}
+gpui = {workspace = true, features = ["test-support"]}
+serde_json.workspace = true
+theme = {workspace = true, features = ["test-support"]}
+workspace = {workspace = true, features = ["test-support"]}
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/open_path_prompt.rs"
@@ -0,0 +1 @@
+../../LICENSE-GPL
@@ -1,4 +1,9 @@
-use crate::file_finder_settings::FileFinderSettings;
+pub mod file_finder_settings;
+
+#[cfg(test)]
+mod open_path_prompt_tests;
+
+use file_finder_settings::FileFinderSettings;
use file_icons::FileIcons;
use futures::channel::oneshot;
use fuzzy::{CharBag, StringMatch, StringMatchCandidate};
@@ -21,7 +26,7 @@ use util::{
};
use workspace::Workspace;
-pub(crate) struct OpenPathPrompt;
+pub struct OpenPathPrompt;
pub struct OpenPathDelegate {
tx: Option<oneshot::Sender<Option<Vec<PathBuf>>>>,
@@ -184,7 +189,7 @@ struct CandidateInfo {
}
impl OpenPathPrompt {
- pub(crate) fn register(
+ pub fn register(
workspace: &mut Workspace,
_window: Option<&mut Window>,
_: &mut Context<Workspace>,
@@ -196,7 +201,7 @@ impl OpenPathPrompt {
}));
}
- pub(crate) fn register_new_path(
+ pub fn register_new_path(
workspace: &mut Workspace,
_window: Option<&mut Window>,
_: &mut Context<Workspace>,
@@ -220,7 +225,7 @@ impl OpenPathPrompt {
let delegate = OpenPathDelegate::new(tx, lister.clone(), creating_path, cx);
let picker = Picker::uniform_list(delegate, window, cx).width(rems(34.));
let query = lister.default_query(cx);
- picker.set_query(query, window, cx);
+ picker.set_query(&query, window, cx);
picker
});
}
@@ -930,7 +935,7 @@ fn get_dir_and_suffix(query: String, path_style: PathStyle) -> (String, String)
mod tests {
use util::paths::PathStyle;
- use crate::open_path_prompt::get_dir_and_suffix;
+ use super::get_dir_and_suffix;
#[test]
fn test_get_dir_and_suffix_with_windows_style() {
@@ -347,7 +347,7 @@ fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
cx.update(|cx| {
let state = AppState::test(cx);
theme::init(theme::LoadThemes::JustBase, cx);
- super::init(cx);
+
editor::init(cx);
state
})
@@ -370,7 +370,7 @@ fn build_open_path_prompt(
.width(rems(34.))
.modal(false);
let query = lister.default_query(cx);
- picker.set_query(query, window, cx);
+ picker.set_query(&query, window, cx);
picker
})
}),
@@ -17,14 +17,15 @@ test-support = []
[dependencies]
anyhow.workspace = true
-editor.workspace = true
gpui.workspace = true
menu.workspace = true
schemars.workspace = true
serde.workspace = true
theme.workspace = true
ui.workspace = true
+ui_input.workspace = true
workspace.workspace = true
+zed_actions.workspace = true
[dev-dependencies]
ctor.workspace = true
@@ -1,13 +1,13 @@
use std::sync::Arc;
-use editor::{Editor, EditorEvent};
use gpui::{App, Entity, FocusHandle, Focusable, prelude::*};
use ui::prelude::*;
+use ui_input::{ErasedEditor, ErasedEditorEvent};
/// The head of a [`Picker`](crate::Picker).
pub(crate) enum Head {
/// Picker has an editor that allows the user to filter the list.
- Editor(Entity<Editor>),
+ Editor(Arc<dyn ErasedEditor>),
/// Picker has no head, it's just a list of items.
Empty(Entity<EmptyHead>),
@@ -16,17 +16,28 @@ pub(crate) enum Head {
impl Head {
pub fn editor<V: 'static>(
placeholder_text: Arc<str>,
- edit_handler: impl FnMut(&mut V, &Entity<Editor>, &EditorEvent, &mut Window, &mut Context<V>)
- + 'static,
+ mut edit_handler: impl FnMut(&mut V, &ErasedEditorEvent, &mut Window, &mut Context<V>) + 'static,
window: &mut Window,
cx: &mut Context<V>,
) -> Self {
- let editor = cx.new(|cx| {
- let mut editor = Editor::single_line(window, cx);
- editor.set_placeholder_text(placeholder_text.as_ref(), window, cx);
- editor
- });
- cx.subscribe_in(&editor, window, edit_handler).detach();
+ let editor = (ui_input::ERASED_EDITOR_FACTORY.get().unwrap())(window, cx);
+
+ editor.set_placeholder_text(placeholder_text.as_ref(), window, cx);
+ let this = cx.weak_entity();
+ editor
+ .subscribe(
+ Box::new(move |event, window, cx| {
+ this.update(cx, |this, cx| (edit_handler)(this, &event, window, cx))
+ .ok();
+ }),
+ window,
+ cx,
+ )
+ .detach();
+ // cx.subscribe_in(&editor, window, |v, _, event, window, cx| {
+ // edit_handler(v, event, window, cx);
+ // })
+ // .detach();
Self::Editor(editor)
}
@@ -3,16 +3,12 @@ pub mod highlighted_match_with_paths;
pub mod popover_menu;
use anyhow::Result;
-use editor::{
- Editor, SelectionEffects,
- actions::{MoveDown, MoveUp},
- scroll::Autoscroll,
-};
+
use gpui::{
- Action, AnyElement, App, Bounds, ClickEvent, Context, DismissEvent, Entity, EventEmitter,
- FocusHandle, Focusable, Length, ListSizingBehavior, ListState, MouseButton, MouseUpEvent,
- Pixels, Render, ScrollStrategy, Task, UniformListScrollHandle, Window, actions, canvas, div,
- list, prelude::*, uniform_list,
+ Action, AnyElement, App, Bounds, ClickEvent, Context, DismissEvent, EventEmitter, FocusHandle,
+ Focusable, Length, ListSizingBehavior, ListState, MouseButton, MouseUpEvent, Pixels, Render,
+ ScrollStrategy, Task, UniformListScrollHandle, Window, actions, canvas, div, list, prelude::*,
+ uniform_list,
};
use head::Head;
use schemars::JsonSchema;
@@ -25,7 +21,9 @@ use ui::{
Color, Divider, DocumentationAside, DocumentationSide, Label, ListItem, ListItemSpacing,
ScrollAxes, Scrollbars, WithScrollbar, prelude::*, utils::WithRemSize, v_flex,
};
+use ui_input::{ErasedEditor, ErasedEditorEvent};
use workspace::{ModalView, item::Settings};
+use zed_actions::editor::{MoveDown, MoveUp};
enum ElementContainer {
List(ListState),
@@ -195,9 +193,9 @@ pub trait PickerDelegate: Sized + 'static {
fn render_editor(
&self,
- editor: &Entity<Editor>,
- _window: &mut Window,
- _cx: &mut Context<Picker<Self>>,
+ editor: &Arc<dyn ErasedEditor>,
+ window: &mut Window,
+ cx: &mut Context<Picker<Self>>,
) -> Div {
v_flex()
.when(
@@ -210,7 +208,7 @@ pub trait PickerDelegate: Sized + 'static {
.flex_none()
.h_9()
.px_2p5()
- .child(editor.clone()),
+ .child(editor.render(window, cx)),
)
.when(
self.editor_position() == PickerEditorPosition::Start,
@@ -480,7 +478,7 @@ impl<D: PickerDelegate> Picker<D> {
.delegate
.select_history(Direction::Down, &query, window, cx)
{
- self.set_query(query, window, cx);
+ self.set_query(&query, window, cx);
return;
}
let count = self.delegate.match_count();
@@ -507,7 +505,7 @@ impl<D: PickerDelegate> Picker<D> {
.delegate
.select_history(Direction::Up, &query, window, cx)
{
- self.set_query(query, window, cx);
+ self.set_query(&query, window, cx);
return;
}
let count = self.delegate.match_count();
@@ -606,7 +604,7 @@ impl<D: PickerDelegate> Picker<D> {
cx: &mut Context<Self>,
) {
if let Some(new_query) = self.delegate.confirm_completion(self.query(cx), window, cx) {
- self.set_query(new_query, window, cx);
+ self.set_query(&new_query, window, cx);
} else {
cx.propagate()
}
@@ -627,7 +625,7 @@ impl<D: PickerDelegate> Picker<D> {
fn do_confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Self>) {
if let Some(update_query) = self.delegate.confirm_update_query(window, cx) {
- self.set_query(update_query, window, cx);
+ self.set_query(&update_query, window, cx);
self.set_selected_index(0, Some(Direction::Down), false, window, cx);
} else {
self.delegate.confirm(secondary, window, cx)
@@ -636,8 +634,7 @@ impl<D: PickerDelegate> Picker<D> {
fn on_input_editor_event(
&mut self,
- _: &Entity<Editor>,
- event: &editor::EditorEvent,
+ event: &ErasedEditorEvent,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -645,16 +642,15 @@ impl<D: PickerDelegate> Picker<D> {
panic!("unexpected call");
};
match event {
- editor::EditorEvent::BufferEdited => {
- let query = editor.read(cx).text(cx);
+ ErasedEditorEvent::BufferEdited => {
+ let query = editor.text(cx);
self.update_matches(query, window, cx);
}
- editor::EditorEvent::Blurred => {
+ ErasedEditorEvent::Blurred => {
if self.is_modal && window.is_window_active() {
self.cancel(&menu::Cancel, window, cx);
}
}
- _ => {}
}
}
@@ -667,14 +663,13 @@ impl<D: PickerDelegate> Picker<D> {
}
}
- pub fn refresh_placeholder(&mut self, window: &mut Window, cx: &mut App) {
+ pub fn refresh_placeholder(&mut self, window: &mut Window, cx: &mut Context<Self>) {
match &self.head {
Head::Editor(editor) => {
let placeholder = self.delegate.placeholder_text(window, cx);
- editor.update(cx, |editor, cx| {
- editor.set_placeholder_text(placeholder.as_ref(), window, cx);
- cx.notify();
- });
+
+ editor.set_placeholder_text(placeholder.as_ref(), window, cx);
+ cx.notify();
}
Head::Empty(_) => {}
}
@@ -730,23 +725,15 @@ impl<D: PickerDelegate> Picker<D> {
pub fn query(&self, cx: &App) -> String {
match &self.head {
- Head::Editor(editor) => editor.read(cx).text(cx),
+ Head::Editor(editor) => editor.text(cx),
Head::Empty(_) => "".to_string(),
}
}
- pub fn set_query(&self, query: impl Into<Arc<str>>, window: &mut Window, cx: &mut App) {
+ pub fn set_query(&self, query: &str, window: &mut Window, cx: &mut App) {
if let Head::Editor(editor) = &self.head {
- editor.update(cx, |editor, cx| {
- editor.set_text(query, window, cx);
- let editor_offset = editor.buffer().read(cx).len(cx);
- editor.change_selections(
- SelectionEffects::scroll(Autoscroll::Next),
- window,
- cx,
- |s| s.select_ranges(Some(editor_offset..editor_offset)),
- );
- });
+ editor.set_text(query, window, cx);
+ editor.move_selection_to_end(window, cx);
}
}
@@ -0,0 +1,25 @@
+[package]
+name = "platform_title_bar"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "GPL-3.0-or-later"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/platform_title_bar.rs"
+doctest = false
+
+[dependencies]
+gpui.workspace = true
+settings.workspace = true
+smallvec.workspace = true
+theme.workspace = true
+ui.workspace = true
+workspace.workspace = true
+zed_actions.workspace = true
+
+[target.'cfg(windows)'.dependencies]
+windows.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-GPL
@@ -1,3 +1,6 @@
+mod platforms;
+mod system_window_tabs;
+
use gpui::{
AnyElement, Context, Decorations, Entity, Hsla, InteractiveElement, IntoElement, MouseButton,
ParentElement, Pixels, StatefulInteractiveElement, Styled, Window, WindowControlArea, div, px,
@@ -11,6 +14,10 @@ use crate::{
system_window_tabs::SystemWindowTabs,
};
+pub use system_window_tabs::{
+ DraggedWindowTab, MergeAllWindows, MoveTabToNewWindow, ShowNextWindowTab, ShowPreviousWindowTab,
+};
+
pub struct PlatformTitleBar {
id: ElementId,
platform_style: PlatformStyle,
@@ -62,6 +69,10 @@ impl PlatformTitleBar {
{
self.children = children.into_iter().collect();
}
+
+ pub fn init(cx: &mut App) {
+ SystemWindowTabs::init(cx);
+ }
}
impl Render for PlatformTitleBar {
@@ -24,7 +24,6 @@ db.workspace = true
dev_container.workspace = true
editor.workspace = true
extension_host.workspace = true
-file_finder.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
@@ -33,6 +32,7 @@ log.workspace = true
markdown.workspace = true
menu.workspace = true
node_runtime.workspace = true
+open_path_prompt.workspace = true
ordered-float.workspace = true
paths.workspace = true
picker.workspace = true
@@ -7,7 +7,7 @@ use crate::{
};
use dev_container::start_dev_container;
use editor::Editor;
-use file_finder::OpenPathDelegate;
+
use futures::{FutureExt, channel::oneshot, future::Shared};
use gpui::{
AnyElement, App, ClickEvent, ClipboardItem, Context, DismissEvent, Entity, EventEmitter,
@@ -16,6 +16,7 @@ use gpui::{
};
use language::Point;
use log::{debug, info};
+use open_path_prompt::OpenPathDelegate;
use paths::{global_ssh_config_file, user_ssh_config_file};
use picker::Picker;
use project::{Fs, Project};
@@ -222,13 +223,13 @@ impl ProjectPicker {
) -> Entity<Self> {
let (tx, rx) = oneshot::channel();
let lister = project::DirectoryLister::Project(project.clone());
- let delegate = file_finder::OpenPathDelegate::new(tx, lister, false, cx);
+ let delegate = open_path_prompt::OpenPathDelegate::new(tx, lister, false, cx);
let picker = cx.new(|cx| {
let picker = Picker::uniform_list(delegate, window, cx)
.width(rems(34.))
.modal(false);
- picker.set_query(home_dir.to_string(), window, cx);
+ picker.set_query(&home_dir.to_string(), window, cx);
picker
});
@@ -11,8 +11,6 @@ workspace = true
[lib]
path = "src/rules_library.rs"
-[features]
-test-support = ["title_bar/test-support"]
[dependencies]
anyhow.workspace = true
@@ -24,14 +22,15 @@ language_model.workspace = true
log.workspace = true
menu.workspace = true
picker.workspace = true
+platform_title_bar.workspace = true
prompt_store.workspace = true
release_channel.workspace = true
rope.workspace = true
serde.workspace = true
settings.workspace = true
theme.workspace = true
-title_bar.workspace = true
ui.workspace = true
+ui_input.workspace = true
util.workspace = true
workspace.workspace = true
zed_actions.workspace = true
@@ -12,6 +12,7 @@ use language_model::{
ConfiguredModel, LanguageModelRegistry, LanguageModelRequest, LanguageModelRequestMessage, Role,
};
use picker::{Picker, PickerDelegate};
+use platform_title_bar::PlatformTitleBar;
use release_channel::ReleaseChannel;
use rope::Rope;
use settings::Settings;
@@ -20,8 +21,8 @@ use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::time::Duration;
use theme::ThemeSettings;
-use title_bar::platform_title_bar::PlatformTitleBar;
use ui::{Divider, ListItem, ListItemSpacing, ListSubHeader, Tooltip, prelude::*};
+use ui_input::ErasedEditor;
use util::{ResultExt, TryFutureExt};
use workspace::{Workspace, WorkspaceSettings, client_side_decorations};
use zed_actions::assistant::InlineAssist;
@@ -445,10 +446,12 @@ impl PickerDelegate for RulePickerDelegate {
fn render_editor(
&self,
- editor: &Entity<Editor>,
+ editor: &Arc<dyn ErasedEditor>,
_: &mut Window,
cx: &mut Context<Picker<Self>>,
) -> Div {
+ let editor = editor.as_any().downcast_ref::<Entity<Editor>>().unwrap();
+
h_flex()
.py_1()
.px_1p5()
@@ -983,7 +986,7 @@ impl RulesLibrary {
fn move_down_from_title(
&mut self,
- _: &editor::actions::MoveDown,
+ _: &zed_actions::editor::MoveDown,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -996,7 +999,7 @@ impl RulesLibrary {
fn move_up_from_body(
&mut self,
- _: &editor::actions::MoveUp,
+ _: &zed_actions::editor::MoveUp,
window: &mut Window,
cx: &mut Context<Self>,
) {
@@ -58,4 +58,3 @@ workspace = { workspace = true, features = ["test-support"] }
[package.metadata.cargo-machete]
ignored = ["tracing"]
-
@@ -8,6 +8,7 @@ use ui::{ButtonStyle, IconButton, IconButtonShape};
use ui::{Tooltip, prelude::*};
use workspace::notifications::NotificationId;
use workspace::{Toast, Workspace};
+pub use zed_actions::search::ToggleIncludeIgnored;
pub use search_status_button::SEARCH_ICON;
@@ -33,8 +34,6 @@ actions!(
ToggleWholeWord,
/// Toggles case-sensitive search.
ToggleCaseSensitive,
- /// Toggles searching in ignored files.
- ToggleIncludeIgnored,
/// Toggles regular expression mode.
ToggleRegex,
/// Toggles the replace interface.
@@ -18,6 +18,7 @@ test-support = []
[dependencies]
anyhow.workspace = true
bm25 = "2.3.2"
+component.workspace = true
copilot_ui.workspace = true
edit_prediction.workspace = true
editor.workspace = true
@@ -34,6 +35,7 @@ log.workspace = true
menu.workspace = true
paths.workspace = true
picker.workspace = true
+platform_title_bar.workspace = true
project.workspace = true
release_channel.workspace = true
schemars.workspace = true
@@ -43,8 +45,6 @@ settings.workspace = true
strum.workspace = true
telemetry.workspace = true
theme.workspace = true
-title_bar.workspace = true
-ui_input.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true
@@ -2,6 +2,7 @@ mod dropdown;
mod font_picker;
mod icon_theme_picker;
mod input_field;
+mod number_field;
mod section_items;
mod theme_picker;
@@ -9,5 +10,6 @@ pub use dropdown::*;
pub use font_picker::font_picker;
pub use icon_theme_picker::icon_theme_picker;
pub use input_field::*;
+pub use number_field::*;
pub use section_items::*;
pub use theme_picker::theme_picker;
@@ -5,7 +5,7 @@ use std::{
str::FromStr,
};
-use editor::{Editor, actions::MoveDown, actions::MoveUp};
+use editor::Editor;
use gpui::{
ClickEvent, Entity, FocusHandle, Focusable, FontWeight, Modifiers, TextAlign,
TextStyleRefinement, WeakEntity,
@@ -16,6 +16,7 @@ use settings::{
MinimumContrast,
};
use ui::prelude::*;
+use zed_actions::editor::{MoveDown, MoveUp};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NumberFieldMode {
@@ -316,26 +317,6 @@ impl<T: NumberFieldType> NumberField<T> {
}
}
- pub fn format(mut self, format: impl FnOnce(&T) -> String + 'static) -> Self {
- self.format = Box::new(format);
- self
- }
-
- pub fn small_step(mut self, step: T) -> Self {
- self.small_step = step;
- self
- }
-
- pub fn normal_step(mut self, step: T) -> Self {
- self.step = step;
- self
- }
-
- pub fn large_step(mut self, step: T) -> Self {
- self.large_step = step;
- self
- }
-
pub fn min(mut self, min: T) -> Self {
self.min_value = min;
self
@@ -351,14 +332,6 @@ impl<T: NumberFieldType> NumberField<T> {
self
}
- pub fn on_reset(
- mut self,
- on_reset: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- ) -> Self {
- self.on_reset = Some(Box::new(on_reset));
- self
- }
-
pub fn tab_index(mut self, tab_index: isize) -> Self {
self.tab_index = Some(tab_index);
self
@@ -14,6 +14,7 @@ use gpui::{
};
use language::Buffer;
+use platform_title_bar::PlatformTitleBar;
use project::{Project, ProjectPath, Worktree, WorktreeId};
use release_channel::ReleaseChannel;
use schemars::JsonSchema;
@@ -32,20 +33,19 @@ use std::{
time::Duration,
};
use theme::ThemeSettings;
-use title_bar::platform_title_bar::PlatformTitleBar;
use ui::{
Banner, ContextMenu, Divider, DropdownMenu, DropdownStyle, IconButtonShape, KeyBinding,
KeybindingHint, PopoverMenu, Scrollbars, Switch, Tooltip, TreeViewItem, WithScrollbar,
prelude::*,
};
-use ui_input::{NumberField, NumberFieldMode, NumberFieldType};
+
use util::{ResultExt as _, paths::PathStyle, rel_path::RelPath};
use workspace::{AppState, OpenOptions, OpenVisible, Workspace, client_side_decorations};
use zed_actions::{OpenProjectSettings, OpenSettings, OpenSettingsAt};
use crate::components::{
- EnumVariantDropdown, SettingsInputField, SettingsSectionHeader, font_picker, icon_theme_picker,
- theme_picker,
+ EnumVariantDropdown, NumberField, NumberFieldMode, NumberFieldType, SettingsInputField,
+ SettingsSectionHeader, font_picker, icon_theme_picker, theme_picker,
};
const NAVBAR_CONTAINER_TAB_INDEX: isize = 0;
@@ -12,11 +12,11 @@ workspace = true
path = "src/snippets_ui.rs"
[dependencies]
-file_finder.workspace = true
file_icons.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
+open_path_prompt.workspace = true
paths.workspace = true
picker.workspace = true
settings.workspace = true
@@ -1,4 +1,3 @@
-use file_finder::file_finder_settings::FileFinderSettings;
use file_icons::FileIcons;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{
@@ -6,6 +5,7 @@ use gpui::{
WeakEntity, Window, actions,
};
use language::{LanguageMatcher, LanguageName, LanguageRegistry};
+use open_path_prompt::file_finder_settings::FileFinderSettings;
use paths::snippets_dir;
use picker::{Picker, PickerDelegate};
use settings::Settings;
@@ -31,6 +31,7 @@ test-support = [
[dependencies]
anyhow.workspace = true
auto_update.workspace = true
+platform_title_bar.workspace = true
call.workspace = true
channel.workspace = true
chrono.workspace = true
@@ -1,19 +1,16 @@
mod application_menu;
pub mod collab;
mod onboarding_banner;
-pub mod platform_title_bar;
-mod platforms;
mod project_dropdown;
-mod system_window_tabs;
mod title_bar_settings;
#[cfg(feature = "stories")]
mod stories;
-use crate::{
- application_menu::{ApplicationMenu, show_menus},
- platform_title_bar::PlatformTitleBar,
- system_window_tabs::SystemWindowTabs,
+use crate::application_menu::{ApplicationMenu, show_menus};
+pub use platform_title_bar::{
+ self, DraggedWindowTab, MergeAllWindows, MoveTabToNewWindow, PlatformTitleBar,
+ ShowNextWindowTab, ShowPreviousWindowTab,
};
#[cfg(not(target_os = "macos"))]
@@ -69,7 +66,7 @@ actions!(
);
pub fn init(cx: &mut App) {
- SystemWindowTabs::init(cx);
+ platform_title_bar::PlatformTitleBar::init(cx);
cx.observe_new(|workspace: &mut Workspace, window, cx| {
let Some(window) = window else {
@@ -9,12 +9,12 @@ license = "GPL-3.0-or-later"
anyhow.workspace = true
convert_case.workspace = true
editor.workspace = true
-file_finder.workspace = true
futures.workspace = true
fuzzy.workspace = true
gpui.workspace = true
language.workspace = true
menu.workspace = true
+open_path_prompt.workspace = true
picker.workspace = true
project.workspace = true
ui.workspace = true
@@ -4,7 +4,6 @@ pub use active_toolchain::ActiveToolchain;
use anyhow::Context as _;
use convert_case::Casing as _;
use editor::Editor;
-use file_finder::OpenPathDelegate;
use futures::channel::oneshot;
use fuzzy::{StringMatch, StringMatchCandidate, match_strings};
use gpui::{
@@ -13,6 +12,7 @@ use gpui::{
actions, pulsating_between,
};
use language::{Language, LanguageName, Toolchain, ToolchainScope};
+use open_path_prompt::OpenPathDelegate;
use picker::{Picker, PickerDelegate};
use project::{DirectoryLister, Project, ProjectPath, Toolchains, WorktreeId};
use std::{
@@ -82,7 +82,7 @@ enum PathInputState {
enum AddState {
Path {
- picker: Entity<Picker<file_finder::OpenPathDelegate>>,
+ picker: Entity<Picker<open_path_prompt::OpenPathDelegate>>,
error: Option<Arc<str>>,
input_state: PathInputState,
_subscription: Subscription,
@@ -226,11 +226,7 @@ impl AddToolchainState {
Self::create_path_browser_delegate(this.project.clone(), cx);
picker.update(cx, |picker, cx| {
*picker = Picker::uniform_list(delegate, window, cx);
- picker.set_query(
- Arc::from(path.to_string_lossy().as_ref()),
- window,
- cx,
- );
+ picker.set_query(path.to_string_lossy().as_ref(), window, cx);
});
*input_state = Self::wait_for_path(rx, window, cx);
this.focus_handle(cx).focus(window, cx);
@@ -13,11 +13,7 @@ path = "src/ui_input.rs"
[dependencies]
component.workspace = true
-editor.workspace = true
gpui.workspace = true
-menu.workspace = true
-settings.workspace = true
-theme.workspace = true
ui.workspace = true
[features]
@@ -1,11 +1,12 @@
use component::{example_group, single_example};
-use editor::{Editor, EditorElement, EditorStyle};
-use gpui::{App, Entity, FocusHandle, Focusable, FontStyle, Hsla, Length, TextStyle};
-use settings::Settings;
+
+use gpui::{App, FocusHandle, Focusable, Hsla, Length};
use std::sync::Arc;
-use theme::ThemeSettings;
+
use ui::prelude::*;
+use crate::ErasedEditor;
+
pub struct InputFieldStyle {
text_color: Hsla,
background_color: Hsla,
@@ -25,16 +26,12 @@ pub struct InputField {
label_size: LabelSize,
/// The placeholder text for the text field.
placeholder: SharedString,
- /// Exposes the underlying [`Entity<Editor>`] to allow for customizing the editor beyond the provided API.
- ///
- /// This likely will only be public in the short term, ideally the API will be expanded to cover necessary use cases.
- pub editor: Entity<Editor>,
+
+ editor: Arc<dyn ErasedEditor>,
/// An optional icon that is displayed at the start of the text field.
///
/// For example, a magnifying glass icon in a search field.
start_icon: Option<IconName>,
- /// Whether the text field is disabled.
- disabled: bool,
/// The minimum width of for the input
min_width: Length,
/// The tab index for keyboard navigation order.
@@ -50,22 +47,19 @@ impl Focusable for InputField {
}
impl InputField {
- pub fn new(window: &mut Window, cx: &mut App, placeholder: impl Into<SharedString>) -> Self {
- let placeholder_text = placeholder.into();
-
- let editor = cx.new(|cx| {
- let mut input = Editor::single_line(window, cx);
- input.set_placeholder_text(&placeholder_text, window, cx);
- input
- });
+ pub fn new(window: &mut Window, cx: &mut App, placeholder_text: &str) -> Self {
+ let editor_factory = crate::ERASED_EDITOR_FACTORY
+ .get()
+ .expect("ErasedEditorFactory to be initialized");
+ let editor = (editor_factory)(window, cx);
+ editor.set_placeholder_text(placeholder_text, window, cx);
Self {
label: None,
label_size: LabelSize::Small,
- placeholder: placeholder_text,
+ placeholder: SharedString::new(placeholder_text),
editor,
start_icon: None,
- disabled: false,
min_width: px(192.).into(),
tab_index: None,
tab_stop: true,
@@ -102,77 +96,39 @@ impl InputField {
self
}
- pub fn set_disabled(&mut self, disabled: bool, cx: &mut Context<Self>) {
- self.disabled = disabled;
- self.editor
- .update(cx, |editor, _| editor.set_read_only(disabled))
- }
-
pub fn is_empty(&self, cx: &App) -> bool {
- self.editor().read(cx).text(cx).trim().is_empty()
+ self.editor().text(cx).trim().is_empty()
}
- pub fn editor(&self) -> &Entity<Editor> {
+ pub fn editor(&self) -> &Arc<dyn ErasedEditor> {
&self.editor
}
pub fn text(&self, cx: &App) -> String {
- self.editor().read(cx).text(cx)
+ self.editor().text(cx)
}
pub fn clear(&self, window: &mut Window, cx: &mut App) {
- self.editor()
- .update(cx, |editor, cx| editor.clear(window, cx))
+ self.editor().clear(window, cx)
}
- pub fn set_text(&self, text: impl Into<Arc<str>>, window: &mut Window, cx: &mut App) {
- self.editor()
- .update(cx, |editor, cx| editor.set_text(text, window, cx))
+ pub fn set_text(&self, text: &str, window: &mut Window, cx: &mut App) {
+ self.editor().set_text(text, window, cx)
}
}
impl Render for InputField {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let editor = self.editor.clone();
- let settings = ThemeSettings::get_global(cx);
+
let theme_color = cx.theme().colors();
- let mut style = InputFieldStyle {
+ let style = InputFieldStyle {
text_color: theme_color.text,
background_color: theme_color.editor_background,
border_color: theme_color.border_variant,
};
- if self.disabled {
- style.text_color = theme_color.text_disabled;
- style.background_color = theme_color.editor_background;
- style.border_color = theme_color.border_disabled;
- }
-
- // if self.error_message.is_some() {
- // style.text_color = cx.theme().status().error;
- // style.border_color = cx.theme().status().error_border
- // }
-
- let text_style = TextStyle {
- font_family: settings.ui_font.family.clone(),
- font_features: settings.ui_font.features.clone(),
- font_size: rems(0.875).into(),
- font_weight: settings.buffer_font.weight,
- font_style: FontStyle::Normal,
- line_height: relative(1.2),
- color: style.text_color,
- ..Default::default()
- };
-
- let editor_style = EditorStyle {
- background: theme_color.ghost_element_background,
- local_player: cx.theme().players().local(),
- syntax: cx.theme().syntax().clone(),
- text: text_style,
- ..Default::default()
- };
-
let focus_handle = self.editor.focus_handle(cx);
let configured_handle = if let Some(tab_index) = self.tab_index {
@@ -191,11 +147,7 @@ impl Render for InputField {
this.child(
Label::new(label)
.size(self.label_size)
- .color(if self.disabled {
- Color::Disabled
- } else {
- Color::Default
- }),
+ .color(Color::Default),
)
})
.child(
@@ -220,7 +172,7 @@ impl Render for InputField {
this.gap_1()
.child(Icon::new(icon).size(IconSize::Small).color(Color::Muted))
})
- .child(EditorElement::new(&self.editor, editor_style)),
+ .child(self.editor.render(window, cx)),
)
}
}
@@ -3,7 +3,39 @@
//! It can't be located in the `ui` crate because it depends on `editor`.
//!
mod input_field;
-mod number_field;
+use std::{
+ any::Any,
+ sync::{Arc, OnceLock},
+};
+
+use gpui::{FocusHandle, Subscription};
pub use input_field::*;
-pub use number_field::*;
+use ui::{AnyElement, App, Window};
+
+pub trait ErasedEditor: 'static {
+ fn text(&self, cx: &App) -> String;
+ fn set_text(&self, text: &str, window: &mut Window, cx: &mut App);
+ fn clear(&self, window: &mut Window, cx: &mut App);
+ fn set_placeholder_text(&self, text: &str, window: &mut Window, _: &mut App);
+ fn move_selection_to_end(&self, window: &mut Window, _: &mut App);
+
+ fn focus_handle(&self, cx: &App) -> FocusHandle;
+
+ fn subscribe(
+ &self,
+ callback: Box<dyn FnMut(ErasedEditorEvent, &mut Window, &mut App) + 'static>,
+ window: &mut Window,
+ cx: &mut App,
+ ) -> Subscription;
+ fn render(&self, window: &mut Window, cx: &App) -> AnyElement;
+ fn as_any(&self) -> &dyn Any;
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ErasedEditorEvent {
+ BufferEdited,
+ Blurred,
+}
+pub static ERASED_EDITOR_FACTORY: OnceLock<fn(&mut Window, &mut App) -> Arc<dyn ErasedEditor>> =
+ OnceLock::new();
@@ -182,6 +182,19 @@ pub struct ResetAllZoom {
pub persist: bool,
}
+pub mod editor {
+ use gpui::actions;
+ actions!(
+ editor,
+ [
+ /// Moves cursor up.
+ MoveUp,
+ /// Moves cursor down.
+ MoveDown,
+ ]
+ );
+}
+
pub mod dev {
use gpui::actions;
@@ -340,6 +353,16 @@ pub mod icon_theme_selector {
}
}
+pub mod search {
+ use gpui::actions;
+ actions!(
+ search,
+ [
+ /// Toggles searching in ignored files.
+ ToggleIncludeIgnored
+ ]
+ );
+}
pub mod settings_profile_selector {
use gpui::Action;
use schemars::JsonSchema;