search: Fix toggle soft wrap not working while the search bar is focused (#61150)
Closes #34698 The buffer search query input is itself an editor, so dispatching `editor::ToggleSoftWrap` from the command palette while the search bar had focus toggled soft wrap on the single-line query editor (with no visible effect) and never reached the searched editor. This intercepts the action on the search bar in the capture phase and applies it to the active searchable item, the same way `ToggleFoldAll` is already relayed. Focus stays in the search bar, so you can keep typing your query. `Editor::toggle_soft_wrap` and `Editor::soft_wrap_mode` become `pub` (matching `fold_all`/`has_any_buffer_folded`, which the existing `ToggleFoldAll` relay already uses). Includes a regression test that fails without the fix: it deploys the search bar, dispatches `ToggleSoftWrap` with the query editor focused, and asserts the searched editor's wrap mode changes while focus remains in the search bar. Release Notes: - Fixed `editor: toggle soft wrap` doing nothing when invoked while the buffer search bar was focused --------- Co-authored-by: Christopher Biscardi <chris@christopherbiscardi.com>
This commit is contained in:
1 parent
34bfb3842d
commit
c5e0fb8ffd
3 files changed
+87
-10
No files matched your search
@@ -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<Self>,
|
||||
) {
|
||||
pub fn toggle_soft_wrap(&mut self, _: &ToggleSoftWrap, _: &mut Window, cx: &mut Context<Self>) {
|
||||
if self.soft_wrap_mode_override.is_some() {
|
||||
self.soft_wrap_mode_override.take();
|
||||
} else {
|
||||
|
||||
@@ -1158,7 +1158,7 @@ impl SplittableEditor {
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_soft_wrap(
|
||||
pub fn toggle_soft_wrap(
|
||||
&mut self,
|
||||
_: &ToggleSoftWrap,
|
||||
window: &mut Window,
|
||||
|
||||
@@ -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<Self>,
|
||||
) {
|
||||
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::<SplittableEditor>(), cx) {
|
||||
let split_editor = split_editor
|
||||
.downcast::<SplittableEditor>()
|
||||
.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::<Editor>(), cx) {
|
||||
let editor = item.downcast::<Editor>().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<Self>) {
|
||||
if let Some(item) = &self.active_searchable_item {
|
||||
if let Some(item) = item.act_as_type(TypeId::of::<Editor>(), 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) {
|
||||
|
||||
Reference in new issue
Block a user