diff --git a/Cargo.lock b/Cargo.lock index 9c6093504f4e6088bd98fd92930236181f89441b..c243209655452fdf9c7362fce5bf7f5d3f07d998 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -863,7 +863,6 @@ dependencies = [ "assistant_slash_command", "chrono", "collections", - "context_server", "editor", "feature_flags", "fs", @@ -2346,7 +2345,6 @@ dependencies = [ name = "breadcrumbs" version = "0.1.0" dependencies = [ - "editor", "gpui", "ui", "workspace", @@ -4975,7 +4973,6 @@ dependencies = [ "pretty_assertions", "project", "rand 0.9.2", - "search", "serde", "serde_json", "settings", @@ -5506,6 +5503,7 @@ dependencies = [ "aho-corasick", "anyhow", "assets", + "breadcrumbs", "buffer_diff", "client", "clock", @@ -14859,7 +14857,6 @@ dependencies = [ "menu", "pretty_assertions", "project", - "schemars", "serde", "serde_json", "settings", @@ -16887,7 +16884,6 @@ dependencies = [ "rand 0.9.2", "regex", "schemars", - "search", "serde", "serde_json", "settings", @@ -21260,6 +21256,7 @@ dependencies = [ "gpui", "schemars", "serde", + "util", "uuid", ] diff --git a/crates/assistant_slash_commands/Cargo.toml b/crates/assistant_slash_commands/Cargo.toml index 843aab9b60255fb5952921439d2ca1fea8b108b7..bc156712f7ee98d8780e74a1510a9d209ffded53 100644 --- a/crates/assistant_slash_commands/Cargo.toml +++ b/crates/assistant_slash_commands/Cargo.toml @@ -16,7 +16,6 @@ anyhow.workspace = true assistant_slash_command.workspace = true chrono.workspace = true collections.workspace = true -context_server.workspace = true editor.workspace = true feature_flags.workspace = true fs.workspace = true diff --git a/crates/assistant_slash_commands/src/assistant_slash_commands.rs b/crates/assistant_slash_commands/src/assistant_slash_commands.rs index 2bf2573e99d7a5a0140c1972967ec68523b0b56a..bfb3784ffcc1e76680724d09586c1674df09016b 100644 --- a/crates/assistant_slash_commands/src/assistant_slash_commands.rs +++ b/crates/assistant_slash_commands/src/assistant_slash_commands.rs @@ -1,4 +1,3 @@ -mod context_server_command; mod default_command; mod delta_command; mod diagnostics_command; @@ -11,7 +10,6 @@ mod streaming_example_command; mod symbols_command; mod tab_command; -pub use crate::context_server_command::*; pub use crate::default_command::*; pub use crate::delta_command::*; pub use crate::diagnostics_command::*; diff --git a/crates/assistant_slash_commands/src/cargo_workspace_command.rs b/crates/assistant_slash_commands/src/cargo_workspace_command.rs deleted file mode 100644 index 8a6950a4a2ff40d0452669dd388886d05d71022a..0000000000000000000000000000000000000000 --- a/crates/assistant_slash_commands/src/cargo_workspace_command.rs +++ /dev/null @@ -1,159 +0,0 @@ -use anyhow::{Context as _, Result, anyhow}; -use assistant_slash_command::{ - ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection, - SlashCommandResult, -}; -use fs::Fs; -use gpui::{App, Entity, Task, WeakEntity}; -use language::{BufferSnapshot, LspAdapterDelegate}; -use project::{Project, ProjectPath}; -use std::{ - fmt::Write, - path::Path, - sync::{Arc, atomic::AtomicBool}, -}; -use ui::prelude::*; -use util::rel_path::RelPath; -use workspace::Workspace; - -pub struct CargoWorkspaceSlashCommand; - -impl CargoWorkspaceSlashCommand { - async fn build_message(fs: Arc, path_to_cargo_toml: &Path) -> Result { - let buffer = fs.load(path_to_cargo_toml).await?; - let cargo_toml: cargo_toml::Manifest = toml::from_str(&buffer)?; - - let mut message = String::new(); - writeln!(message, "You are in a Rust project.")?; - - if let Some(workspace) = cargo_toml.workspace { - writeln!( - message, - "The project is a Cargo workspace with the following members:" - )?; - for member in workspace.members { - writeln!(message, "- {member}")?; - } - - if !workspace.default_members.is_empty() { - writeln!(message, "The default members are:")?; - for member in workspace.default_members { - writeln!(message, "- {member}")?; - } - } - - if !workspace.dependencies.is_empty() { - writeln!( - message, - "The following workspace dependencies are installed:" - )?; - for dependency in workspace.dependencies.keys() { - writeln!(message, "- {dependency}")?; - } - } - } else if let Some(package) = cargo_toml.package { - writeln!( - message, - "The project name is \"{name}\".", - name = package.name - )?; - - let description = package - .description - .as_ref() - .and_then(|description| description.get().ok().cloned()); - if let Some(description) = description.as_ref() { - writeln!(message, "It describes itself as \"{description}\".")?; - } - - if !cargo_toml.dependencies.is_empty() { - writeln!(message, "The following dependencies are installed:")?; - for dependency in cargo_toml.dependencies.keys() { - writeln!(message, "- {dependency}")?; - } - } - } - - Ok(message) - } - - fn path_to_cargo_toml(project: Entity, cx: &mut App) -> Option> { - let worktree = project.read(cx).worktrees(cx).next()?; - let worktree = worktree.read(cx); - let entry = worktree.entry_for_path(RelPath::new("Cargo.toml").unwrap())?; - let path = ProjectPath { - worktree_id: worktree.id(), - path: entry.path.clone(), - }; - Some(Arc::from( - project.read(cx).absolute_path(&path, cx)?.as_path(), - )) - } -} - -impl SlashCommand for CargoWorkspaceSlashCommand { - fn name(&self) -> String { - "cargo-workspace".into() - } - - fn description(&self) -> String { - "insert project workspace metadata".into() - } - - fn menu_text(&self) -> String { - "Insert Project Workspace Metadata".into() - } - - fn complete_argument( - self: Arc, - _arguments: &[String], - _cancel: Arc, - _workspace: Option>, - _window: &mut Window, - _cx: &mut App, - ) -> Task>> { - Task::ready(Err(anyhow!("this command does not require argument"))) - } - - fn requires_argument(&self) -> bool { - false - } - - fn run( - self: Arc, - _arguments: &[String], - _context_slash_command_output_sections: &[SlashCommandOutputSection], - _context_buffer: BufferSnapshot, - workspace: WeakEntity, - _delegate: Option>, - _window: &mut Window, - cx: &mut App, - ) -> Task { - let output = workspace.update(cx, |workspace, cx| { - let project = workspace.project().clone(); - let fs = workspace.project().read(cx).fs().clone(); - let path = Self::path_to_cargo_toml(project, cx); - let output = cx.background_spawn(async move { - let path = path.with_context(|| "Cargo.toml not found")?; - Self::build_message(fs, &path).await - }); - - cx.foreground_executor().spawn(async move { - let text = output.await?; - let range = 0..text.len(); - Ok(SlashCommandOutput { - text, - sections: vec![SlashCommandOutputSection { - range, - icon: IconName::FileTree, - label: "Project".into(), - metadata: None, - }], - run_commands_in_text: false, - } - .into_event_stream()) - }) - }); - output.unwrap_or_else(|error| Task::ready(Err(error))) - } -} diff --git a/crates/assistant_text_thread/Cargo.toml b/crates/assistant_text_thread/Cargo.toml index 5ad429758ea1785ecb4fcecb2f3ad83a71afda0d..4c3563a7d26dca06282d5f3d15ec2a64c411dfba 100644 --- a/crates/assistant_text_thread/Cargo.toml +++ b/crates/assistant_text_thread/Cargo.toml @@ -18,7 +18,6 @@ test-support = [] agent_settings.workspace = true anyhow.workspace = true assistant_slash_command.workspace = true -assistant_slash_commands.workspace = true chrono.workspace = true client.workspace = true clock.workspace = true @@ -55,6 +54,7 @@ workspace.workspace = true zed_env_vars.workspace = true [dev-dependencies] +assistant_slash_commands.workspace = true indoc.workspace = true language_model = { workspace = true, features = ["test-support"] } pretty_assertions.workspace = true diff --git a/crates/assistant_text_thread/src/assistant_text_thread.rs b/crates/assistant_text_thread/src/assistant_text_thread.rs index 7eab9800d5d6f43ba8eabec0682961e073781ace..6b0602121fe460e26eb13f8d1a186f26a434df26 100644 --- a/crates/assistant_text_thread/src/assistant_text_thread.rs +++ b/crates/assistant_text_thread/src/assistant_text_thread.rs @@ -1,5 +1,6 @@ #[cfg(test)] mod assistant_text_thread_tests; +mod context_server_command; mod text_thread; mod text_thread_store; diff --git a/crates/assistant_slash_commands/src/context_server_command.rs b/crates/assistant_text_thread/src/context_server_command.rs similarity index 99% rename from crates/assistant_slash_commands/src/context_server_command.rs rename to crates/assistant_text_thread/src/context_server_command.rs index ee0cbf54c23a595f6503162c91dd1df3be019dd5..55e5664f7aef67591e0f53c2fa670f41b1143f98 100644 --- a/crates/assistant_slash_commands/src/context_server_command.rs +++ b/crates/assistant_text_thread/src/context_server_command.rs @@ -14,7 +14,7 @@ use text::LineEnding; use ui::{IconName, SharedString}; use workspace::Workspace; -use crate::create_label_for_command; +use assistant_slash_command::create_label_for_command; pub struct ContextServerSlashCommand { store: Entity, diff --git a/crates/assistant_text_thread/src/text_thread.rs b/crates/assistant_text_thread/src/text_thread.rs index 471a4c1db9532647d4c439aa25b6b13a5e372cdc..684ef57602651aa2b05defe3e3ec2c2682684013 100644 --- a/crates/assistant_text_thread/src/text_thread.rs +++ b/crates/assistant_text_thread/src/text_thread.rs @@ -4,7 +4,6 @@ use assistant_slash_command::{ SlashCommandContent, SlashCommandEvent, SlashCommandLine, SlashCommandOutputSection, SlashCommandResult, SlashCommandWorkingSet, }; -use assistant_slash_commands::FileCommandMetadata; use client::{self, proto}; use clock::ReplicaId; use cloud_llm_client::CompletionIntent; @@ -1176,6 +1175,11 @@ impl TextThread { } pub fn contains_files(&self, cx: &App) -> bool { + // Mimics assistant_slash_commands::FileCommandMetadata. + #[derive(Serialize, Deserialize)] + pub struct FileCommandMetadata { + pub path: String, + } let buffer = self.buffer.read(cx); self.slash_command_output_sections.iter().any(|section| { section.is_valid(buffer) diff --git a/crates/assistant_text_thread/src/text_thread_store.rs b/crates/assistant_text_thread/src/text_thread_store.rs index 18661902c0baefba2fff6d64ffe5091d2e6c313f..248a57d6861ccf2af30a80d1c62687f943542c12 100644 --- a/crates/assistant_text_thread/src/text_thread_store.rs +++ b/crates/assistant_text_thread/src/text_thread_store.rs @@ -1,6 +1,6 @@ use crate::{ SavedTextThread, SavedTextThreadMetadata, TextThread, TextThreadEvent, TextThreadId, - TextThreadOperation, TextThreadVersion, + TextThreadOperation, TextThreadVersion, context_server_command, }; use anyhow::{Context as _, Result}; use assistant_slash_command::{SlashCommandId, SlashCommandWorkingSet}; @@ -938,11 +938,11 @@ impl TextThreadStore { let slash_command_ids = response .prompts .into_iter() - .filter(assistant_slash_commands::acceptable_prompt) + .filter(context_server_command::acceptable_prompt) .map(|prompt| { log::info!("registering context server command: {:?}", prompt.name); slash_command_working_set.insert(Arc::new( - assistant_slash_commands::ContextServerSlashCommand::new( + context_server_command::ContextServerSlashCommand::new( context_server_store.clone(), server.id(), prompt, diff --git a/crates/breadcrumbs/Cargo.toml b/crates/breadcrumbs/Cargo.toml index bd020af6ba4cc96bc2c7c8721b2d536792f023a3..a74974774c0f0742a39d871515b61035752d8de3 100644 --- a/crates/breadcrumbs/Cargo.toml +++ b/crates/breadcrumbs/Cargo.toml @@ -13,12 +13,10 @@ path = "src/breadcrumbs.rs" doctest = false [dependencies] -editor.workspace = true gpui.workspace = true ui.workspace = true workspace.workspace = true [dev-dependencies] -editor = { workspace = true, features = ["test-support"] } gpui = { workspace = true, features = ["test-support"] } -workspace = { workspace = true, features = ["test-support"] } +workspace = { workspace = true, features = ["test-support"] } \ No newline at end of file diff --git a/crates/breadcrumbs/src/breadcrumbs.rs b/crates/breadcrumbs/src/breadcrumbs.rs index cf456065524a5a241ea6e05e68292ea0fea2f48e..54a5e40337dc4b41ddd668783656498e9be841b9 100644 --- a/crates/breadcrumbs/src/breadcrumbs.rs +++ b/crates/breadcrumbs/src/breadcrumbs.rs @@ -1,11 +1,25 @@ -use editor::render_breadcrumb_text; -use gpui::{Context, EventEmitter, IntoElement, Render, Subscription, Window}; +use gpui::{ + AnyElement, App, Context, EventEmitter, Global, IntoElement, Render, Subscription, Window, +}; use ui::prelude::*; use workspace::{ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, - item::{ItemEvent, ItemHandle}, + item::{BreadcrumbText, ItemEvent, ItemHandle}, }; +type RenderBreadcrumbTextFn = fn( + Vec, + Option, + &dyn ItemHandle, + bool, + &mut Window, + &App, +) -> AnyElement; + +pub struct RenderBreadcrumbText(pub RenderBreadcrumbTextFn); + +impl Global for RenderBreadcrumbText {} + pub struct Breadcrumbs { pane_focused: bool, active_item: Option>, @@ -49,15 +63,18 @@ impl Render for Breadcrumbs { let prefix_element = active_item.breadcrumb_prefix(window, cx); - render_breadcrumb_text( - segments, - prefix_element, - active_item.as_ref(), - false, - window, - cx, - ) - .into_any_element() + if let Some(render_fn) = cx.try_global::() { + (render_fn.0)( + segments, + prefix_element, + active_item.as_ref(), + false, + window, + cx, + ) + } else { + element.into_any_element() + } } } diff --git a/crates/diagnostics/Cargo.toml b/crates/diagnostics/Cargo.toml index 3d59d0d0fda229e3db806725f24c0590fe9067fb..68877168dd11679592a9276f69d04ff536e9dafa 100644 --- a/crates/diagnostics/Cargo.toml +++ b/crates/diagnostics/Cargo.toml @@ -27,7 +27,7 @@ lsp.workspace = true markdown.workspace = true project.workspace = true rand.workspace = true -search.workspace = true + serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/diagnostics/src/toolbar_controls.rs b/crates/diagnostics/src/toolbar_controls.rs index a1212c3dc50844a23ce080f41a6027598fbd9813..28bcb328bbbe9ff550d3d081b6a64a204641ff52 100644 --- a/crates/diagnostics/src/toolbar_controls.rs +++ b/crates/diagnostics/src/toolbar_controls.rs @@ -1,11 +1,11 @@ use crate::{BufferDiagnosticsEditor, ProjectDiagnosticsEditor, ToggleDiagnosticsRefresh}; use gpui::{Context, EventEmitter, ParentElement, Render, Window}; use language::DiagnosticEntry; -use search::buffer_search; use text::{Anchor, BufferId}; use ui::{Tooltip, prelude::*}; use workspace::{ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, item::ItemHandle}; use zed_actions::assistant::InlineAssist; +use zed_actions::buffer_search; pub struct ToolbarControls { editor: Option>, diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index 8122af7053bc9af63768d9a38fa40d23d7208a6b..b200d25f6d9ca90e862091b1b999613b0f5e2723 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -33,6 +33,7 @@ test-support = [ aho-corasick.workspace = true anyhow.workspace = true assets.workspace = true +breadcrumbs.workspace = true client.workspace = true clock.workspace = true collections.workspace = true diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 89a53ed8c800f942605cd88a169ff6d899e80018..afda579bcff13bb96d0ad94695745724a2ca4deb 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -328,6 +328,7 @@ pub enum HideMouseCursorOrigin { pub fn init(cx: &mut App) { cx.set_global(GlobalBlameRenderer(Arc::new(()))); + cx.set_global(breadcrumbs::RenderBreadcrumbText(render_breadcrumb_text)); workspace::register_project_item::(cx); workspace::FollowableViewRegistry::register::(cx); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d1c5f3c8be8500d1e89308aaf54d407632d8f21a..d1fd99f09217dc736c12c3f8902fc2bdb777e03d 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -7776,7 +7776,7 @@ pub fn render_breadcrumb_text( multibuffer_header: bool, window: &mut Window, cx: &App, -) -> impl IntoElement { +) -> gpui::AnyElement { const MAX_SEGMENTS: usize = 12; let element = h_flex().flex_grow().text_ui(cx); diff --git a/crates/search/Cargo.toml b/crates/search/Cargo.toml index 1b0864e5cc2e25334085b39affe29f61a4147aa0..9fa515d4349a34fb4bfd62d5466f651a6ad28057 100644 --- a/crates/search/Cargo.toml +++ b/crates/search/Cargo.toml @@ -32,7 +32,6 @@ gpui.workspace = true language.workspace = true menu.workspace = true project.workspace = true -schemars.workspace = true serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index bb192261083ca517fb2de4440e4eb8b6d59a033e..2f0b676b65d77794f16f16b554378dc3432a20aa 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -22,15 +22,14 @@ use futures::channel::oneshot; use gpui::{ Action, App, ClickEvent, Context, Entity, EventEmitter, Focusable, InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, ScrollHandle, Styled, Subscription, Task, - WeakEntity, Window, actions, div, + WeakEntity, Window, div, }; use language::{Language, LanguageRegistry}; use project::{ search::SearchQuery, search_history::{SearchHistory, SearchHistoryCursor}, }; -use schemars::JsonSchema; -use serde::Deserialize; + use settings::Settings; use std::{any::TypeId, sync::Arc}; use zed_actions::{outline::ToggleOutline, workspace::CopyPath, workspace::CopyRelativePath}; @@ -46,53 +45,12 @@ use workspace::{ }, }; -pub use registrar::DivRegistrar; +pub use registrar::{DivRegistrar, register_pane_search_actions}; use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar}; const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50; -/// Opens the buffer search interface with the specified configuration. -#[derive(PartialEq, Clone, Deserialize, JsonSchema, Action)] -#[action(namespace = buffer_search)] -#[serde(deny_unknown_fields)] -pub struct Deploy { - #[serde(default = "util::serde::default_true")] - pub focus: bool, - #[serde(default)] - pub replace_enabled: bool, - #[serde(default)] - pub selection_search_enabled: bool, -} - -actions!( - buffer_search, - [ - /// Deploys the search and replace interface. - DeployReplace, - /// Dismisses the search bar. - Dismiss, - /// Focuses back on the editor. - FocusEditor - ] -); - -impl Deploy { - pub fn find() -> Self { - Self { - focus: true, - replace_enabled: false, - selection_search_enabled: false, - } - } - - pub fn replace() -> Self { - Self { - focus: true, - replace_enabled: true, - selection_search_enabled: false, - } - } -} +pub use zed_actions::buffer_search::{Deploy, DeployReplace, Dismiss, FocusEditor}; pub enum Event { UpdateLocation, diff --git a/crates/search/src/buffer_search/registrar.rs b/crates/search/src/buffer_search/registrar.rs index 2d0d58a71e7457b2f5ea47135b2d55a5372ce6bf..618f52aa10715e084abc9bdb298f21a41d86f8f0 100644 --- a/crates/search/src/buffer_search/registrar.rs +++ b/crates/search/src/buffer_search/registrar.rs @@ -1,5 +1,5 @@ -use gpui::{Action, Context, Div, Entity, InteractiveElement, Window, div}; -use workspace::Workspace; +use gpui::{Action, App, Context, Div, Entity, InteractiveElement, Window, div}; +use workspace::{Pane, Workspace}; use crate::BufferSearchBar; @@ -58,6 +58,57 @@ impl SearchActionsRegistrar for DivRegistrar<'_, '_, T> { } } +pub struct PaneDivRegistrar { + div: Option
, + pane: Entity, +} + +impl PaneDivRegistrar { + pub fn new(div: Div, pane: Entity) -> Self { + Self { + div: Some(div), + pane, + } + } + + pub fn into_div(self) -> Div { + self.div.unwrap() + } +} + +impl SearchActionsRegistrar for PaneDivRegistrar { + fn register_handler(&mut self, callback: impl ActionExecutor) { + let pane = self.pane.clone(); + self.div = self.div.take().map(|div| { + div.on_action(move |action: &A, window: &mut Window, cx: &mut App| { + let search_bar = pane + .read(cx) + .toolbar() + .read(cx) + .item_of_type::(); + let should_notify = search_bar + .map(|search_bar| { + search_bar.update(cx, |search_bar, cx| { + callback.execute(search_bar, action, window, cx) + }) + }) + .unwrap_or(false); + if should_notify { + pane.update(cx, |_, cx| cx.notify()); + } else { + cx.propagate(); + } + }) + }); + } +} + +pub fn register_pane_search_actions(div: Div, pane: Entity) -> Div { + let mut registrar = PaneDivRegistrar::new(div, pane); + BufferSearchBar::register(&mut registrar); + registrar.into_div() +} + /// Register actions for an active pane. impl SearchActionsRegistrar for Workspace { fn register_handler(&mut self, callback: impl ActionExecutor) { diff --git a/crates/terminal_view/Cargo.toml b/crates/terminal_view/Cargo.toml index dfac354ddac223109a9c5de86e5d1d040a3cf287..ef31480341ddc873e00612b471217899836a3bd1 100644 --- a/crates/terminal_view/Cargo.toml +++ b/crates/terminal_view/Cargo.toml @@ -35,7 +35,7 @@ project.workspace = true regex.workspace = true task.workspace = true schemars.workspace = true -search.workspace = true + serde.workspace = true serde_json.workspace = true settings.workspace = true diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 9d6049b6944dbf7a574b5c721b01b59bc7cf84f7..292028f7077b99eb1bc47542c1cfb507fc42ef69 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -17,7 +17,7 @@ use gpui::{ }; use itertools::Itertools; use project::{Fs, Project, ProjectEntryId}; -use search::{BufferSearchBar, buffer_search::DivRegistrar}; + use settings::{Settings, TerminalDockPosition}; use task::{RevealStrategy, RevealTarget, Shell, ShellBuilder, SpawnInTerminal, TaskId}; use terminal::{Terminal, terminal_settings::TerminalSettings}; @@ -1238,12 +1238,13 @@ pub fn new_terminal_pane( false }))); - let buffer_search_bar = cx.new(|cx| { - search::BufferSearchBar::new(Some(project.read(cx).languages().clone()), window, cx) - }); + let toolbar = pane.toolbar().clone(); + if let Some(callbacks) = cx.try_global::() { + let languages = Some(project.read(cx).languages().clone()); + (callbacks.setup_search_bar)(languages, &toolbar, window, cx); + } let breadcrumbs = cx.new(|_| Breadcrumbs::new()); - pane.toolbar().update(cx, |toolbar, cx| { - toolbar.add_item(buffer_search_bar, window, cx); + toolbar.update(cx, |toolbar, cx| { toolbar.add_item(breadcrumbs, window, cx); }); @@ -1483,19 +1484,12 @@ impl EventEmitter for TerminalPanel {} impl Render for TerminalPanel { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let mut registrar = DivRegistrar::new( - |panel, _, cx| { - panel - .active_pane - .read(cx) - .toolbar() - .read(cx) - .item_of_type::() - }, - cx, - ); - BufferSearchBar::register(&mut registrar); - let registrar = registrar.into_div(); + let registrar = cx + .try_global::() + .map(|callbacks| { + (callbacks.wrap_div_with_search_actions)(div(), self.active_pane.clone()) + }) + .unwrap_or_else(div); self.workspace .update(cx, |workspace, cx| { registrar.size_full().child(self.center.render( diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index e4432a46dc1530572b71a376fbdac35ad43e28e1..b14775f289d27d31e57ef554478a8ddfcd5cfe86 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -1,11 +1,21 @@ use crate::ItemHandle; use gpui::{ - AnyView, App, Context, Entity, EntityId, EventEmitter, KeyContext, ParentElement as _, Render, - Styled, Window, + AnyView, App, Context, Div, Entity, EntityId, EventEmitter, Global, KeyContext, + ParentElement as _, Render, Styled, Window, }; +use language::LanguageRegistry; +use std::sync::Arc; use ui::prelude::*; use ui::{h_flex, v_flex}; +pub struct PaneSearchBarCallbacks { + pub setup_search_bar: + fn(Option>, &Entity, &mut Window, &mut App), + pub wrap_div_with_search_actions: fn(Div, Entity) -> Div, +} + +impl Global for PaneSearchBarCallbacks {} + #[derive(Copy, Clone, Debug, PartialEq)] pub enum ToolbarItemEvent { ChangeLocation(ToolbarItemLocation), diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ae1945af5b5af032cd86c45357cc50686ee4d691..8dff340e264abd583c471b95d96c90e14486a2c5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -118,7 +118,9 @@ use std::{ }; use task::{DebugScenario, SharedTaskContext, SpawnInTerminal}; use theme::{ActiveTheme, GlobalTheme, SystemAppearance, ThemeSettings}; -pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView}; +pub use toolbar::{ + PaneSearchBarCallbacks, Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, +}; pub use ui; use ui::{Window, prelude::*}; use util::{ diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3bac2b92198946118ed50d379cf0de611eeed8a1..9a2cea33882b1a9d8434bf0f3e2cb1dba8471007 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -659,6 +659,15 @@ fn main() { snippets_ui::init(cx); channel::init(&app_state.client.clone(), app_state.user_store.clone(), cx); search::init(cx); + cx.set_global(workspace::PaneSearchBarCallbacks { + setup_search_bar: |languages, toolbar, window, cx| { + let search_bar = cx.new(|cx| search::BufferSearchBar::new(languages, window, cx)); + toolbar.update(cx, |toolbar, cx| { + toolbar.add_item(search_bar, window, cx); + }); + }, + wrap_div_with_search_actions: search::buffer_search::register_pane_search_actions, + }); vim::init(cx); terminal_view::init(cx); journal::init(app_state.clone(), cx); diff --git a/crates/zed/src/visual_test_runner.rs b/crates/zed/src/visual_test_runner.rs index d93cb54d811ca49d6f628b364cebb1ed647eb300..e713c7b440263734fb1202f15ada029f7a3e2cab 100644 --- a/crates/zed/src/visual_test_runner.rs +++ b/crates/zed/src/visual_test_runner.rs @@ -187,6 +187,15 @@ fn run_visual_tests(project_path: PathBuf, update_baseline: bool) -> Result<()> terminal_view::init(cx); image_viewer::init(cx); search::init(cx); + cx.set_global(workspace::PaneSearchBarCallbacks { + setup_search_bar: |languages, toolbar, window, cx| { + let search_bar = cx.new(|cx| search::BufferSearchBar::new(languages, window, cx)); + toolbar.update(cx, |toolbar, cx| { + toolbar.add_item(search_bar, window, cx); + }); + }, + wrap_div_with_search_actions: search::buffer_search::register_pane_search_actions, + }); prompt_store::init(cx); language_model::init(app_state.client.clone(), cx); language_models::init(app_state.user_store.clone(), app_state.client.clone(), cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 79e19e7e01d130f2a550fc753a56a77235e91573..6e80f13cec5bebe67062aed2a2f722af4269b2e1 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -5067,6 +5067,16 @@ mod tests { debugger_ui::init(cx); initialize_workspace(app_state.clone(), prompt_builder, cx); search::init(cx); + cx.set_global(workspace::PaneSearchBarCallbacks { + setup_search_bar: |languages, toolbar, window, cx| { + let search_bar = + cx.new(|cx| search::BufferSearchBar::new(languages, window, cx)); + toolbar.update(cx, |toolbar, cx| { + toolbar.add_item(search_bar, window, cx); + }); + }, + wrap_div_with_search_actions: search::buffer_search::register_pane_search_actions, + }); app_state }) } diff --git a/crates/zed/src/zed/visual_tests.rs b/crates/zed/src/zed/visual_tests.rs index 58c4d54b9b2de02bd6c00e278020c0494a14a1de..5f725d9fd844563e6fedbcfa669b65f2b7346061 100644 --- a/crates/zed/src/zed/visual_tests.rs +++ b/crates/zed/src/zed/visual_tests.rs @@ -62,6 +62,15 @@ pub fn init_visual_test(cx: &mut VisualTestAppContext) -> Arc { terminal_view::init(cx); image_viewer::init(cx); search::init(cx); + cx.set_global(workspace::PaneSearchBarCallbacks { + setup_search_bar: |languages, toolbar, window, cx| { + let search_bar = cx.new(|cx| search::BufferSearchBar::new(languages, window, cx)); + toolbar.update(cx, |toolbar, cx| { + toolbar.add_item(search_bar, window, cx); + }); + }, + wrap_div_with_search_actions: search::buffer_search::register_pane_search_actions, + }); app_state }) diff --git a/crates/zed_actions/Cargo.toml b/crates/zed_actions/Cargo.toml index 1a140c483fff56b8b045b381adc67cbce778be39..9da1f323a270912038b016dee10b352cbcc363a2 100644 --- a/crates/zed_actions/Cargo.toml +++ b/crates/zed_actions/Cargo.toml @@ -12,4 +12,5 @@ workspace = true gpui.workspace = true schemars.workspace = true serde.workspace = true +util.workspace = true uuid.workspace = true diff --git a/crates/zed_actions/src/lib.rs b/crates/zed_actions/src/lib.rs index d13bfdd2f2e20d8c435009e210c2f2deadfdd038..874cb569a2d43065e091fe94cbe9575d0e24d8ba 100644 --- a/crates/zed_actions/src/lib.rs +++ b/crates/zed_actions/src/lib.rs @@ -363,6 +363,54 @@ pub mod search { ] ); } +pub mod buffer_search { + use gpui::{Action, actions}; + use schemars::JsonSchema; + use serde::Deserialize; + + /// Opens the buffer search interface with the specified configuration. + #[derive(PartialEq, Clone, Deserialize, JsonSchema, Action)] + #[action(namespace = buffer_search)] + #[serde(deny_unknown_fields)] + pub struct Deploy { + #[serde(default = "util::serde::default_true")] + pub focus: bool, + #[serde(default)] + pub replace_enabled: bool, + #[serde(default)] + pub selection_search_enabled: bool, + } + + impl Deploy { + pub fn find() -> Self { + Self { + focus: true, + replace_enabled: false, + selection_search_enabled: false, + } + } + + pub fn replace() -> Self { + Self { + focus: true, + replace_enabled: true, + selection_search_enabled: false, + } + } + } + + actions!( + buffer_search, + [ + /// Deploys the search and replace interface. + DeployReplace, + /// Dismisses the search bar. + Dismiss, + /// Focuses back on the editor. + FocusEditor + ] + ); +} pub mod settings_profile_selector { use gpui::Action; use schemars::JsonSchema;