vim.rs

   1//! Vim support for Zed.
   2
   3#[cfg(test)]
   4mod test;
   5
   6mod change_list;
   7mod command;
   8mod digraph;
   9mod helix;
  10mod indent;
  11mod insert;
  12mod mode_indicator;
  13mod motion;
  14mod normal;
  15mod object;
  16mod replace;
  17mod rewrap;
  18mod state;
  19mod surrounds;
  20mod visual;
  21
  22use anyhow::Result;
  23use collections::HashMap;
  24use editor::{
  25    movement::{self, FindRange},
  26    scroll::Autoscroll,
  27    Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
  28};
  29use gpui::{
  30    actions, impl_actions, Action, App, AppContext as _, Axis, Context, Entity, EventEmitter,
  31    KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window,
  32};
  33use insert::{NormalBefore, TemporaryNormal};
  34use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
  35pub use mode_indicator::ModeIndicator;
  36use motion::Motion;
  37use normal::search::SearchSubmit;
  38use schemars::JsonSchema;
  39use serde::Deserialize;
  40use serde_derive::Serialize;
  41use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
  42use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
  43use std::{mem, ops::Range, sync::Arc};
  44use surrounds::SurroundsType;
  45use theme::ThemeSettings;
  46use ui::{px, IntoElement, SharedString};
  47use vim_mode_setting::VimModeSetting;
  48use workspace::{self, Pane, ResizeIntent, Workspace};
  49
  50use crate::state::ReplayableAction;
  51
  52/// Used to resize the current pane
  53#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
  54pub struct ResizePane(pub ResizeIntent);
  55
  56/// An Action to Switch between modes
  57#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
  58pub struct SwitchMode(pub Mode);
  59
  60/// PushOperator is used to put vim into a "minor" mode,
  61/// where it's waiting for a specific next set of keystrokes.
  62/// For example 'd' needs a motion to complete.
  63#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
  64pub struct PushOperator(pub Operator);
  65
  66/// Number is used to manage vim's count. Pushing a digit
  67/// multiplies the current value by 10 and adds the digit.
  68#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
  69struct Number(usize);
  70
  71#[derive(Clone, Deserialize, JsonSchema, PartialEq)]
  72struct SelectRegister(String);
  73
  74actions!(
  75    vim,
  76    [
  77        ClearOperators,
  78        Tab,
  79        Enter,
  80        InnerObject,
  81        FindForward,
  82        FindBackward,
  83        MaximizePane,
  84        OpenDefaultKeymap,
  85        ResetPaneSizes,
  86        Sneak,
  87        SneakBackward,
  88    ]
  89);
  90
  91// in the workspace namespace so it's not filtered out when vim is disabled.
  92actions!(workspace, [ToggleVimMode]);
  93
  94impl_actions!(
  95    vim,
  96    [ResizePane, SwitchMode, PushOperator, Number, SelectRegister]
  97);
  98
  99/// Initializes the `vim` crate.
 100pub fn init(cx: &mut App) {
 101    vim_mode_setting::init(cx);
 102    VimSettings::register(cx);
 103    VimGlobals::register(cx);
 104
 105    cx.observe_new(Vim::register).detach();
 106
 107    cx.observe_new(|workspace: &mut Workspace, _, _| {
 108        workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
 109            let fs = workspace.app_state().fs.clone();
 110            let currently_enabled = Vim::enabled(cx);
 111            update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
 112                *setting = Some(!currently_enabled)
 113            })
 114        });
 115
 116        workspace.register_action(|_, _: &OpenDefaultKeymap, _, cx| {
 117            cx.emit(workspace::Event::OpenBundledFile {
 118                text: settings::vim_keymap(),
 119                title: "Default Vim Bindings",
 120                language: "JSON",
 121            });
 122        });
 123
 124        workspace.register_action(|workspace, _: &ResetPaneSizes, _, cx| {
 125            workspace.reset_pane_sizes(cx);
 126        });
 127
 128        workspace.register_action(|workspace, _: &MaximizePane, window, cx| {
 129            let pane = workspace.active_pane();
 130            let Some(size) = workspace.bounding_box_for_pane(&pane) else {
 131                return;
 132            };
 133
 134            let theme = ThemeSettings::get_global(cx);
 135            let height = theme.buffer_font_size() * theme.buffer_line_height.value();
 136
 137            let desired_size = if let Some(count) = Vim::take_count(cx) {
 138                height * count
 139            } else {
 140                px(10000.)
 141            };
 142            workspace.resize_pane(Axis::Vertical, desired_size - size.size.height, window, cx)
 143        });
 144
 145        workspace.register_action(|workspace, action: &ResizePane, window, cx| {
 146            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 147            let theme = ThemeSettings::get_global(cx);
 148            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
 149                return;
 150            };
 151            let Ok(width) = window
 152                .text_system()
 153                .advance(font_id, theme.buffer_font_size(), 'm')
 154            else {
 155                return;
 156            };
 157            let height = theme.buffer_font_size() * theme.buffer_line_height.value();
 158
 159            let (axis, amount) = match action.0 {
 160                ResizeIntent::Lengthen => (Axis::Vertical, height),
 161                ResizeIntent::Shorten => (Axis::Vertical, height * -1.),
 162                ResizeIntent::Widen => (Axis::Horizontal, width.width),
 163                ResizeIntent::Narrow => (Axis::Horizontal, width.width * -1.),
 164            };
 165
 166            workspace.resize_pane(axis, amount * count, window, cx);
 167        });
 168
 169        workspace.register_action(|workspace, _: &SearchSubmit, window, cx| {
 170            let vim = workspace
 171                .focused_pane(window, cx)
 172                .read(cx)
 173                .active_item()
 174                .and_then(|item| item.act_as::<Editor>(cx))
 175                .and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned());
 176            let Some(vim) = vim else { return };
 177            vim.entity.update(cx, |_, cx| {
 178                cx.defer_in(window, |vim, window, cx| vim.search_submit(window, cx))
 179            })
 180        });
 181    })
 182    .detach();
 183}
 184
 185#[derive(Clone)]
 186pub(crate) struct VimAddon {
 187    pub(crate) entity: Entity<Vim>,
 188}
 189
 190impl editor::Addon for VimAddon {
 191    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &App) {
 192        self.entity.read(cx).extend_key_context(key_context, cx)
 193    }
 194
 195    fn to_any(&self) -> &dyn std::any::Any {
 196        self
 197    }
 198}
 199
 200/// The state pertaining to Vim mode.
 201pub(crate) struct Vim {
 202    pub(crate) mode: Mode,
 203    pub last_mode: Mode,
 204    pub temp_mode: bool,
 205    pub status_label: Option<SharedString>,
 206    pub exit_temporary_mode: bool,
 207
 208    operator_stack: Vec<Operator>,
 209    pub(crate) replacements: Vec<(Range<editor::Anchor>, String)>,
 210
 211    pub(crate) marks: HashMap<String, Vec<Anchor>>,
 212    pub(crate) stored_visual_mode: Option<(Mode, Vec<bool>)>,
 213    pub(crate) change_list: Vec<Vec<Anchor>>,
 214    pub(crate) change_list_position: Option<usize>,
 215
 216    pub(crate) current_tx: Option<TransactionId>,
 217    pub(crate) current_anchor: Option<Selection<Anchor>>,
 218    pub(crate) undo_modes: HashMap<TransactionId, Mode>,
 219
 220    selected_register: Option<char>,
 221    pub search: SearchState,
 222
 223    editor: WeakEntity<Editor>,
 224
 225    last_command: Option<String>,
 226    running_command: Option<Task<()>>,
 227    _subscriptions: Vec<Subscription>,
 228}
 229
 230// Hack: Vim intercepts events dispatched to a window and updates the view in response.
 231// This means it needs a VisualContext. The easiest way to satisfy that constraint is
 232// to make Vim a "View" that is just never actually rendered.
 233impl Render for Vim {
 234    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 235        gpui::Empty
 236    }
 237}
 238
 239enum VimEvent {
 240    Focused,
 241}
 242impl EventEmitter<VimEvent> for Vim {}
 243
 244impl Vim {
 245    /// The namespace for Vim actions.
 246    const NAMESPACE: &'static str = "vim";
 247
 248    pub fn new(window: &mut Window, cx: &mut Context<Editor>) -> Entity<Self> {
 249        let editor = cx.entity().clone();
 250
 251        cx.new(|cx| Vim {
 252            mode: Mode::Normal,
 253            last_mode: Mode::Normal,
 254            temp_mode: false,
 255            exit_temporary_mode: false,
 256            operator_stack: Vec::new(),
 257            replacements: Vec::new(),
 258
 259            marks: HashMap::default(),
 260            stored_visual_mode: None,
 261            change_list: Vec::new(),
 262            change_list_position: None,
 263            current_tx: None,
 264            current_anchor: None,
 265            undo_modes: HashMap::default(),
 266
 267            status_label: None,
 268            selected_register: None,
 269            search: SearchState::default(),
 270
 271            last_command: None,
 272            running_command: None,
 273
 274            editor: editor.downgrade(),
 275            _subscriptions: vec![
 276                cx.observe_keystrokes(Self::observe_keystrokes),
 277                cx.subscribe_in(&editor, window, |this, _, event, window, cx| {
 278                    this.handle_editor_event(event, window, cx)
 279                }),
 280            ],
 281        })
 282    }
 283
 284    fn register(editor: &mut Editor, window: Option<&mut Window>, cx: &mut Context<Editor>) {
 285        let Some(window) = window else {
 286            return;
 287        };
 288
 289        if !editor.use_modal_editing() {
 290            return;
 291        }
 292
 293        let mut was_enabled = Vim::enabled(cx);
 294        let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 295        cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
 296            let enabled = Vim::enabled(cx);
 297            let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 298            if enabled && was_enabled && (toggle != was_toggle) {
 299                if toggle {
 300                    let is_relative = editor
 301                        .addon::<VimAddon>()
 302                        .map(|vim| vim.entity.read(cx).mode != Mode::Insert);
 303                    editor.set_relative_line_number(is_relative, cx)
 304                } else {
 305                    editor.set_relative_line_number(None, cx)
 306                }
 307            }
 308            was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 309            if was_enabled == enabled {
 310                return;
 311            }
 312            was_enabled = enabled;
 313            if enabled {
 314                Self::activate(editor, window, cx)
 315            } else {
 316                Self::deactivate(editor, cx)
 317            }
 318        })
 319        .detach();
 320        if was_enabled {
 321            Self::activate(editor, window, cx)
 322        }
 323    }
 324
 325    fn activate(editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>) {
 326        let vim = Vim::new(window, cx);
 327
 328        editor.register_addon(VimAddon {
 329            entity: vim.clone(),
 330        });
 331
 332        vim.update(cx, |_, cx| {
 333            Vim::action(editor, cx, |vim, action: &SwitchMode, window, cx| {
 334                vim.switch_mode(action.0, false, window, cx)
 335            });
 336
 337            Vim::action(editor, cx, |vim, action: &PushOperator, window, cx| {
 338                vim.push_operator(action.0.clone(), window, cx)
 339            });
 340
 341            Vim::action(editor, cx, |vim, _: &ClearOperators, window, cx| {
 342                vim.clear_operator(window, cx)
 343            });
 344            Vim::action(editor, cx, |vim, n: &Number, window, cx| {
 345                vim.push_count_digit(n.0, window, cx);
 346            });
 347            Vim::action(editor, cx, |vim, _: &Tab, window, cx| {
 348                let Some(anchor) = vim
 349                    .editor()
 350                    .and_then(|editor| editor.read(cx).inline_completion_start_anchor())
 351                else {
 352                    return;
 353                };
 354
 355                vim.update_editor(window, cx, |_, editor, window, cx| {
 356                    editor.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
 357                        s.select_anchor_ranges([anchor..anchor])
 358                    });
 359                });
 360                vim.switch_mode(Mode::Insert, true, window, cx);
 361            });
 362            Vim::action(editor, cx, |vim, _: &Enter, window, cx| {
 363                vim.input_ignored("\n".into(), window, cx)
 364            });
 365
 366            normal::register(editor, cx);
 367            insert::register(editor, cx);
 368            helix::register(editor, cx);
 369            motion::register(editor, cx);
 370            command::register(editor, cx);
 371            replace::register(editor, cx);
 372            indent::register(editor, cx);
 373            rewrap::register(editor, cx);
 374            object::register(editor, cx);
 375            visual::register(editor, cx);
 376            change_list::register(editor, cx);
 377            digraph::register(editor, cx);
 378
 379            cx.defer_in(window, |vim, window, cx| {
 380                vim.focused(false, window, cx);
 381            })
 382        })
 383    }
 384
 385    fn deactivate(editor: &mut Editor, cx: &mut Context<Editor>) {
 386        editor.set_cursor_shape(CursorShape::Bar, cx);
 387        editor.set_clip_at_line_ends(false, cx);
 388        editor.set_collapse_matches(false);
 389        editor.set_input_enabled(true);
 390        editor.set_autoindent(true);
 391        editor.selections.line_mode = false;
 392        editor.unregister_addon::<VimAddon>();
 393        editor.set_relative_line_number(None, cx);
 394        if let Some(vim) = Vim::globals(cx).focused_vim() {
 395            if vim.entity_id() == cx.entity().entity_id() {
 396                Vim::globals(cx).focused_vim = None;
 397            }
 398        }
 399    }
 400
 401    /// Register an action on the editor.
 402    pub fn action<A: Action>(
 403        editor: &mut Editor,
 404        cx: &mut Context<Vim>,
 405        f: impl Fn(&mut Vim, &A, &mut Window, &mut Context<Vim>) + 'static,
 406    ) {
 407        let subscription = editor.register_action(cx.listener(f));
 408        cx.on_release(|_, _| drop(subscription)).detach();
 409    }
 410
 411    pub fn editor(&self) -> Option<Entity<Editor>> {
 412        self.editor.upgrade()
 413    }
 414
 415    pub fn workspace(&self, window: &mut Window) -> Option<Entity<Workspace>> {
 416        window.root::<Workspace>().flatten()
 417    }
 418
 419    pub fn pane(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Entity<Pane>> {
 420        self.workspace(window)
 421            .map(|workspace| workspace.read(cx).focused_pane(window, cx))
 422    }
 423
 424    pub fn enabled(cx: &mut App) -> bool {
 425        VimModeSetting::get_global(cx).0
 426    }
 427
 428    /// Called whenever an keystroke is typed so vim can observe all actions
 429    /// and keystrokes accordingly.
 430    fn observe_keystrokes(
 431        &mut self,
 432        keystroke_event: &KeystrokeEvent,
 433        window: &mut Window,
 434        cx: &mut Context<Self>,
 435    ) {
 436        if self.exit_temporary_mode {
 437            self.exit_temporary_mode = false;
 438            // Don't switch to insert mode if the action is temporary_normal.
 439            if let Some(action) = keystroke_event.action.as_ref() {
 440                if action.as_any().downcast_ref::<TemporaryNormal>().is_some() {
 441                    return;
 442                }
 443            }
 444            self.switch_mode(Mode::Insert, false, window, cx)
 445        }
 446        if let Some(action) = keystroke_event.action.as_ref() {
 447            // Keystroke is handled by the vim system, so continue forward
 448            if action.name().starts_with("vim::") {
 449                return;
 450            }
 451        } else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
 452        {
 453            return;
 454        }
 455
 456        if let Some(operator) = self.active_operator() {
 457            match operator {
 458                Operator::Literal { prefix } => {
 459                    self.handle_literal_keystroke(
 460                        keystroke_event,
 461                        prefix.unwrap_or_default(),
 462                        window,
 463                        cx,
 464                    );
 465                }
 466                _ if !operator.is_waiting(self.mode) => {
 467                    self.clear_operator(window, cx);
 468                    self.stop_recording_immediately(Box::new(ClearOperators), cx)
 469                }
 470                _ => {}
 471            }
 472        }
 473    }
 474
 475    fn handle_editor_event(
 476        &mut self,
 477        event: &EditorEvent,
 478        window: &mut Window,
 479        cx: &mut Context<Self>,
 480    ) {
 481        match event {
 482            EditorEvent::Focused => self.focused(true, window, cx),
 483            EditorEvent::Blurred => self.blurred(window, cx),
 484            EditorEvent::SelectionsChanged { local: true } => {
 485                self.local_selections_changed(window, cx);
 486            }
 487            EditorEvent::InputIgnored { text } => {
 488                self.input_ignored(text.clone(), window, cx);
 489                Vim::globals(cx).observe_insertion(text, None)
 490            }
 491            EditorEvent::InputHandled {
 492                text,
 493                utf16_range_to_replace: range_to_replace,
 494            } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
 495            EditorEvent::TransactionBegun { transaction_id } => {
 496                self.transaction_begun(*transaction_id, window, cx)
 497            }
 498            EditorEvent::TransactionUndone { transaction_id } => {
 499                self.transaction_undone(transaction_id, window, cx)
 500            }
 501            EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
 502            EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
 503            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
 504            _ => {}
 505        }
 506    }
 507
 508    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
 509        if matches!(
 510            operator,
 511            Operator::Change
 512                | Operator::Delete
 513                | Operator::Replace
 514                | Operator::Indent
 515                | Operator::Outdent
 516                | Operator::AutoIndent
 517                | Operator::Lowercase
 518                | Operator::Uppercase
 519                | Operator::OppositeCase
 520                | Operator::ToggleComments
 521        ) {
 522            self.start_recording(cx)
 523        };
 524        // Since these operations can only be entered with pre-operators,
 525        // we need to clear the previous operators when pushing,
 526        // so that the current stack is the most correct
 527        if matches!(
 528            operator,
 529            Operator::AddSurrounds { .. }
 530                | Operator::ChangeSurrounds { .. }
 531                | Operator::DeleteSurrounds
 532        ) {
 533            self.operator_stack.clear();
 534            if let Operator::AddSurrounds { target: None } = operator {
 535                self.start_recording(cx);
 536            }
 537        };
 538        self.operator_stack.push(operator);
 539        self.sync_vim_settings(window, cx);
 540    }
 541
 542    pub fn switch_mode(
 543        &mut self,
 544        mode: Mode,
 545        leave_selections: bool,
 546        window: &mut Window,
 547        cx: &mut Context<Self>,
 548    ) {
 549        if self.temp_mode && mode == Mode::Normal {
 550            self.temp_mode = false;
 551            self.switch_mode(Mode::Normal, leave_selections, window, cx);
 552            self.switch_mode(Mode::Insert, false, window, cx);
 553            return;
 554        } else if self.temp_mode
 555            && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
 556        {
 557            self.temp_mode = false;
 558        }
 559
 560        let last_mode = self.mode;
 561        let prior_mode = self.last_mode;
 562        let prior_tx = self.current_tx;
 563        self.status_label.take();
 564        self.last_mode = last_mode;
 565        self.mode = mode;
 566        self.operator_stack.clear();
 567        self.selected_register.take();
 568        self.cancel_running_command(window, cx);
 569        if mode == Mode::Normal || mode != last_mode {
 570            self.current_tx.take();
 571            self.current_anchor.take();
 572        }
 573        if mode != Mode::Insert && mode != Mode::Replace {
 574            Vim::take_count(cx);
 575        }
 576
 577        // Sync editor settings like clip mode
 578        self.sync_vim_settings(window, cx);
 579
 580        if VimSettings::get_global(cx).toggle_relative_line_numbers
 581            && self.mode != self.last_mode
 582            && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
 583        {
 584            self.update_editor(window, cx, |vim, editor, _, cx| {
 585                let is_relative = vim.mode != Mode::Insert;
 586                editor.set_relative_line_number(Some(is_relative), cx)
 587            });
 588        }
 589
 590        if leave_selections {
 591            return;
 592        }
 593
 594        if !mode.is_visual() && last_mode.is_visual() {
 595            self.create_visual_marks(last_mode, window, cx);
 596        }
 597
 598        // Adjust selections
 599        self.update_editor(window, cx, |vim, editor, window, cx| {
 600            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 601            {
 602                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
 603                    Some((point, goal))
 604                })
 605            }
 606            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 607                if let Some(prior_tx) = prior_tx {
 608                    editor.group_until_transaction(prior_tx, cx)
 609                }
 610            }
 611
 612            editor.change_selections(None, window, cx, |s| {
 613                // we cheat with visual block mode and use multiple cursors.
 614                // the cost of this cheat is we need to convert back to a single
 615                // cursor whenever vim would.
 616                if last_mode == Mode::VisualBlock
 617                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 618                {
 619                    let tail = s.oldest_anchor().tail();
 620                    let head = s.newest_anchor().head();
 621                    s.select_anchor_ranges(vec![tail..head]);
 622                } else if last_mode == Mode::Insert
 623                    && prior_mode == Mode::VisualBlock
 624                    && mode != Mode::VisualBlock
 625                {
 626                    let pos = s.first_anchor().head();
 627                    s.select_anchor_ranges(vec![pos..pos])
 628                }
 629
 630                let snapshot = s.display_map();
 631                if let Some(pending) = s.pending.as_mut() {
 632                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 633                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 634                        end = snapshot
 635                            .buffer_snapshot
 636                            .clip_point(end + Point::new(0, 1), Bias::Right);
 637                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 638                    }
 639                }
 640
 641                s.move_with(|map, selection| {
 642                    if last_mode.is_visual() && !mode.is_visual() {
 643                        let mut point = selection.head();
 644                        if !selection.reversed && !selection.is_empty() {
 645                            point = movement::left(map, selection.head());
 646                        }
 647                        selection.collapse_to(point, selection.goal)
 648                    } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
 649                        selection.end = movement::right(map, selection.start);
 650                    }
 651                });
 652            })
 653        });
 654    }
 655
 656    pub fn take_count(cx: &mut App) -> Option<usize> {
 657        let global_state = cx.global_mut::<VimGlobals>();
 658        if global_state.dot_replaying {
 659            return global_state.recorded_count;
 660        }
 661
 662        let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
 663            return None;
 664        } else {
 665            Some(
 666                global_state.post_count.take().unwrap_or(1)
 667                    * global_state.pre_count.take().unwrap_or(1),
 668            )
 669        };
 670
 671        if global_state.dot_recording {
 672            global_state.recorded_count = count;
 673        }
 674        count
 675    }
 676
 677    pub fn cursor_shape(&self) -> CursorShape {
 678        match self.mode {
 679            Mode::Normal => {
 680                if let Some(operator) = self.operator_stack.last() {
 681                    match operator {
 682                        // Navigation operators -> Block cursor
 683                        Operator::FindForward { .. }
 684                        | Operator::FindBackward { .. }
 685                        | Operator::Mark
 686                        | Operator::Jump { .. }
 687                        | Operator::Register
 688                        | Operator::RecordRegister
 689                        | Operator::ReplayRegister => CursorShape::Block,
 690
 691                        // All other operators -> Underline cursor
 692                        _ => CursorShape::Underline,
 693                    }
 694                } else {
 695                    // No operator active -> Block cursor
 696                    CursorShape::Block
 697                }
 698            }
 699            Mode::Replace => CursorShape::Underline,
 700            Mode::HelixNormal | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
 701                CursorShape::Block
 702            }
 703            Mode::Insert => CursorShape::Bar,
 704        }
 705    }
 706
 707    pub fn editor_input_enabled(&self) -> bool {
 708        match self.mode {
 709            Mode::Insert => {
 710                if let Some(operator) = self.operator_stack.last() {
 711                    !operator.is_waiting(self.mode)
 712                } else {
 713                    true
 714                }
 715            }
 716            Mode::Normal
 717            | Mode::HelixNormal
 718            | Mode::Replace
 719            | Mode::Visual
 720            | Mode::VisualLine
 721            | Mode::VisualBlock => false,
 722        }
 723    }
 724
 725    pub fn should_autoindent(&self) -> bool {
 726        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
 727    }
 728
 729    pub fn clip_at_line_ends(&self) -> bool {
 730        match self.mode {
 731            Mode::Insert
 732            | Mode::Visual
 733            | Mode::VisualLine
 734            | Mode::VisualBlock
 735            | Mode::Replace
 736            | Mode::HelixNormal => false,
 737            Mode::Normal => true,
 738        }
 739    }
 740
 741    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
 742        let mut mode = match self.mode {
 743            Mode::Normal => "normal",
 744            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
 745            Mode::Insert => "insert",
 746            Mode::Replace => "replace",
 747            Mode::HelixNormal => "helix_normal",
 748        }
 749        .to_string();
 750
 751        let mut operator_id = "none";
 752
 753        let active_operator = self.active_operator();
 754        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
 755            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
 756        {
 757            context.add("VimCount");
 758        }
 759
 760        if let Some(active_operator) = active_operator {
 761            if active_operator.is_waiting(self.mode) {
 762                if matches!(active_operator, Operator::Literal { .. }) {
 763                    mode = "literal".to_string();
 764                } else {
 765                    mode = "waiting".to_string();
 766                }
 767            } else {
 768                operator_id = active_operator.id();
 769                mode = "operator".to_string();
 770            }
 771        }
 772
 773        if mode == "normal" || mode == "visual" || mode == "operator" {
 774            context.add("VimControl");
 775        }
 776        context.set("vim_mode", mode);
 777        context.set("vim_operator", operator_id);
 778    }
 779
 780    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
 781        let Some(editor) = self.editor() else {
 782            return;
 783        };
 784        let newest_selection_empty = editor.update(cx, |editor, cx| {
 785            editor.selections.newest::<usize>(cx).is_empty()
 786        });
 787        let editor = editor.read(cx);
 788        let editor_mode = editor.mode();
 789
 790        if editor_mode == EditorMode::Full
 791                && !newest_selection_empty
 792                && self.mode == Mode::Normal
 793                // When following someone, don't switch vim mode.
 794                && editor.leader_peer_id().is_none()
 795        {
 796            if preserve_selection {
 797                self.switch_mode(Mode::Visual, true, window, cx);
 798            } else {
 799                self.update_editor(window, cx, |_, editor, window, cx| {
 800                    editor.set_clip_at_line_ends(false, cx);
 801                    editor.change_selections(None, window, cx, |s| {
 802                        s.move_with(|_, selection| {
 803                            selection.collapse_to(selection.start, selection.goal)
 804                        })
 805                    });
 806                });
 807            }
 808        }
 809
 810        cx.emit(VimEvent::Focused);
 811        self.sync_vim_settings(window, cx);
 812
 813        if VimSettings::get_global(cx).toggle_relative_line_numbers {
 814            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
 815                if old_vim.entity_id() != cx.entity().entity_id() {
 816                    old_vim.update(cx, |vim, cx| {
 817                        vim.update_editor(window, cx, |_, editor, _, cx| {
 818                            editor.set_relative_line_number(None, cx)
 819                        });
 820                    });
 821
 822                    self.update_editor(window, cx, |vim, editor, _, cx| {
 823                        let is_relative = vim.mode != Mode::Insert;
 824                        editor.set_relative_line_number(Some(is_relative), cx)
 825                    });
 826                }
 827            } else {
 828                self.update_editor(window, cx, |vim, editor, _, cx| {
 829                    let is_relative = vim.mode != Mode::Insert;
 830                    editor.set_relative_line_number(Some(is_relative), cx)
 831                });
 832            }
 833        }
 834        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
 835    }
 836
 837    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 838        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
 839        self.store_visual_marks(window, cx);
 840        self.clear_operator(window, cx);
 841        self.update_editor(window, cx, |_, editor, _, cx| {
 842            editor.set_cursor_shape(language::CursorShape::Hollow, cx);
 843        });
 844    }
 845
 846    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
 847        self.update_editor(window, cx, |vim, editor, _, cx| {
 848            editor.set_cursor_shape(vim.cursor_shape(), cx);
 849        });
 850    }
 851
 852    fn update_editor<S>(
 853        &mut self,
 854        window: &mut Window,
 855        cx: &mut Context<Self>,
 856        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
 857    ) -> Option<S> {
 858        let editor = self.editor.upgrade()?;
 859        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
 860    }
 861
 862    fn editor_selections(
 863        &mut self,
 864        window: &mut Window,
 865        cx: &mut Context<Self>,
 866    ) -> Vec<Range<Anchor>> {
 867        self.update_editor(window, cx, |_, editor, _, _| {
 868            editor
 869                .selections
 870                .disjoint_anchors()
 871                .iter()
 872                .map(|selection| selection.tail()..selection.head())
 873                .collect()
 874        })
 875        .unwrap_or_default()
 876    }
 877
 878    /// When doing an action that modifies the buffer, we start recording so that `.`
 879    /// will replay the action.
 880    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
 881        Vim::update_globals(cx, |globals, cx| {
 882            if !globals.dot_replaying {
 883                globals.dot_recording = true;
 884                globals.recording_actions = Default::default();
 885                globals.recorded_count = None;
 886
 887                let selections = self.editor().map(|editor| {
 888                    editor.update(cx, |editor, cx| {
 889                        (
 890                            editor.selections.oldest::<Point>(cx),
 891                            editor.selections.newest::<Point>(cx),
 892                        )
 893                    })
 894                });
 895
 896                if let Some((oldest, newest)) = selections {
 897                    globals.recorded_selection = match self.mode {
 898                        Mode::Visual if newest.end.row == newest.start.row => {
 899                            RecordedSelection::SingleLine {
 900                                cols: newest.end.column - newest.start.column,
 901                            }
 902                        }
 903                        Mode::Visual => RecordedSelection::Visual {
 904                            rows: newest.end.row - newest.start.row,
 905                            cols: newest.end.column,
 906                        },
 907                        Mode::VisualLine => RecordedSelection::VisualLine {
 908                            rows: newest.end.row - newest.start.row,
 909                        },
 910                        Mode::VisualBlock => RecordedSelection::VisualBlock {
 911                            rows: newest.end.row.abs_diff(oldest.start.row),
 912                            cols: newest.end.column.abs_diff(oldest.start.column),
 913                        },
 914                        _ => RecordedSelection::None,
 915                    }
 916                } else {
 917                    globals.recorded_selection = RecordedSelection::None;
 918                }
 919            }
 920        })
 921    }
 922
 923    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
 924        let globals = Vim::globals(cx);
 925        globals.dot_replaying = false;
 926        if let Some(replayer) = globals.replayer.take() {
 927            replayer.stop();
 928        }
 929    }
 930
 931    /// When finishing an action that modifies the buffer, stop recording.
 932    /// as you usually call this within a keystroke handler we also ensure that
 933    /// the current action is recorded.
 934    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
 935        let globals = Vim::globals(cx);
 936        if globals.dot_recording {
 937            globals.stop_recording_after_next_action = true;
 938        }
 939        self.exit_temporary_mode = self.temp_mode;
 940    }
 941
 942    /// Stops recording actions immediately rather than waiting until after the
 943    /// next action to stop recording.
 944    ///
 945    /// This doesn't include the current action.
 946    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
 947        let globals = Vim::globals(cx);
 948        if globals.dot_recording {
 949            globals
 950                .recording_actions
 951                .push(ReplayableAction::Action(action.boxed_clone()));
 952            globals.recorded_actions = mem::take(&mut globals.recording_actions);
 953            globals.dot_recording = false;
 954            globals.stop_recording_after_next_action = false;
 955        }
 956        self.exit_temporary_mode = self.temp_mode;
 957    }
 958
 959    /// Explicitly record one action (equivalents to start_recording and stop_recording)
 960    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
 961        self.start_recording(cx);
 962        self.stop_recording(cx);
 963    }
 964
 965    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
 966        if self.active_operator().is_some() {
 967            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
 968
 969            Vim::globals(cx).post_count = Some(
 970                post_count
 971                    .checked_mul(10)
 972                    .and_then(|post_count| post_count.checked_add(number))
 973                    .unwrap_or(post_count),
 974            )
 975        } else {
 976            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
 977
 978            Vim::globals(cx).pre_count = Some(
 979                pre_count
 980                    .checked_mul(10)
 981                    .and_then(|pre_count| pre_count.checked_add(number))
 982                    .unwrap_or(pre_count),
 983            )
 984        }
 985        // update the keymap so that 0 works
 986        self.sync_vim_settings(window, cx)
 987    }
 988
 989    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
 990        if register.chars().count() == 1 {
 991            self.selected_register
 992                .replace(register.chars().next().unwrap());
 993        }
 994        self.operator_stack.clear();
 995        self.sync_vim_settings(window, cx);
 996    }
 997
 998    fn maybe_pop_operator(&mut self) -> Option<Operator> {
 999        self.operator_stack.pop()
1000    }
1001
1002    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1003        let popped_operator = self.operator_stack.pop()
1004            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1005        self.sync_vim_settings(window, cx);
1006        popped_operator
1007    }
1008
1009    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1010        Vim::take_count(cx);
1011        self.selected_register.take();
1012        self.operator_stack.clear();
1013        self.sync_vim_settings(window, cx);
1014    }
1015
1016    fn active_operator(&self) -> Option<Operator> {
1017        self.operator_stack.last().cloned()
1018    }
1019
1020    fn transaction_begun(
1021        &mut self,
1022        transaction_id: TransactionId,
1023        _window: &mut Window,
1024        _: &mut Context<Self>,
1025    ) {
1026        let mode = if (self.mode == Mode::Insert
1027            || self.mode == Mode::Replace
1028            || self.mode == Mode::Normal)
1029            && self.current_tx.is_none()
1030        {
1031            self.current_tx = Some(transaction_id);
1032            self.last_mode
1033        } else {
1034            self.mode
1035        };
1036        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1037            self.undo_modes.insert(transaction_id, mode);
1038        }
1039    }
1040
1041    fn transaction_undone(
1042        &mut self,
1043        transaction_id: &TransactionId,
1044        window: &mut Window,
1045        cx: &mut Context<Self>,
1046    ) {
1047        match self.mode {
1048            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1049                self.update_editor(window, cx, |vim, editor, window, cx| {
1050                    let original_mode = vim.undo_modes.get(transaction_id);
1051                    editor.change_selections(None, window, cx, |s| match original_mode {
1052                        Some(Mode::VisualLine) => {
1053                            s.move_with(|map, selection| {
1054                                selection.collapse_to(
1055                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1056                                    SelectionGoal::None,
1057                                )
1058                            });
1059                        }
1060                        Some(Mode::VisualBlock) => {
1061                            let mut first = s.first_anchor();
1062                            first.collapse_to(first.start, first.goal);
1063                            s.select_anchors(vec![first]);
1064                        }
1065                        _ => {
1066                            s.move_with(|map, selection| {
1067                                selection.collapse_to(
1068                                    map.clip_at_line_end(selection.start),
1069                                    selection.goal,
1070                                );
1071                            });
1072                        }
1073                    });
1074                });
1075                self.switch_mode(Mode::Normal, true, window, cx)
1076            }
1077            Mode::Normal => {
1078                self.update_editor(window, cx, |_, editor, window, cx| {
1079                    editor.change_selections(None, window, cx, |s| {
1080                        s.move_with(|map, selection| {
1081                            selection
1082                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1083                        })
1084                    })
1085                });
1086            }
1087            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1088        }
1089    }
1090
1091    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1092        let Some(editor) = self.editor() else { return };
1093
1094        if editor.read(cx).leader_peer_id().is_some() {
1095            return;
1096        }
1097
1098        let newest = editor.read(cx).selections.newest_anchor().clone();
1099        let is_multicursor = editor.read(cx).selections.count() > 1;
1100        if self.mode == Mode::Insert && self.current_tx.is_some() {
1101            if self.current_anchor.is_none() {
1102                self.current_anchor = Some(newest);
1103            } else if self.current_anchor.as_ref().unwrap() != &newest {
1104                if let Some(tx_id) = self.current_tx.take() {
1105                    self.update_editor(window, cx, |_, editor, _, cx| {
1106                        editor.group_until_transaction(tx_id, cx)
1107                    });
1108                }
1109            }
1110        } else if self.mode == Mode::Normal && newest.start != newest.end {
1111            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1112                self.switch_mode(Mode::VisualBlock, false, window, cx);
1113            } else {
1114                self.switch_mode(Mode::Visual, false, window, cx)
1115            }
1116        } else if newest.start == newest.end
1117            && !is_multicursor
1118            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1119        {
1120            self.switch_mode(Mode::Normal, true, window, cx);
1121        }
1122    }
1123
1124    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1125        if text.is_empty() {
1126            return;
1127        }
1128
1129        match self.active_operator() {
1130            Some(Operator::FindForward { before }) => {
1131                let find = Motion::FindForward {
1132                    before,
1133                    char: text.chars().next().unwrap(),
1134                    mode: if VimSettings::get_global(cx).use_multiline_find {
1135                        FindRange::MultiLine
1136                    } else {
1137                        FindRange::SingleLine
1138                    },
1139                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1140                };
1141                Vim::globals(cx).last_find = Some(find.clone());
1142                self.motion(find, window, cx)
1143            }
1144            Some(Operator::FindBackward { after }) => {
1145                let find = Motion::FindBackward {
1146                    after,
1147                    char: text.chars().next().unwrap(),
1148                    mode: if VimSettings::get_global(cx).use_multiline_find {
1149                        FindRange::MultiLine
1150                    } else {
1151                        FindRange::SingleLine
1152                    },
1153                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1154                };
1155                Vim::globals(cx).last_find = Some(find.clone());
1156                self.motion(find, window, cx)
1157            }
1158            Some(Operator::Sneak { first_char }) => {
1159                if let Some(first_char) = first_char {
1160                    if let Some(second_char) = text.chars().next() {
1161                        let sneak = Motion::Sneak {
1162                            first_char,
1163                            second_char,
1164                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1165                        };
1166                        Vim::globals(cx).last_find = Some((&sneak).clone());
1167                        self.motion(sneak, window, cx)
1168                    }
1169                } else {
1170                    let first_char = text.chars().next();
1171                    self.pop_operator(window, cx);
1172                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1173                }
1174            }
1175            Some(Operator::SneakBackward { first_char }) => {
1176                if let Some(first_char) = first_char {
1177                    if let Some(second_char) = text.chars().next() {
1178                        let sneak = Motion::SneakBackward {
1179                            first_char,
1180                            second_char,
1181                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1182                        };
1183                        Vim::globals(cx).last_find = Some((&sneak).clone());
1184                        self.motion(sneak, window, cx)
1185                    }
1186                } else {
1187                    let first_char = text.chars().next();
1188                    self.pop_operator(window, cx);
1189                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1190                }
1191            }
1192            Some(Operator::Replace) => match self.mode {
1193                Mode::Normal => self.normal_replace(text, window, cx),
1194                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1195                    self.visual_replace(text, window, cx)
1196                }
1197                _ => self.clear_operator(window, cx),
1198            },
1199            Some(Operator::Digraph { first_char }) => {
1200                if let Some(first_char) = first_char {
1201                    if let Some(second_char) = text.chars().next() {
1202                        self.insert_digraph(first_char, second_char, window, cx);
1203                    }
1204                } else {
1205                    let first_char = text.chars().next();
1206                    self.pop_operator(window, cx);
1207                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1208                }
1209            }
1210            Some(Operator::Literal { prefix }) => {
1211                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1212            }
1213            Some(Operator::AddSurrounds { target }) => match self.mode {
1214                Mode::Normal => {
1215                    if let Some(target) = target {
1216                        self.add_surrounds(text, target, window, cx);
1217                        self.clear_operator(window, cx);
1218                    }
1219                }
1220                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1221                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1222                    self.clear_operator(window, cx);
1223                }
1224                _ => self.clear_operator(window, cx),
1225            },
1226            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1227                Mode::Normal => {
1228                    if let Some(target) = target {
1229                        self.change_surrounds(text, target, window, cx);
1230                        self.clear_operator(window, cx);
1231                    }
1232                }
1233                _ => self.clear_operator(window, cx),
1234            },
1235            Some(Operator::DeleteSurrounds) => match self.mode {
1236                Mode::Normal => {
1237                    self.delete_surrounds(text, window, cx);
1238                    self.clear_operator(window, cx);
1239                }
1240                _ => self.clear_operator(window, cx),
1241            },
1242            Some(Operator::Mark) => self.create_mark(text, false, window, cx),
1243            Some(Operator::RecordRegister) => {
1244                self.record_register(text.chars().next().unwrap(), window, cx)
1245            }
1246            Some(Operator::ReplayRegister) => {
1247                self.replay_register(text.chars().next().unwrap(), window, cx)
1248            }
1249            Some(Operator::Register) => match self.mode {
1250                Mode::Insert => {
1251                    self.update_editor(window, cx, |_, editor, window, cx| {
1252                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1253                            globals.read_register(text.chars().next(), Some(editor), cx)
1254                        }) {
1255                            editor.do_paste(
1256                                &register.text.to_string(),
1257                                register.clipboard_selections.clone(),
1258                                false,
1259                                window,
1260                                cx,
1261                            )
1262                        }
1263                    });
1264                    self.clear_operator(window, cx);
1265                }
1266                _ => {
1267                    self.select_register(text, window, cx);
1268                }
1269            },
1270            Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
1271            _ => {
1272                if self.mode == Mode::Replace {
1273                    self.multi_replace(text, window, cx)
1274                }
1275
1276                if self.mode == Mode::Normal {
1277                    self.update_editor(window, cx, |_, editor, window, cx| {
1278                        editor.accept_inline_completion(
1279                            &editor::actions::AcceptInlineCompletion {},
1280                            window,
1281                            cx,
1282                        );
1283                    });
1284                }
1285            }
1286        }
1287    }
1288
1289    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1290        self.update_editor(window, cx, |vim, editor, window, cx| {
1291            editor.set_cursor_shape(vim.cursor_shape(), cx);
1292            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1293            editor.set_collapse_matches(true);
1294            editor.set_input_enabled(vim.editor_input_enabled());
1295            editor.set_autoindent(vim.should_autoindent());
1296            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1297
1298            let hide_inline_completions = match vim.mode {
1299                Mode::Insert | Mode::Replace => false,
1300                _ => true,
1301            };
1302            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1303        });
1304        cx.notify()
1305    }
1306}
1307
1308/// Controls when to use system clipboard.
1309#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1310#[serde(rename_all = "snake_case")]
1311pub enum UseSystemClipboard {
1312    /// Don't use system clipboard.
1313    Never,
1314    /// Use system clipboard.
1315    Always,
1316    /// Use system clipboard for yank operations.
1317    OnYank,
1318}
1319
1320#[derive(Deserialize)]
1321struct VimSettings {
1322    pub toggle_relative_line_numbers: bool,
1323    pub use_system_clipboard: UseSystemClipboard,
1324    pub use_multiline_find: bool,
1325    pub use_smartcase_find: bool,
1326    pub custom_digraphs: HashMap<String, Arc<str>>,
1327    pub highlight_on_yank_duration: u64,
1328}
1329
1330#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1331struct VimSettingsContent {
1332    pub toggle_relative_line_numbers: Option<bool>,
1333    pub use_system_clipboard: Option<UseSystemClipboard>,
1334    pub use_multiline_find: Option<bool>,
1335    pub use_smartcase_find: Option<bool>,
1336    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1337    pub highlight_on_yank_duration: Option<u64>,
1338}
1339
1340impl Settings for VimSettings {
1341    const KEY: Option<&'static str> = Some("vim");
1342
1343    type FileContent = VimSettingsContent;
1344
1345    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1346        sources.json_merge()
1347    }
1348}