diff --git a/assets/settings/default.json b/assets/settings/default.json index 5df2c32f57..5c89d1e96a 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -946,6 +946,7 @@ }, // Vim settings "vim": { + "toggle_relative_line_numbers": false, "use_system_clipboard": "always", "use_multiline_find": false, "use_smartcase_find": false, diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index e0ae75b636..812b733054 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -318,6 +318,7 @@ gpui::actions!( ToggleHunkDiff, ToggleInlayHints, ToggleLineNumbers, + ToggleRelativeLineNumbers, ToggleIndentGuides, ToggleSoftWrap, ToggleTabBar, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index a458660185..f21b551a54 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -512,6 +512,7 @@ pub struct Editor { show_breadcrumbs: bool, show_gutter: bool, show_line_numbers: Option, + use_relative_line_numbers: Option, show_git_diff_gutter: Option, show_code_actions: Option, show_runnables: Option, @@ -1853,6 +1854,7 @@ impl Editor { show_breadcrumbs: EditorSettings::get_global(cx).toolbar.breadcrumbs, show_gutter: mode == EditorMode::Full, show_line_numbers: None, + use_relative_line_numbers: None, show_git_diff_gutter: None, show_code_actions: None, show_runnables: None, @@ -10610,6 +10612,29 @@ impl Editor { EditorSettings::override_global(editor_settings, cx); } + pub fn should_use_relative_line_numbers(&self, cx: &WindowContext) -> bool { + self.use_relative_line_numbers + .unwrap_or(EditorSettings::get_global(cx).relative_line_numbers) + } + + pub fn toggle_relative_line_numbers( + &mut self, + _: &ToggleRelativeLineNumbers, + cx: &mut ViewContext, + ) { + let is_relative = self.should_use_relative_line_numbers(cx); + self.set_relative_line_number(Some(!is_relative), cx) + } + + pub fn set_relative_line_number( + &mut self, + is_relative: Option, + cx: &mut ViewContext, + ) { + self.use_relative_line_numbers = is_relative; + cx.notify(); + } + pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext) { self.show_gutter = show_gutter; cx.notify(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 0126f3491d..8ff81939a1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -344,6 +344,7 @@ impl EditorElement { register_action(view, cx, Editor::toggle_soft_wrap); register_action(view, cx, Editor::toggle_tab_bar); register_action(view, cx, Editor::toggle_line_numbers); + register_action(view, cx, Editor::toggle_relative_line_numbers); register_action(view, cx, Editor::toggle_indent_guides); register_action(view, cx, Editor::toggle_inlay_hints); register_action(view, cx, hover_popover::hover); @@ -1770,7 +1771,7 @@ impl EditorElement { }); let font_size = self.style.text.font_size.to_pixels(cx.rem_size()); - let is_relative = EditorSettings::get_global(cx).relative_line_numbers; + let is_relative = editor.should_use_relative_line_numbers(cx); let relative_to = if is_relative { Some(newest_selection_head.row()) } else { diff --git a/crates/vim/src/state.rs b/crates/vim/src/state.rs index db8cffa2bb..1255c783f4 100644 --- a/crates/vim/src/state.rs +++ b/crates/vim/src/state.rs @@ -9,7 +9,9 @@ use crate::{UseSystemClipboard, Vim, VimSettings}; use collections::HashMap; use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor}; use editor::{Anchor, ClipboardSelection, Editor}; -use gpui::{Action, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, Global}; +use gpui::{ + Action, AppContext, BorrowAppContext, ClipboardEntry, ClipboardItem, Global, View, WeakView, +}; use language::Point; use serde::{Deserialize, Serialize}; use settings::{Settings, SettingsStore}; @@ -160,6 +162,8 @@ pub struct VimGlobals { pub last_yank: Option, pub registers: HashMap, pub recordings: HashMap>, + + pub focused_vim: Option>, } impl Global for VimGlobals {} @@ -373,6 +377,10 @@ impl VimGlobals { ); } } + + pub fn focused_vim(&self) -> Option> { + self.focused_vim.as_ref().and_then(|vim| vim.upgrade()) + } } impl Vim { diff --git a/crates/vim/src/vim.rs b/crates/vim/src/vim.rs index fe3236a52c..ac8257b058 100644 --- a/crates/vim/src/vim.rs +++ b/crates/vim/src/vim.rs @@ -23,8 +23,8 @@ use editor::{ Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint, }; use gpui::{ - actions, impl_actions, Action, AppContext, EventEmitter, KeyContext, KeystrokeEvent, Render, - View, ViewContext, WeakView, + actions, impl_actions, Action, AppContext, Entity, EventEmitter, KeyContext, KeystrokeEvent, + Render, View, ViewContext, WeakView, }; use insert::NormalBefore; use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId}; @@ -228,8 +228,21 @@ impl Vim { } let mut was_enabled = Vim::enabled(cx); + let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; cx.observe_global::(move |editor, cx| { let enabled = Vim::enabled(cx); + let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; + if enabled && was_enabled && (toggle != was_toggle) { + if toggle { + let is_relative = editor + .addon::() + .map(|vim| vim.view.read(cx).mode != Mode::Insert); + editor.set_relative_line_number(is_relative, cx) + } else { + editor.set_relative_line_number(None, cx) + } + } + was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers; if was_enabled == enabled { return; } @@ -296,6 +309,7 @@ impl Vim { editor.set_autoindent(true); editor.selections.line_mode = false; editor.unregister_addon::(); + editor.set_relative_line_number(None, cx) } /// Register an action on the editor. @@ -424,6 +438,17 @@ impl Vim { // Sync editor settings like clip mode self.sync_vim_settings(cx); + if VimSettings::get_global(cx).toggle_relative_line_numbers { + if self.mode != self.last_mode { + if self.mode == Mode::Insert || self.last_mode == Mode::Insert { + self.update_editor(cx, |vim, editor, cx| { + let is_relative = vim.mode != Mode::Insert; + editor.set_relative_line_number(Some(is_relative), cx) + }); + } + } + } + if leave_selections { return; } @@ -616,6 +641,24 @@ impl Vim { cx.emit(VimEvent::Focused); self.sync_vim_settings(cx); + + if VimSettings::get_global(cx).toggle_relative_line_numbers { + if let Some(old_vim) = Vim::globals(cx).focused_vim() { + if old_vim.entity_id() != cx.view().entity_id() { + old_vim.update(cx, |vim, cx| { + vim.update_editor(cx, |_, editor, cx| { + editor.set_relative_line_number(None, cx) + }); + }); + + self.update_editor(cx, |vim, editor, cx| { + let is_relative = vim.mode != Mode::Insert; + editor.set_relative_line_number(Some(is_relative), cx) + }); + } + } + } + Vim::globals(cx).focused_vim = Some(cx.view().downgrade()); } fn blurred(&mut self, cx: &mut ViewContext) { @@ -1039,6 +1082,7 @@ pub enum UseSystemClipboard { #[derive(Deserialize)] struct VimSettings { + pub toggle_relative_line_numbers: bool, pub use_system_clipboard: UseSystemClipboard, pub use_multiline_find: bool, pub use_smartcase_find: bool, @@ -1047,6 +1091,7 @@ struct VimSettings { #[derive(Clone, Default, Serialize, Deserialize, JsonSchema)] struct VimSettingsContent { + pub toggle_relative_line_numbers: Option, pub use_system_clipboard: Option, pub use_multiline_find: Option, pub use_smartcase_find: Option, diff --git a/docs/src/vim.md b/docs/src/vim.md index b1b1af82ba..e7098959e8 100644 --- a/docs/src/vim.md +++ b/docs/src/vim.md @@ -241,8 +241,17 @@ Some vim settings are available to modify the default vim behavior: // "never": don't use system clipboard unless "+ or "* is specified // "on_yank": use system clipboard for yank operations when no register is specified "use_system_clipboard": "always", - // Lets `f` and `t` motions extend across multiple lines - "use_multiline_find": true + // Let `f` and `t` motions extend across multiple lines + "use_multiline_find": true, + // Let `f` and `t` motions match case insensitively if the target is lowercase + "use_smartcase_find": true, + // Use relative line numbers in normal mode, absolute in insert mode + // c.f. https://github.com/jeffkreeftmeijer/vim-numbertoggle + "toggle_relative_line_numbers": true, + // Add custom digraphs (e.g. ctrl-k f z will insert a zombie emoji) + "custom_digraphs": { + "fz": "🧟‍♀️" + } } } ```