diff --git a/crates/editor/src/config.rs b/crates/editor/src/config.rs index f1a717db7c..6676a790a0 100644 --- a/crates/editor/src/config.rs +++ b/crates/editor/src/config.rs @@ -257,7 +257,7 @@ impl Editor { wrap_guides } - pub(super) fn soft_wrap_mode(&self, cx: &App) -> SoftWrap { + pub fn soft_wrap_mode(&self, cx: &App) -> SoftWrap { let settings = self.buffer.read(cx).language_settings(cx); let mode = self.soft_wrap_mode_override.unwrap_or(settings.soft_wrap); match mode { @@ -286,12 +286,7 @@ impl Editor { } } - pub(super) fn toggle_soft_wrap( - &mut self, - _: &ToggleSoftWrap, - _: &mut Window, - cx: &mut Context, - ) { + pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context) { if self.soft_wrap_mode_override.is_some() { self.soft_wrap_mode_override.take(); } else { diff --git a/crates/editor/src/split.rs b/crates/editor/src/split.rs index 93a6f96a44..96ed0a23b0 100644 --- a/crates/editor/src/split.rs +++ b/crates/editor/src/split.rs @@ -1158,7 +1158,7 @@ impl SplittableEditor { } } - fn toggle_soft_wrap( + pub fn toggle_soft_wrap( &mut self, _: &ToggleSoftWrap, window: &mut Window, diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index f0fa4acafd..b4818a1160 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -15,7 +15,7 @@ use any_vec::AnyVec; use collections::HashMap; use editor::{ DiffStyleControls, Editor, EditorSettings, MultiBufferOffset, SplittableEditor, - actions::{Backtab, FoldAll, Tab, ToggleFoldAll, UnfoldAll}, + actions::{Backtab, FoldAll, Tab, ToggleFoldAll, ToggleSoftWrap, UnfoldAll}, scroll::Autoscroll, }; use futures::channel::oneshot; @@ -462,6 +462,7 @@ impl Render for BufferSearchBar { .capture_action(cx.listener(Self::tab)) .capture_action(cx.listener(Self::backtab)) .capture_action(cx.listener(Self::toggle_fold_all)) + .capture_action(cx.listener(Self::toggle_soft_wrap)) .on_action(cx.listener(Self::previous_history_query)) .on_action(cx.listener(Self::next_history_query)) .on_action(cx.listener(Self::dismiss)) @@ -995,6 +996,38 @@ impl BufferSearchBar { self.toggle_fold_all_in_item(window, cx); } + // The query editor is an editor itself and would otherwise swallow this + // action without any visible effect, so relay it to the searched editor + // while keeping the focus in the search bar. + fn toggle_soft_wrap( + &mut self, + action: &ToggleSoftWrap, + window: &mut Window, + cx: &mut Context, + ) { + let Some(item) = &self.active_searchable_item else { + return; + }; + // A split diff editor picks which side to toggle and keeps both sides + // in sync, while its `act_as_type(Editor)` only exposes the RHS + // editor, so relay to the split editor itself when it is split. + if let Some(split_editor) = item.act_as_type(TypeId::of::(), cx) { + let split_editor = split_editor + .downcast::() + .expect("Is a splittable editor"); + if split_editor.read(cx).is_split() { + split_editor.update(cx, |split_editor, cx| { + split_editor.toggle_soft_wrap(action, window, cx) + }); + return; + } + } + if let Some(item) = item.act_as_type(TypeId::of::(), cx) { + let editor = item.downcast::().expect("Is an editor"); + editor.update(cx, |editor, cx| editor.toggle_soft_wrap(action, window, cx)); + } + } + fn toggle_fold_all_in_item(&self, window: &mut Window, cx: &mut Context) { if let Some(item) = &self.active_searchable_item { if let Some(item) = item.act_as_type(TypeId::of::(), cx) { @@ -1846,7 +1879,7 @@ mod tests { use super::*; use editor::{ DisplayPoint, Editor, HighlightKey, MultiBuffer, PathKey, - SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT, SearchSettings, SelectionEffects, + SELECTION_HIGHLIGHT_DEBOUNCE_TIMEOUT, SearchSettings, SelectionEffects, SoftWrap, display_map::DisplayRow, test::editor_test_context::EditorTestContext, }; use futures::stream::StreamExt as _; @@ -3188,6 +3221,55 @@ mod tests { }); } + #[gpui::test] + async fn test_toggle_soft_wrap_relays_to_searched_editor(cx: &mut TestAppContext) { + init_globals(cx); + let (editor, search_bar, cx) = init_test(cx); + + search_bar.update_in(cx, |search_bar, window, cx| { + search_bar.deploy( + &Deploy { + focus: true, + replace_enabled: false, + selection_search_enabled: false, + }, + None, + window, + cx, + ); + }); + cx.run_until_parked(); + + search_bar.update_in(cx, |search_bar, window, cx| { + assert!( + search_bar.query_editor.focus_handle(cx).is_focused(window), + "query editor should be focused after deploying the search bar", + ); + }); + editor.update_in(cx, |editor, _, cx| { + assert!( + matches!(editor.soft_wrap_mode(cx), SoftWrap::None), + "soft wrap should be disabled initially", + ); + }); + + cx.dispatch_action(ToggleSoftWrap); + cx.run_until_parked(); + + editor.update_in(cx, |editor, _, cx| { + assert!( + matches!(editor.soft_wrap_mode(cx), SoftWrap::EditorWidth), + "toggling soft wrap while the search bar is focused should affect the searched editor", + ); + }); + search_bar.update_in(cx, |search_bar, window, cx| { + assert!( + search_bar.query_editor.focus_handle(cx).is_focused(window), + "focus should stay in the search bar", + ); + }); + } + #[cfg(target_os = "macos")] #[gpui::test] async fn test_cmd_e_then_cmd_g_uses_selection_for_find(cx: &mut TestAppContext) {