From 46fc6938a6da938e381707a8c62fe5d0eb2a3d86 Mon Sep 17 00:00:00 2001 From: Sean Hagstrom Date: Mon, 6 Apr 2026 19:47:02 -0700 Subject: [PATCH] vim: Add editor setting for changing regex mode default in vim searches (#53092) Closes #48007 Release Notes: - Added editor setting for changing regex mode default in vim searches Summary: - Based on the report in #48007 and the discussion here https://github.com/zed-industries/zed/pull/48127#issuecomment-3838678903 - There was feedback mentioning that vim-mode needs to default vim-searches to use regex-mode (even when the editor regex-search setting is disabled). However, it was suggested that a vim search setting could be configured to adjust this behaviour. - In this PR a new vim setting was added to change whether vim-searches will use regex-mode by default, so now users can can configure vim-search to not use regex-mode when typing the `/` character (or using the vim search command). Screen Captures: https://github.com/user-attachments/assets/172669fb-ab78-41a1-9485-c973825543c5 --- assets/settings/default.json | 1 + .../settings_content/src/settings_content.rs | 1 + crates/settings_ui/src/page_data.rs | 20 +++++- crates/vim/src/normal/search.rs | 64 ++++++++++++++++++- crates/vim/src/vim.rs | 2 + docs/src/vim.md | 2 + 6 files changed, 88 insertions(+), 2 deletions(-) diff --git a/assets/settings/default.json b/assets/settings/default.json index 5e1eb0e68d2f8a17f89422597aa29b99516333e8..63e906e3b11206fc458f8d7353f3ecba0abeb825 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -2417,6 +2417,7 @@ "toggle_relative_line_numbers": false, "use_system_clipboard": "always", "use_smartcase_find": false, + "use_regex_search": true, "gdefault": false, "highlight_on_yank_duration": 200, "custom_digraphs": {}, diff --git a/crates/settings_content/src/settings_content.rs b/crates/settings_content/src/settings_content.rs index 325e86e9e3af0fb888c2691f4be1b0fdeb06dfb4..6c60a7010f7cfc5b4fadf9a8cc386fe6e3267abc 100644 --- a/crates/settings_content/src/settings_content.rs +++ b/crates/settings_content/src/settings_content.rs @@ -763,6 +763,7 @@ pub struct VimSettingsContent { pub toggle_relative_line_numbers: Option, pub use_system_clipboard: Option, pub use_smartcase_find: Option, + pub use_regex_search: Option, /// When enabled, the `:substitute` command replaces all matches in a line /// by default. The 'g' flag then toggles this behavior., pub gdefault: Option, diff --git a/crates/settings_ui/src/page_data.rs b/crates/settings_ui/src/page_data.rs index 20a0c53534988a873b3b3f6e393eefd5bb0b3f7c..9978832c05bb29c97f118fccbe301214d81fa0c6 100644 --- a/crates/settings_ui/src/page_data.rs +++ b/crates/settings_ui/src/page_data.rs @@ -2447,7 +2447,7 @@ fn editor_page() -> SettingsPage { ] } - fn vim_settings_section() -> [SettingsPageItem; 12] { + fn vim_settings_section() -> [SettingsPageItem; 13] { [ SettingsPageItem::SectionHeader("Vim"), SettingsPageItem::SettingItem(SettingItem { @@ -2556,6 +2556,24 @@ fn editor_page() -> SettingsPage { metadata: None, files: USER, }), + SettingsPageItem::SettingItem(SettingItem { + title: "Regex Search", + description: "Use regex search by default in Vim search.", + field: Box::new(SettingField { + json_path: Some("vim.use_regex_search"), + pick: |settings_content| { + settings_content.vim.as_ref()?.use_regex_search.as_ref() + }, + write: |settings_content, value| { + settings_content + .vim + .get_or_insert_default() + .use_regex_search = value; + }, + }), + metadata: None, + files: USER, + }), SettingsPageItem::SettingItem(SettingItem { title: "Cursor Shape - Normal Mode", description: "Cursor shape for normal mode.", diff --git a/crates/vim/src/normal/search.rs b/crates/vim/src/normal/search.rs index 6a8394f44710b7e241b7ba38f4913899a5afbce6..22c453c877ec89fdbf432d19d89167285b78b12f 100644 --- a/crates/vim/src/normal/search.rs +++ b/crates/vim/src/normal/search.rs @@ -245,7 +245,7 @@ impl Vim { search_bar.set_replacement(None, cx); let mut options = SearchOptions::NONE; - if action.regex { + if action.regex && VimSettings::get_global(cx).use_regex_search { options |= SearchOptions::REGEX; } if action.backwards { @@ -1446,4 +1446,66 @@ mod test { // The cursor should be at the match location on line 3 (row 2). cx.assert_state("hello world\nfoo bar\nhello ˇagain\n", Mode::Normal); } + + #[gpui::test] + async fn test_vim_search_respects_search_settings(cx: &mut gpui::TestAppContext) { + let mut cx = VimTestContext::new(cx, true).await; + + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.vim.get_or_insert_default().use_regex_search = Some(false); + }); + }); + + cx.set_state("ˇcontent", Mode::Normal); + cx.simulate_keystrokes("/"); + cx.run_until_parked(); + + // Verify search options are set from settings + let search_bar = cx.workspace(|workspace, _, cx| { + workspace + .active_pane() + .read(cx) + .toolbar() + .read(cx) + .item_of_type::() + .expect("Buffer search bar should be active") + }); + + cx.update_entity(search_bar, |bar, _window, _cx| { + assert!( + !bar.has_search_option(search::SearchOptions::REGEX), + "Vim search open without regex mode" + ); + }); + + cx.simulate_keystrokes("escape"); + cx.run_until_parked(); + + cx.update_global(|store: &mut SettingsStore, cx| { + store.update_user_settings(cx, |settings| { + settings.vim.get_or_insert_default().use_regex_search = Some(true); + }); + }); + + cx.simulate_keystrokes("/"); + cx.run_until_parked(); + + let search_bar = cx.workspace(|workspace, _, cx| { + workspace + .active_pane() + .read(cx) + .toolbar() + .read(cx) + .item_of_type::() + .expect("Buffer search bar should be active") + }); + + cx.update_entity(search_bar, |bar, _window, _cx| { + assert!( + bar.has_search_option(search::SearchOptions::REGEX), + "Vim search opens with regex mode" + ); + }); + } } diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index 6e1849340f17b776a34546dd9a118dc55e8dab84..a66111cae1576744c4c51d717984d67c12fc8235 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -2141,6 +2141,7 @@ struct VimSettings { pub toggle_relative_line_numbers: bool, pub use_system_clipboard: settings::UseSystemClipboard, pub use_smartcase_find: bool, + pub use_regex_search: bool, pub gdefault: bool, pub custom_digraphs: HashMap>, pub highlight_on_yank_duration: u64, @@ -2227,6 +2228,7 @@ impl Settings for VimSettings { toggle_relative_line_numbers: vim.toggle_relative_line_numbers.unwrap(), use_system_clipboard: vim.use_system_clipboard.unwrap(), use_smartcase_find: vim.use_smartcase_find.unwrap(), + use_regex_search: vim.use_regex_search.unwrap(), gdefault: vim.gdefault.unwrap(), custom_digraphs: vim.custom_digraphs.unwrap(), highlight_on_yank_duration: vim.highlight_on_yank_duration.unwrap(), diff --git a/docs/src/vim.md b/docs/src/vim.md index 1798f16a93244f2694b30ffa70119da1e4498fdc..8e93edff081681a3e094c811e2d76822766ef67e 100644 --- a/docs/src/vim.md +++ b/docs/src/vim.md @@ -562,6 +562,7 @@ You can change the following settings to modify vim mode's behavior: | use_system_clipboard | Determines how system clipboard is used:
  • "always": use for all operations
  • "never": only use when explicitly specified
  • "on_yank": use for yank operations
| "always" | | use_multiline_find | deprecated | | use_smartcase_find | If `true`, `f` and `t` motions are case-insensitive when the target letter is lowercase. | false | +| use_regex_search | If `true`, then vim search will use regex mode | true | | gdefault | If `true`, the `:substitute` command replaces all matches in a line by default (as if `g` flag was given). The `g` flag then toggles this, replacing only the first match. | false | | toggle_relative_line_numbers | If `true`, line numbers are relative in normal mode and absolute in insert mode, giving you the best of both options. | false | | custom_digraphs | An object that allows you to add custom digraphs. Read below for an example. | {} | @@ -587,6 +588,7 @@ Here's an example of these settings changed: "default_mode": "insert", "use_system_clipboard": "never", "use_smartcase_find": true, + "use_regex_search": true, "gdefault": true, "toggle_relative_line_numbers": true, "highlight_on_yank_duration": 50,