mod registrar; use crate::{ FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOption, SearchOptions, SearchSource, SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord, buffer_search::registrar::WithResultsOrExternalQuery, search_bar::{ ActionButtonState, alignment_element, filter_search_results_input, input_base_styles, render_action_button, render_text_input, },};use any_vec::AnyVec;use collections::HashMap;use editor::{ Editor, EditorSettings, MultiBufferOffset, SplittableEditor, ToggleSplitDiff, actions::{Backtab, FoldAll, Tab, ToggleFoldAll, UnfoldAll},};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, div,};use language::{Language, LanguageRegistry};use project::{ search::SearchQuery, search_history::{SearchHistory, SearchHistoryCursor},}; use fs::Fs;use settings::{DiffViewStyle, Settings, update_settings_file};use std::{any::TypeId, sync::Arc};use zed_actions::{outline::ToggleOutline, workspace::CopyPath, workspace::CopyRelativePath}; use ui::{ BASE_REM_SIZE_IN_PX, IconButtonShape, PlatformStyle, TextSize, Tooltip, prelude::*, render_modifiers, utils::SearchInputWidth,};use util::{ResultExt, paths::PathMatcher};use workspace::{ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{ItemBufferKind, ItemHandle}, searchable::{ Direction, FilteredSearchRange, SearchEvent, SearchToken, SearchableItemHandle, WeakSearchableItemHandle, },}; pub use registrar::{DivRegistrar, register_pane_search_actions};use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar}; const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50; pub use zed_actions::buffer_search::{Deploy, DeployReplace, Dismiss, FocusEditor}; pub enum Event { UpdateLocation, Dismissed,} pub fn init(cx: &mut App) { cx.observe_new(|workspace: &mut Workspace, _, _| BufferSearchBar::register(workspace)) .detach();} pub struct BufferSearchBar { query_editor: Entity<Editor>, query_editor_focused: bool, replacement_editor: Entity<Editor>, replacement_editor_focused: bool, active_searchable_item: Option<Box<dyn SearchableItemHandle>>, active_match_index: Option<usize>, #[cfg(target_os = "macos")] active_searchable_item_subscriptions: Option<[Subscription; 2]>, #[cfg(not(target_os = "macos"))] active_searchable_item_subscriptions: Option<Subscription>, #[cfg(target_os = "macos")] pending_external_query: Option<(String, SearchOptions)>, active_search: Option<Arc<SearchQuery>>, searchable_items_with_matches: HashMap<Box<dyn WeakSearchableItemHandle>, (AnyVec<dyn Send>, SearchToken)>, pending_search: Option<Task<()>>, search_options: SearchOptions, default_options: SearchOptions, configured_options: SearchOptions, query_error: Option<String>, dismissed: bool, search_history: SearchHistory, search_history_cursor: SearchHistoryCursor, replace_enabled: bool, selection_search_enabled: Option<FilteredSearchRange>, scroll_handle: ScrollHandle, regex_language: Option<Arc<Language>>, splittable_editor: Option<WeakEntity<SplittableEditor>>, _splittable_editor_subscription: Option<Subscription>,} impl EventEmitter<Event> for BufferSearchBar {}impl EventEmitter<workspace::ToolbarItemEvent> for BufferSearchBar {}impl Render for BufferSearchBar { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let focus_handle = self.focus_handle(cx); let has_splittable_editor = self.splittable_editor.is_some(); let split_buttons = if has_splittable_editor { self.splittable_editor .as_ref() .and_then(|weak| weak.upgrade()) .map(|splittable_editor| { let is_split = splittable_editor.read(cx).is_split(); let focus_handle = splittable_editor.focus_handle(cx); h_flex() .gap_1() .child( IconButton::new("diff-unified", IconName::DiffUnified) .shape(IconButtonShape::Square) .toggle_state(!is_split) .tooltip(Tooltip::element(move |_, cx| { v_flex() .gap_1() .child(Label::new("Unified")) .child( h_flex() .gap_0p5() .text_sm() .text_color(Color::Muted.color(cx)) .children(render_modifiers( &gpui::Modifiers::secondary_key(), PlatformStyle::platform(), None, Some(TextSize::Default.rems(cx).into()), false, )) .child("click to set as default"), ) .into_any() })) .on_click({ let focus_handle = focus_handle.clone(); move |_, window, cx| { if window.modifiers().secondary() { update_settings_file( <dyn Fs>::global(cx), cx, |settings, _| { settings.editor.diff_view_style = Some(DiffViewStyle::Unified); }, ); } if is_split { focus_handle.focus(window, cx); window .dispatch_action(ToggleSplitDiff.boxed_clone(), cx); } } }), ) .child( IconButton::new("diff-split", IconName::DiffSplit) .shape(IconButtonShape::Square) .toggle_state(is_split) .tooltip(Tooltip::element(move |_, cx| { v_flex() .gap_1() .child(Label::new("Split")) .child( h_flex() .gap_0p5() .text_sm() .text_color(Color::Muted.color(cx)) .children(render_modifiers( &gpui::Modifiers::secondary_key(), PlatformStyle::platform(), None, Some(TextSize::Default.rems(cx).into()), false, )) .child("click to set as default"), ) .into_any() })) .on_click({ move |_, window, cx| { if window.modifiers().secondary() { update_settings_file( <dyn Fs>::global(cx), cx, |settings, _| { settings.editor.diff_view_style = Some(DiffViewStyle::Split); }, ); } if !is_split { focus_handle.focus(window, cx); window .dispatch_action(ToggleSplitDiff.boxed_clone(), cx); } } }), ) }) } else { None }; let collapse_expand_button = if self.needs_expand_collapse_option(cx) { let query_editor_focus = self.query_editor.focus_handle(cx); let is_collapsed = self .active_searchable_item .as_ref() .and_then(|item| item.act_as_type(TypeId::of::<Editor>(), cx)) .and_then(|item| item.downcast::<Editor>().ok()) .map(|editor: Entity<Editor>| editor.read(cx).has_any_buffer_folded(cx)) .unwrap_or_default(); let (icon, tooltip_label) = if is_collapsed { (IconName::ChevronUpDown, "Expand All Files") } else { (IconName::ChevronDownUp, "Collapse All Files") }; let collapse_expand_icon_button = |id| { IconButton::new(id, icon) .shape(IconButtonShape::Square) .tooltip(move |_, cx| { Tooltip::for_action_in( tooltip_label, &ToggleFoldAll, &query_editor_focus, cx, ) }) .on_click(|_event, window, cx| { window.dispatch_action(ToggleFoldAll.boxed_clone(), cx) }) }; if self.dismissed { return h_flex() .pl_0p5() .gap_1() .child(collapse_expand_icon_button( "multibuffer-collapse-expand-empty", )) .when(has_splittable_editor, |this| this.children(split_buttons)) .into_any_element(); } Some( h_flex() .gap_1() .child(collapse_expand_icon_button("multibuffer-collapse-expand")) .children(split_buttons) .into_any_element(), ) } else { None }; let narrow_mode = self.scroll_handle.bounds().size.width / window.rem_size() < 340. / BASE_REM_SIZE_IN_PX; let workspace::searchable::SearchOptions { case, word, regex, replacement, selection, find_in_results, } = self.supported_options(cx); self.query_editor.update(cx, |query_editor, cx| { if query_editor.placeholder_text(cx).is_none() { query_editor.set_placeholder_text("Search…", window, cx); } }); self.replacement_editor.update(cx, |editor, cx| { editor.set_placeholder_text("Replace with…", window, cx); }); let mut color_override = None; let match_text = self .active_searchable_item .as_ref() .and_then(|searchable_item| { if self.query(cx).is_empty() { return None; } let matches_count = self .searchable_items_with_matches .get(&searchable_item.downgrade()) .map(|(matches, _)| matches.len()) .unwrap_or(0); if let Some(match_ix) = self.active_match_index { Some(format!("{}/{}", match_ix + 1, matches_count)) } else { color_override = Some(Color::Error); // No matches found None } }) .unwrap_or_else(|| "0/0".to_string()); let should_show_replace_input = self.replace_enabled && replacement; let in_replace = self.replacement_editor.focus_handle(cx).is_focused(window); let theme_colors = cx.theme().colors(); let query_border = if self.query_error.is_some() { Color::Error.color(cx) } else { theme_colors.border }; let replacement_border = theme_colors.border; let container_width = window.viewport_size().width; let input_width = SearchInputWidth::calc_width(container_width); let input_base_styles = |border_color| input_base_styles(border_color, |div| div.w(input_width)); let input_style = if find_in_results { filter_search_results_input(query_border, |div| div.w(input_width), cx) } else { input_base_styles(query_border) }; let query_column = input_style .child( div() .flex_1() .min_w(px(0.)) .overflow_hidden() .child(render_text_input(&self.query_editor, color_override, cx)), ) .child( h_flex() .flex_none() .gap_1() .when(case, |div| { div.child(SearchOption::CaseSensitive.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }) .when(word, |div| { div.child(SearchOption::WholeWord.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }) .when(regex, |div| { div.child(SearchOption::Regex.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }), ); let mode_column = h_flex() .gap_1() .min_w_64() .when(replacement, |this| { this.child(render_action_button( "buffer-search-bar-toggle", IconName::Replace, self.replace_enabled.then_some(ActionButtonState::Toggled), "Toggle Replace", &ToggleReplace, focus_handle.clone(), )) }) .when(selection, |this| { this.child( IconButton::new( "buffer-search-bar-toggle-search-selection-button", IconName::Quote, ) .style(ButtonStyle::Subtle) .shape(IconButtonShape::Square) .when(self.selection_search_enabled.is_some(), |button| { button.style(ButtonStyle::Filled) }) .on_click(cx.listener(|this, _: &ClickEvent, window, cx| { this.toggle_selection(&ToggleSelection, window, cx); })) .toggle_state(self.selection_search_enabled.is_some()) .tooltip({ let focus_handle = focus_handle.clone(); move |_window, cx| { Tooltip::for_action_in( "Toggle Search Selection", &ToggleSelection, &focus_handle, cx, ) } }), ) }) .when(!find_in_results, |el| { let query_focus = self.query_editor.focus_handle(cx); let matches_column = h_flex() .pl_2() .ml_2() .border_l_1() .border_color(theme_colors.border_variant) .child(render_action_button( "buffer-search-nav-button", ui::IconName::ChevronLeft, self.active_match_index .is_none() .then_some(ActionButtonState::Disabled), "Select Previous Match", &SelectPreviousMatch, query_focus.clone(), )) .child(render_action_button( "buffer-search-nav-button", ui::IconName::ChevronRight, self.active_match_index .is_none() .then_some(ActionButtonState::Disabled), "Select Next Match", &SelectNextMatch, query_focus.clone(), )) .when(!narrow_mode, |this| { this.child(div().ml_2().min_w(rems_from_px(40.)).child( Label::new(match_text).size(LabelSize::Small).color( if self.active_match_index.is_some() { Color::Default } else { Color::Disabled }, ), )) }); el.child(render_action_button( "buffer-search-nav-button", IconName::SelectAll, Default::default(), "Select All Matches", &SelectAllMatches, query_focus, )) .child(matches_column) }) .when(find_in_results, |el| { el.child(render_action_button( "buffer-search", IconName::Close, Default::default(), "Close Search Bar", &Dismiss, focus_handle.clone(), )) }); let has_collapse_button = collapse_expand_button.is_some(); let search_line = h_flex() .w_full() .gap_2() .when(find_in_results, |el| el.child(alignment_element())) .when(!find_in_results && has_collapse_button, |el| { el.pl_0p5().child(collapse_expand_button.expect("button")) }) .child(query_column) .child(mode_column); let replace_line = should_show_replace_input.then(|| { let replace_column = input_base_styles(replacement_border) .child(render_text_input(&self.replacement_editor, None, cx)); let focus_handle = self.replacement_editor.read(cx).focus_handle(cx); let replace_actions = h_flex() .min_w_64() .gap_1() .child(render_action_button( "buffer-search-replace-button", IconName::ReplaceNext, Default::default(), "Replace Next Match", &ReplaceNext, focus_handle.clone(), )) .child(render_action_button( "buffer-search-replace-button", IconName::ReplaceAll, Default::default(), "Replace All Matches", &ReplaceAll, focus_handle,Lines 1–500 of 3656. Load more
mod registrar; use crate::{ FocusSearch, NextHistoryQuery, PreviousHistoryQuery, ReplaceAll, ReplaceNext, SearchOption, SearchOptions, SearchSource, SelectAllMatches, SelectNextMatch, SelectPreviousMatch, ToggleCaseSensitive, ToggleRegex, ToggleReplace, ToggleSelection, ToggleWholeWord, buffer_search::registrar::WithResultsOrExternalQuery, search_bar::{ ActionButtonState, alignment_element, filter_search_results_input, input_base_styles, render_action_button, render_text_input, }, }; use any_vec::AnyVec; use collections::HashMap; use editor::{ Editor, EditorSettings, MultiBufferOffset, SplittableEditor, ToggleSplitDiff, actions::{Backtab, FoldAll, Tab, ToggleFoldAll, UnfoldAll}, }; 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, div, }; use language::{Language, LanguageRegistry}; use project::{ search::SearchQuery, search_history::{SearchHistory, SearchHistoryCursor}, }; use fs::Fs; use settings::{DiffViewStyle, Settings, update_settings_file}; use std::{any::TypeId, sync::Arc}; use zed_actions::{outline::ToggleOutline, workspace::CopyPath, workspace::CopyRelativePath}; use ui::{ BASE_REM_SIZE_IN_PX, IconButtonShape, PlatformStyle, TextSize, Tooltip, prelude::*, render_modifiers, utils::SearchInputWidth, }; use util::{ResultExt, paths::PathMatcher}; use workspace::{ ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView, Workspace, item::{ItemBufferKind, ItemHandle}, searchable::{ Direction, FilteredSearchRange, SearchEvent, SearchToken, SearchableItemHandle, WeakSearchableItemHandle, }, }; pub use registrar::{DivRegistrar, register_pane_search_actions}; use registrar::{ForDeployed, ForDismissed, SearchActionsRegistrar}; const MAX_BUFFER_SEARCH_HISTORY_SIZE: usize = 50; pub use zed_actions::buffer_search::{Deploy, DeployReplace, Dismiss, FocusEditor}; pub enum Event { UpdateLocation, Dismissed, } pub fn init(cx: &mut App) { cx.observe_new(|workspace: &mut Workspace, _, _| BufferSearchBar::register(workspace)) .detach(); } pub struct BufferSearchBar { query_editor: Entity<Editor>, query_editor_focused: bool, replacement_editor: Entity<Editor>, replacement_editor_focused: bool, active_searchable_item: Option<Box<dyn SearchableItemHandle>>, active_match_index: Option<usize>, #[cfg(target_os = "macos")] active_searchable_item_subscriptions: Option<[Subscription; 2]>, #[cfg(not(target_os = "macos"))] active_searchable_item_subscriptions: Option<Subscription>, #[cfg(target_os = "macos")] pending_external_query: Option<(String, SearchOptions)>, active_search: Option<Arc<SearchQuery>>, searchable_items_with_matches: HashMap<Box<dyn WeakSearchableItemHandle>, (AnyVec<dyn Send>, SearchToken)>, pending_search: Option<Task<()>>, search_options: SearchOptions, default_options: SearchOptions, configured_options: SearchOptions, query_error: Option<String>, dismissed: bool, search_history: SearchHistory, search_history_cursor: SearchHistoryCursor, replace_enabled: bool, selection_search_enabled: Option<FilteredSearchRange>, scroll_handle: ScrollHandle, regex_language: Option<Arc<Language>>, splittable_editor: Option<WeakEntity<SplittableEditor>>, _splittable_editor_subscription: Option<Subscription>, } impl EventEmitter<Event> for BufferSearchBar {} impl EventEmitter<workspace::ToolbarItemEvent> for BufferSearchBar {} impl Render for BufferSearchBar { fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement { let focus_handle = self.focus_handle(cx); let has_splittable_editor = self.splittable_editor.is_some(); let split_buttons = if has_splittable_editor { self.splittable_editor .as_ref() .and_then(|weak| weak.upgrade()) .map(|splittable_editor| { let is_split = splittable_editor.read(cx).is_split(); let focus_handle = splittable_editor.focus_handle(cx); h_flex() .gap_1() .child( IconButton::new("diff-unified", IconName::DiffUnified) .shape(IconButtonShape::Square) .toggle_state(!is_split) .tooltip(Tooltip::element(move |_, cx| { v_flex() .gap_1() .child(Label::new("Unified")) .child( h_flex() .gap_0p5() .text_sm() .text_color(Color::Muted.color(cx)) .children(render_modifiers( &gpui::Modifiers::secondary_key(), PlatformStyle::platform(), None, Some(TextSize::Default.rems(cx).into()), false, )) .child("click to set as default"), ) .into_any() })) .on_click({ let focus_handle = focus_handle.clone(); move |_, window, cx| { if window.modifiers().secondary() { update_settings_file( <dyn Fs>::global(cx), cx, |settings, _| { settings.editor.diff_view_style = Some(DiffViewStyle::Unified); }, ); } if is_split { focus_handle.focus(window, cx); window .dispatch_action(ToggleSplitDiff.boxed_clone(), cx); } } }), ) .child( IconButton::new("diff-split", IconName::DiffSplit) .shape(IconButtonShape::Square) .toggle_state(is_split) .tooltip(Tooltip::element(move |_, cx| { v_flex() .gap_1() .child(Label::new("Split")) .child( h_flex() .gap_0p5() .text_sm() .text_color(Color::Muted.color(cx)) .children(render_modifiers( &gpui::Modifiers::secondary_key(), PlatformStyle::platform(), None, Some(TextSize::Default.rems(cx).into()), false, )) .child("click to set as default"), ) .into_any() })) .on_click({ move |_, window, cx| { if window.modifiers().secondary() { update_settings_file( <dyn Fs>::global(cx), cx, |settings, _| { settings.editor.diff_view_style = Some(DiffViewStyle::Split); }, ); } if !is_split { focus_handle.focus(window, cx); window .dispatch_action(ToggleSplitDiff.boxed_clone(), cx); } } }), ) }) } else { None }; let collapse_expand_button = if self.needs_expand_collapse_option(cx) { let query_editor_focus = self.query_editor.focus_handle(cx); let is_collapsed = self .active_searchable_item .as_ref() .and_then(|item| item.act_as_type(TypeId::of::<Editor>(), cx)) .and_then(|item| item.downcast::<Editor>().ok()) .map(|editor: Entity<Editor>| editor.read(cx).has_any_buffer_folded(cx)) .unwrap_or_default(); let (icon, tooltip_label) = if is_collapsed { (IconName::ChevronUpDown, "Expand All Files") } else { (IconName::ChevronDownUp, "Collapse All Files") }; let collapse_expand_icon_button = |id| { IconButton::new(id, icon) .shape(IconButtonShape::Square) .tooltip(move |_, cx| { Tooltip::for_action_in( tooltip_label, &ToggleFoldAll, &query_editor_focus, cx, ) }) .on_click(|_event, window, cx| { window.dispatch_action(ToggleFoldAll.boxed_clone(), cx) }) }; if self.dismissed { return h_flex() .pl_0p5() .gap_1() .child(collapse_expand_icon_button( "multibuffer-collapse-expand-empty", )) .when(has_splittable_editor, |this| this.children(split_buttons)) .into_any_element(); } Some( h_flex() .gap_1() .child(collapse_expand_icon_button("multibuffer-collapse-expand")) .children(split_buttons) .into_any_element(), ) } else { None }; let narrow_mode = self.scroll_handle.bounds().size.width / window.rem_size() < 340. / BASE_REM_SIZE_IN_PX; let workspace::searchable::SearchOptions { case, word, regex, replacement, selection, find_in_results, } = self.supported_options(cx); self.query_editor.update(cx, |query_editor, cx| { if query_editor.placeholder_text(cx).is_none() { query_editor.set_placeholder_text("Search…", window, cx); } }); self.replacement_editor.update(cx, |editor, cx| { editor.set_placeholder_text("Replace with…", window, cx); }); let mut color_override = None; let match_text = self .active_searchable_item .as_ref() .and_then(|searchable_item| { if self.query(cx).is_empty() { return None; } let matches_count = self .searchable_items_with_matches .get(&searchable_item.downgrade()) .map(|(matches, _)| matches.len()) .unwrap_or(0); if let Some(match_ix) = self.active_match_index { Some(format!("{}/{}", match_ix + 1, matches_count)) } else { color_override = Some(Color::Error); // No matches found None } }) .unwrap_or_else(|| "0/0".to_string()); let should_show_replace_input = self.replace_enabled && replacement; let in_replace = self.replacement_editor.focus_handle(cx).is_focused(window); let theme_colors = cx.theme().colors(); let query_border = if self.query_error.is_some() { Color::Error.color(cx) } else { theme_colors.border }; let replacement_border = theme_colors.border; let container_width = window.viewport_size().width; let input_width = SearchInputWidth::calc_width(container_width); let input_base_styles = |border_color| input_base_styles(border_color, |div| div.w(input_width)); let input_style = if find_in_results { filter_search_results_input(query_border, |div| div.w(input_width), cx) } else { input_base_styles(query_border) }; let query_column = input_style .child( div() .flex_1() .min_w(px(0.)) .overflow_hidden() .child(render_text_input(&self.query_editor, color_override, cx)), ) .child( h_flex() .flex_none() .gap_1() .when(case, |div| { div.child(SearchOption::CaseSensitive.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }) .when(word, |div| { div.child(SearchOption::WholeWord.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }) .when(regex, |div| { div.child(SearchOption::Regex.as_button( self.search_options, SearchSource::Buffer, focus_handle.clone(), )) }), ); let mode_column = h_flex() .gap_1() .min_w_64() .when(replacement, |this| { this.child(render_action_button( "buffer-search-bar-toggle", IconName::Replace, self.replace_enabled.then_some(ActionButtonState::Toggled), "Toggle Replace", &ToggleReplace, focus_handle.clone(), )) }) .when(selection, |this| { this.child( IconButton::new( "buffer-search-bar-toggle-search-selection-button", IconName::Quote, ) .style(ButtonStyle::Subtle) .shape(IconButtonShape::Square) .when(self.selection_search_enabled.is_some(), |button| { button.style(ButtonStyle::Filled) }) .on_click(cx.listener(|this, _: &ClickEvent, window, cx| { this.toggle_selection(&ToggleSelection, window, cx); })) .toggle_state(self.selection_search_enabled.is_some()) .tooltip({ let focus_handle = focus_handle.clone(); move |_window, cx| { Tooltip::for_action_in( "Toggle Search Selection", &ToggleSelection, &focus_handle, cx, ) } }), ) }) .when(!find_in_results, |el| { let query_focus = self.query_editor.focus_handle(cx); let matches_column = h_flex() .pl_2() .ml_2() .border_l_1() .border_color(theme_colors.border_variant) .child(render_action_button( "buffer-search-nav-button", ui::IconName::ChevronLeft, self.active_match_index .is_none() .then_some(ActionButtonState::Disabled), "Select Previous Match", &SelectPreviousMatch, query_focus.clone(), )) .child(render_action_button( "buffer-search-nav-button", ui::IconName::ChevronRight, self.active_match_index .is_none() .then_some(ActionButtonState::Disabled), "Select Next Match", &SelectNextMatch, query_focus.clone(), )) .when(!narrow_mode, |this| { this.child(div().ml_2().min_w(rems_from_px(40.)).child( Label::new(match_text).size(LabelSize::Small).color( if self.active_match_index.is_some() { Color::Default } else { Color::Disabled }, ), )) }); el.child(render_action_button( "buffer-search-nav-button", IconName::SelectAll, Default::default(), "Select All Matches", &SelectAllMatches, query_focus, )) .child(matches_column) }) .when(find_in_results, |el| { el.child(render_action_button( "buffer-search", IconName::Close, Default::default(), "Close Search Bar", &Dismiss, focus_handle.clone(), )) }); let has_collapse_button = collapse_expand_button.is_some(); let search_line = h_flex() .w_full() .gap_2() .when(find_in_results, |el| el.child(alignment_element())) .when(!find_in_results && has_collapse_button, |el| { el.pl_0p5().child(collapse_expand_button.expect("button")) }) .child(query_column) .child(mode_column); let replace_line = should_show_replace_input.then(|| { let replace_column = input_base_styles(replacement_border) .child(render_text_input(&self.replacement_editor, None, cx)); let focus_handle = self.replacement_editor.read(cx).focus_handle(cx); let replace_actions = h_flex() .min_w_64() .gap_1() .child(render_action_button( "buffer-search-replace-button", IconName::ReplaceNext, Default::default(), "Replace Next Match", &ReplaceNext, focus_handle.clone(), )) .child(render_action_button( "buffer-search-replace-button", IconName::ReplaceAll, Default::default(), "Replace All Matches", &ReplaceAll, focus_handle,