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,
0x2CA and Conrad Irwin created
Closes #16514
Release Notes:
- Added Vim: absolute numbering in any mode except `insert` mode
---------
Co-authored-by: Conrad Irwin <conrad.irwin@gmail.com>
assets/settings/default.json | 1
crates/editor/src/actions.rs | 1
crates/editor/src/editor.rs | 25 +++++++++++++++++++
crates/editor/src/element.rs | 3 +
crates/vim/src/state.rs | 10 ++++++
crates/vim/src/vim.rs | 49 ++++++++++++++++++++++++++++++++++++-
docs/src/vim.md | 13 ++++++++-
7 files changed, 96 insertions(+), 6 deletions(-)
@@ -946,6 +946,7 @@
},
// Vim settings
"vim": {
+ "toggle_relative_line_numbers": false,
"use_system_clipboard": "always",
"use_multiline_find": false,
"use_smartcase_find": false,
@@ -318,6 +318,7 @@ gpui::actions!(
ToggleHunkDiff,
ToggleInlayHints,
ToggleLineNumbers,
+ ToggleRelativeLineNumbers,
ToggleIndentGuides,
ToggleSoftWrap,
ToggleTabBar,
@@ -512,6 +512,7 @@ pub struct Editor {
show_breadcrumbs: bool,
show_gutter: bool,
show_line_numbers: Option<bool>,
+ use_relative_line_numbers: Option<bool>,
show_git_diff_gutter: Option<bool>,
show_code_actions: Option<bool>,
show_runnables: Option<bool>,
@@ -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<Self>,
+ ) {
+ 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<bool>,
+ cx: &mut ViewContext<Self>,
+ ) {
+ self.use_relative_line_numbers = is_relative;
+ cx.notify();
+ }
+
pub fn set_show_gutter(&mut self, show_gutter: bool, cx: &mut ViewContext<Self>) {
self.show_gutter = show_gutter;
cx.notify();
@@ -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 {
@@ -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<SharedString>,
pub registers: HashMap<char, Register>,
pub recordings: HashMap<char, Vec<ReplayableAction>>,
+
+ pub focused_vim: Option<WeakView<Vim>>,
}
impl Global for VimGlobals {}
@@ -373,6 +377,10 @@ impl VimGlobals {
);
}
}
+
+ pub fn focused_vim(&self) -> Option<View<Vim>> {
+ self.focused_vim.as_ref().and_then(|vim| vim.upgrade())
+ }
}
impl Vim {
@@ -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::<SettingsStore>(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::<VimAddon>()
+ .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::<VimAddon>();
+ 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<Self>) {
@@ -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<bool>,
pub use_system_clipboard: Option<UseSystemClipboard>,
pub use_multiline_find: Option<bool>,
pub use_smartcase_find: Option<bool>,
@@ -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": "🧟♀️"
+ }
}
}
```