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