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    Anchor, Bias, Editor, EditorEvent, EditorSettings, HideMouseCursorOrigin, ToPoint,
  26    movement::{self, FindRange},
  27};
  28use gpui::{
  29    Action, App, AppContext, Axis, Context, Entity, EventEmitter, KeyContext, KeystrokeEvent,
  30    Render, Subscription, Task, WeakEntity, Window, actions,
  31};
  32use insert::{NormalBefore, TemporaryNormal};
  33use language::{CharKind, CursorShape, Point, Selection, SelectionGoal, TransactionId};
  34pub use mode_indicator::ModeIndicator;
  35use motion::Motion;
  36use normal::search::SearchSubmit;
  37use object::Object;
  38use schemars::JsonSchema;
  39use serde::Deserialize;
  40use serde_derive::Serialize;
  41use settings::{Settings, SettingsSources, SettingsStore, update_settings_file};
  42use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
  43use std::{mem, ops::Range, sync::Arc};
  44use surrounds::SurroundsType;
  45use theme::ThemeSettings;
  46use ui::{IntoElement, SharedString, px};
  47use vim_mode_setting::HelixModeSetting;
  48use vim_mode_setting::VimModeSetting;
  49use workspace::{self, Pane, Workspace};
  50
  51use crate::state::ReplayableAction;
  52
  53/// Number is used to manage vim's count. Pushing a digit
  54/// multiplies the current value by 10 and adds the digit.
  55#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  56#[action(namespace = vim)]
  57struct Number(usize);
  58
  59#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  60#[action(namespace = vim)]
  61struct SelectRegister(String);
  62
  63#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  64#[action(namespace = vim)]
  65#[serde(deny_unknown_fields)]
  66struct PushObject {
  67    around: bool,
  68}
  69
  70#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  71#[action(namespace = vim)]
  72#[serde(deny_unknown_fields)]
  73struct PushFindForward {
  74    before: bool,
  75    multiline: bool,
  76}
  77
  78#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  79#[action(namespace = vim)]
  80#[serde(deny_unknown_fields)]
  81struct PushFindBackward {
  82    after: bool,
  83    multiline: bool,
  84}
  85
  86#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  87#[action(namespace = vim)]
  88#[serde(deny_unknown_fields)]
  89struct PushSneak {
  90    first_char: Option<char>,
  91}
  92
  93#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
  94#[action(namespace = vim)]
  95#[serde(deny_unknown_fields)]
  96struct PushSneakBackward {
  97    first_char: Option<char>,
  98}
  99
 100#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
 101#[action(namespace = vim)]
 102#[serde(deny_unknown_fields)]
 103struct PushAddSurrounds;
 104
 105#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
 106#[action(namespace = vim)]
 107#[serde(deny_unknown_fields)]
 108struct PushChangeSurrounds {
 109    target: Option<Object>,
 110}
 111
 112#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
 113#[action(namespace = vim)]
 114#[serde(deny_unknown_fields)]
 115struct PushJump {
 116    line: bool,
 117}
 118
 119#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
 120#[action(namespace = vim)]
 121#[serde(deny_unknown_fields)]
 122struct PushDigraph {
 123    first_char: Option<char>,
 124}
 125
 126#[derive(Clone, Deserialize, JsonSchema, PartialEq, Action)]
 127#[action(namespace = vim)]
 128#[serde(deny_unknown_fields)]
 129struct PushLiteral {
 130    prefix: Option<String>,
 131}
 132
 133actions!(
 134    vim,
 135    [
 136        SwitchToNormalMode,
 137        SwitchToInsertMode,
 138        SwitchToReplaceMode,
 139        SwitchToVisualMode,
 140        SwitchToVisualLineMode,
 141        SwitchToVisualBlockMode,
 142        SwitchToHelixNormalMode,
 143        ClearOperators,
 144        ClearExchange,
 145        Tab,
 146        Enter,
 147        InnerObject,
 148        MaximizePane,
 149        OpenDefaultKeymap,
 150        ResetPaneSizes,
 151        ResizePaneRight,
 152        ResizePaneLeft,
 153        ResizePaneUp,
 154        ResizePaneDown,
 155        PushChange,
 156        PushDelete,
 157        Exchange,
 158        PushYank,
 159        PushReplace,
 160        PushDeleteSurrounds,
 161        PushMark,
 162        ToggleMarksView,
 163        PushForcedMotion,
 164        PushIndent,
 165        PushOutdent,
 166        PushAutoIndent,
 167        PushRewrap,
 168        PushShellCommand,
 169        PushLowercase,
 170        PushUppercase,
 171        PushOppositeCase,
 172        PushRot13,
 173        PushRot47,
 174        ToggleRegistersView,
 175        PushRegister,
 176        PushRecordRegister,
 177        PushReplayRegister,
 178        PushReplaceWithRegister,
 179        PushToggleComments,
 180    ]
 181);
 182
 183// in the workspace namespace so it's not filtered out when vim is disabled.
 184actions!(workspace, [ToggleVimMode,]);
 185
 186/// Initializes the `vim` crate.
 187pub fn init(cx: &mut App) {
 188    vim_mode_setting::init(cx);
 189    VimSettings::register(cx);
 190    VimGlobals::register(cx);
 191
 192    cx.observe_new(Vim::register).detach();
 193
 194    cx.observe_new(|workspace: &mut Workspace, _, _| {
 195        workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
 196            let fs = workspace.app_state().fs.clone();
 197            let currently_enabled = Vim::enabled(cx);
 198            update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
 199                *setting = Some(!currently_enabled)
 200            })
 201        });
 202
 203        workspace.register_action(|_, _: &OpenDefaultKeymap, _, cx| {
 204            cx.emit(workspace::Event::OpenBundledFile {
 205                text: settings::vim_keymap(),
 206                title: "Default Vim Bindings",
 207                language: "JSON",
 208            });
 209        });
 210
 211        workspace.register_action(|workspace, _: &ResetPaneSizes, _, cx| {
 212            workspace.reset_pane_sizes(cx);
 213        });
 214
 215        workspace.register_action(|workspace, _: &MaximizePane, window, cx| {
 216            let pane = workspace.active_pane();
 217            let Some(size) = workspace.bounding_box_for_pane(&pane) else {
 218                return;
 219            };
 220
 221            let theme = ThemeSettings::get_global(cx);
 222            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 223
 224            let desired_size = if let Some(count) = Vim::take_count(cx) {
 225                height * count
 226            } else {
 227                px(10000.)
 228            };
 229            workspace.resize_pane(Axis::Vertical, desired_size - size.size.height, window, cx)
 230        });
 231
 232        workspace.register_action(|workspace, _: &ResizePaneRight, window, cx| {
 233            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 234            Vim::take_forced_motion(cx);
 235            let theme = ThemeSettings::get_global(cx);
 236            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
 237                return;
 238            };
 239            let Ok(width) = window
 240                .text_system()
 241                .advance(font_id, theme.buffer_font_size(cx), 'm')
 242            else {
 243                return;
 244            };
 245            workspace.resize_pane(Axis::Horizontal, width.width * count, window, cx);
 246        });
 247
 248        workspace.register_action(|workspace, _: &ResizePaneLeft, window, cx| {
 249            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 250            Vim::take_forced_motion(cx);
 251            let theme = ThemeSettings::get_global(cx);
 252            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
 253                return;
 254            };
 255            let Ok(width) = window
 256                .text_system()
 257                .advance(font_id, theme.buffer_font_size(cx), 'm')
 258            else {
 259                return;
 260            };
 261            workspace.resize_pane(Axis::Horizontal, -width.width * count, window, cx);
 262        });
 263
 264        workspace.register_action(|workspace, _: &ResizePaneUp, window, cx| {
 265            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 266            Vim::take_forced_motion(cx);
 267            let theme = ThemeSettings::get_global(cx);
 268            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 269            workspace.resize_pane(Axis::Vertical, height * count, window, cx);
 270        });
 271
 272        workspace.register_action(|workspace, _: &ResizePaneDown, window, cx| {
 273            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 274            Vim::take_forced_motion(cx);
 275            let theme = ThemeSettings::get_global(cx);
 276            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 277            workspace.resize_pane(Axis::Vertical, -height * count, window, cx);
 278        });
 279
 280        workspace.register_action(|workspace, _: &SearchSubmit, window, cx| {
 281            let vim = workspace
 282                .focused_pane(window, cx)
 283                .read(cx)
 284                .active_item()
 285                .and_then(|item| item.act_as::<Editor>(cx))
 286                .and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned());
 287            let Some(vim) = vim else { return };
 288            vim.entity.update(cx, |_, cx| {
 289                cx.defer_in(window, |vim, window, cx| vim.search_submit(window, cx))
 290            })
 291        });
 292    })
 293    .detach();
 294}
 295
 296#[derive(Clone)]
 297pub(crate) struct VimAddon {
 298    pub(crate) entity: Entity<Vim>,
 299}
 300
 301impl editor::Addon for VimAddon {
 302    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &App) {
 303        self.entity.read(cx).extend_key_context(key_context, cx)
 304    }
 305
 306    fn to_any(&self) -> &dyn std::any::Any {
 307        self
 308    }
 309}
 310
 311/// The state pertaining to Vim mode.
 312pub(crate) struct Vim {
 313    pub(crate) mode: Mode,
 314    pub last_mode: Mode,
 315    pub temp_mode: bool,
 316    pub status_label: Option<SharedString>,
 317    pub exit_temporary_mode: bool,
 318
 319    operator_stack: Vec<Operator>,
 320    pub(crate) replacements: Vec<(Range<editor::Anchor>, String)>,
 321
 322    pub(crate) stored_visual_mode: Option<(Mode, Vec<bool>)>,
 323
 324    pub(crate) current_tx: Option<TransactionId>,
 325    pub(crate) current_anchor: Option<Selection<Anchor>>,
 326    pub(crate) undo_modes: HashMap<TransactionId, Mode>,
 327
 328    selected_register: Option<char>,
 329    pub search: SearchState,
 330
 331    editor: WeakEntity<Editor>,
 332
 333    last_command: Option<String>,
 334    running_command: Option<Task<()>>,
 335    _subscriptions: Vec<Subscription>,
 336}
 337
 338// Hack: Vim intercepts events dispatched to a window and updates the view in response.
 339// This means it needs a VisualContext. The easiest way to satisfy that constraint is
 340// to make Vim a "View" that is just never actually rendered.
 341impl Render for Vim {
 342    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 343        gpui::Empty
 344    }
 345}
 346
 347enum VimEvent {
 348    Focused,
 349}
 350impl EventEmitter<VimEvent> for Vim {}
 351
 352impl Vim {
 353    /// The namespace for Vim actions.
 354    const NAMESPACE: &'static str = "vim";
 355
 356    pub fn new(window: &mut Window, cx: &mut Context<Editor>) -> Entity<Self> {
 357        let editor = cx.entity().clone();
 358
 359        let mut initial_mode = VimSettings::get_global(cx).default_mode;
 360        if initial_mode == Mode::Normal && HelixModeSetting::get_global(cx).0 {
 361            initial_mode = Mode::HelixNormal;
 362        }
 363
 364        cx.new(|cx| Vim {
 365            mode: initial_mode,
 366            last_mode: Mode::Normal,
 367            temp_mode: false,
 368            exit_temporary_mode: false,
 369            operator_stack: Vec::new(),
 370            replacements: Vec::new(),
 371
 372            stored_visual_mode: None,
 373            current_tx: None,
 374            current_anchor: None,
 375            undo_modes: HashMap::default(),
 376
 377            status_label: None,
 378            selected_register: None,
 379            search: SearchState::default(),
 380
 381            last_command: None,
 382            running_command: None,
 383
 384            editor: editor.downgrade(),
 385            _subscriptions: vec![
 386                cx.observe_keystrokes(Self::observe_keystrokes),
 387                cx.subscribe_in(&editor, window, |this, _, event, window, cx| {
 388                    this.handle_editor_event(event, window, cx)
 389                }),
 390            ],
 391        })
 392    }
 393
 394    fn register(editor: &mut Editor, window: Option<&mut Window>, cx: &mut Context<Editor>) {
 395        let Some(window) = window else {
 396            return;
 397        };
 398
 399        if !editor.use_modal_editing() {
 400            return;
 401        }
 402
 403        let mut was_enabled = Vim::enabled(cx);
 404        let mut was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 405        cx.observe_global_in::<SettingsStore>(window, move |editor, window, cx| {
 406            let enabled = Vim::enabled(cx);
 407            let toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 408            if enabled && was_enabled && (toggle != was_toggle) {
 409                if toggle {
 410                    let is_relative = editor
 411                        .addon::<VimAddon>()
 412                        .map(|vim| vim.entity.read(cx).mode != Mode::Insert);
 413                    editor.set_relative_line_number(is_relative, cx)
 414                } else {
 415                    editor.set_relative_line_number(None, cx)
 416                }
 417            }
 418            was_toggle = VimSettings::get_global(cx).toggle_relative_line_numbers;
 419            if was_enabled == enabled {
 420                return;
 421            }
 422            was_enabled = enabled;
 423            if enabled {
 424                Self::activate(editor, window, cx)
 425            } else {
 426                Self::deactivate(editor, cx)
 427            }
 428        })
 429        .detach();
 430        if was_enabled {
 431            Self::activate(editor, window, cx)
 432        }
 433    }
 434
 435    fn activate(editor: &mut Editor, window: &mut Window, cx: &mut Context<Editor>) {
 436        let vim = Vim::new(window, cx);
 437
 438        if !editor.mode().is_full() {
 439            vim.update(cx, |vim, _| {
 440                vim.mode = Mode::Insert;
 441            });
 442        }
 443
 444        editor.register_addon(VimAddon {
 445            entity: vim.clone(),
 446        });
 447
 448        vim.update(cx, |_, cx| {
 449            Vim::action(editor, cx, |vim, _: &SwitchToNormalMode, window, cx| {
 450                if HelixModeSetting::get_global(cx).0 {
 451                    vim.switch_mode(Mode::HelixNormal, false, window, cx)
 452                } else {
 453                    vim.switch_mode(Mode::Normal, false, window, cx)
 454                }
 455            });
 456
 457            Vim::action(editor, cx, |vim, _: &SwitchToInsertMode, window, cx| {
 458                vim.switch_mode(Mode::Insert, false, window, cx)
 459            });
 460
 461            Vim::action(editor, cx, |vim, _: &SwitchToReplaceMode, window, cx| {
 462                vim.switch_mode(Mode::Replace, false, window, cx)
 463            });
 464
 465            Vim::action(editor, cx, |vim, _: &SwitchToVisualMode, window, cx| {
 466                vim.switch_mode(Mode::Visual, false, window, cx)
 467            });
 468
 469            Vim::action(editor, cx, |vim, _: &SwitchToVisualLineMode, window, cx| {
 470                vim.switch_mode(Mode::VisualLine, false, window, cx)
 471            });
 472
 473            Vim::action(
 474                editor,
 475                cx,
 476                |vim, _: &SwitchToVisualBlockMode, window, cx| {
 477                    vim.switch_mode(Mode::VisualBlock, false, window, cx)
 478                },
 479            );
 480
 481            Vim::action(
 482                editor,
 483                cx,
 484                |vim, _: &SwitchToHelixNormalMode, window, cx| {
 485                    vim.switch_mode(Mode::HelixNormal, false, window, cx)
 486                },
 487            );
 488            Vim::action(editor, cx, |_, _: &PushForcedMotion, _, cx| {
 489                Vim::globals(cx).forced_motion = true;
 490            });
 491            Vim::action(editor, cx, |vim, action: &PushObject, window, cx| {
 492                vim.push_operator(
 493                    Operator::Object {
 494                        around: action.around,
 495                    },
 496                    window,
 497                    cx,
 498                )
 499            });
 500
 501            Vim::action(editor, cx, |vim, action: &PushFindForward, window, cx| {
 502                vim.push_operator(
 503                    Operator::FindForward {
 504                        before: action.before,
 505                        multiline: action.multiline,
 506                    },
 507                    window,
 508                    cx,
 509                )
 510            });
 511
 512            Vim::action(editor, cx, |vim, action: &PushFindBackward, window, cx| {
 513                vim.push_operator(
 514                    Operator::FindBackward {
 515                        after: action.after,
 516                        multiline: action.multiline,
 517                    },
 518                    window,
 519                    cx,
 520                )
 521            });
 522
 523            Vim::action(editor, cx, |vim, action: &PushSneak, window, cx| {
 524                vim.push_operator(
 525                    Operator::Sneak {
 526                        first_char: action.first_char,
 527                    },
 528                    window,
 529                    cx,
 530                )
 531            });
 532
 533            Vim::action(editor, cx, |vim, action: &PushSneakBackward, window, cx| {
 534                vim.push_operator(
 535                    Operator::SneakBackward {
 536                        first_char: action.first_char,
 537                    },
 538                    window,
 539                    cx,
 540                )
 541            });
 542
 543            Vim::action(editor, cx, |vim, _: &PushAddSurrounds, window, cx| {
 544                vim.push_operator(Operator::AddSurrounds { target: None }, window, cx)
 545            });
 546
 547            Vim::action(
 548                editor,
 549                cx,
 550                |vim, action: &PushChangeSurrounds, window, cx| {
 551                    vim.push_operator(
 552                        Operator::ChangeSurrounds {
 553                            target: action.target,
 554                        },
 555                        window,
 556                        cx,
 557                    )
 558                },
 559            );
 560
 561            Vim::action(editor, cx, |vim, action: &PushJump, window, cx| {
 562                vim.push_operator(Operator::Jump { line: action.line }, window, cx)
 563            });
 564
 565            Vim::action(editor, cx, |vim, action: &PushDigraph, window, cx| {
 566                vim.push_operator(
 567                    Operator::Digraph {
 568                        first_char: action.first_char,
 569                    },
 570                    window,
 571                    cx,
 572                )
 573            });
 574
 575            Vim::action(editor, cx, |vim, action: &PushLiteral, window, cx| {
 576                vim.push_operator(
 577                    Operator::Literal {
 578                        prefix: action.prefix.clone(),
 579                    },
 580                    window,
 581                    cx,
 582                )
 583            });
 584
 585            Vim::action(editor, cx, |vim, _: &PushChange, window, cx| {
 586                vim.push_operator(Operator::Change, window, cx)
 587            });
 588
 589            Vim::action(editor, cx, |vim, _: &PushDelete, window, cx| {
 590                vim.push_operator(Operator::Delete, window, cx)
 591            });
 592
 593            Vim::action(editor, cx, |vim, _: &PushYank, window, cx| {
 594                vim.push_operator(Operator::Yank, window, cx)
 595            });
 596
 597            Vim::action(editor, cx, |vim, _: &PushReplace, window, cx| {
 598                vim.push_operator(Operator::Replace, window, cx)
 599            });
 600
 601            Vim::action(editor, cx, |vim, _: &PushDeleteSurrounds, window, cx| {
 602                vim.push_operator(Operator::DeleteSurrounds, window, cx)
 603            });
 604
 605            Vim::action(editor, cx, |vim, _: &PushMark, window, cx| {
 606                vim.push_operator(Operator::Mark, window, cx)
 607            });
 608
 609            Vim::action(editor, cx, |vim, _: &PushIndent, window, cx| {
 610                vim.push_operator(Operator::Indent, window, cx)
 611            });
 612
 613            Vim::action(editor, cx, |vim, _: &PushOutdent, window, cx| {
 614                vim.push_operator(Operator::Outdent, window, cx)
 615            });
 616
 617            Vim::action(editor, cx, |vim, _: &PushAutoIndent, window, cx| {
 618                vim.push_operator(Operator::AutoIndent, window, cx)
 619            });
 620
 621            Vim::action(editor, cx, |vim, _: &PushRewrap, window, cx| {
 622                vim.push_operator(Operator::Rewrap, window, cx)
 623            });
 624
 625            Vim::action(editor, cx, |vim, _: &PushShellCommand, window, cx| {
 626                vim.push_operator(Operator::ShellCommand, window, cx)
 627            });
 628
 629            Vim::action(editor, cx, |vim, _: &PushLowercase, window, cx| {
 630                vim.push_operator(Operator::Lowercase, window, cx)
 631            });
 632
 633            Vim::action(editor, cx, |vim, _: &PushUppercase, window, cx| {
 634                vim.push_operator(Operator::Uppercase, window, cx)
 635            });
 636
 637            Vim::action(editor, cx, |vim, _: &PushOppositeCase, window, cx| {
 638                vim.push_operator(Operator::OppositeCase, window, cx)
 639            });
 640
 641            Vim::action(editor, cx, |vim, _: &PushRot13, window, cx| {
 642                vim.push_operator(Operator::Rot13, window, cx)
 643            });
 644
 645            Vim::action(editor, cx, |vim, _: &PushRot47, window, cx| {
 646                vim.push_operator(Operator::Rot47, window, cx)
 647            });
 648
 649            Vim::action(editor, cx, |vim, _: &PushRegister, window, cx| {
 650                vim.push_operator(Operator::Register, window, cx)
 651            });
 652
 653            Vim::action(editor, cx, |vim, _: &PushRecordRegister, window, cx| {
 654                vim.push_operator(Operator::RecordRegister, window, cx)
 655            });
 656
 657            Vim::action(editor, cx, |vim, _: &PushReplayRegister, window, cx| {
 658                vim.push_operator(Operator::ReplayRegister, window, cx)
 659            });
 660
 661            Vim::action(
 662                editor,
 663                cx,
 664                |vim, _: &PushReplaceWithRegister, window, cx| {
 665                    vim.push_operator(Operator::ReplaceWithRegister, window, cx)
 666                },
 667            );
 668
 669            Vim::action(editor, cx, |vim, _: &Exchange, window, cx| {
 670                if vim.mode.is_visual() {
 671                    vim.exchange_visual(window, cx)
 672                } else {
 673                    vim.push_operator(Operator::Exchange, window, cx)
 674                }
 675            });
 676
 677            Vim::action(editor, cx, |vim, _: &ClearExchange, window, cx| {
 678                vim.clear_exchange(window, cx)
 679            });
 680
 681            Vim::action(editor, cx, |vim, _: &PushToggleComments, window, cx| {
 682                vim.push_operator(Operator::ToggleComments, window, cx)
 683            });
 684
 685            Vim::action(editor, cx, |vim, _: &ClearOperators, window, cx| {
 686                vim.clear_operator(window, cx)
 687            });
 688            Vim::action(editor, cx, |vim, n: &Number, window, cx| {
 689                vim.push_count_digit(n.0, window, cx);
 690            });
 691            Vim::action(editor, cx, |vim, _: &Tab, window, cx| {
 692                vim.input_ignored(" ".into(), window, cx)
 693            });
 694            Vim::action(
 695                editor,
 696                cx,
 697                |vim, action: &editor::AcceptEditPrediction, window, cx| {
 698                    vim.update_editor(window, cx, |_, editor, window, cx| {
 699                        editor.accept_edit_prediction(action, window, cx);
 700                    });
 701                    // In non-insertion modes, predictions will be hidden and instead a jump will be
 702                    // displayed (and performed by `accept_edit_prediction`). This switches to
 703                    // insert mode so that the prediction is displayed after the jump.
 704                    match vim.mode {
 705                        Mode::Replace => {}
 706                        _ => vim.switch_mode(Mode::Insert, true, window, cx),
 707                    };
 708                },
 709            );
 710            Vim::action(editor, cx, |vim, _: &Enter, window, cx| {
 711                vim.input_ignored("\n".into(), window, cx)
 712            });
 713
 714            normal::register(editor, cx);
 715            insert::register(editor, cx);
 716            helix::register(editor, cx);
 717            motion::register(editor, cx);
 718            command::register(editor, cx);
 719            replace::register(editor, cx);
 720            indent::register(editor, cx);
 721            rewrap::register(editor, cx);
 722            object::register(editor, cx);
 723            visual::register(editor, cx);
 724            change_list::register(editor, cx);
 725            digraph::register(editor, cx);
 726
 727            cx.defer_in(window, |vim, window, cx| {
 728                vim.focused(false, window, cx);
 729            })
 730        })
 731    }
 732
 733    fn deactivate(editor: &mut Editor, cx: &mut Context<Editor>) {
 734        editor.set_cursor_shape(CursorShape::Bar, cx);
 735        editor.set_clip_at_line_ends(false, cx);
 736        editor.set_collapse_matches(false);
 737        editor.set_input_enabled(true);
 738        editor.set_autoindent(true);
 739        editor.selections.line_mode = false;
 740        editor.unregister_addon::<VimAddon>();
 741        editor.set_relative_line_number(None, cx);
 742        if let Some(vim) = Vim::globals(cx).focused_vim() {
 743            if vim.entity_id() == cx.entity().entity_id() {
 744                Vim::globals(cx).focused_vim = None;
 745            }
 746        }
 747    }
 748
 749    /// Register an action on the editor.
 750    pub fn action<A: Action>(
 751        editor: &mut Editor,
 752        cx: &mut Context<Vim>,
 753        f: impl Fn(&mut Vim, &A, &mut Window, &mut Context<Vim>) + 'static,
 754    ) {
 755        let subscription = editor.register_action(cx.listener(f));
 756        cx.on_release(|_, _| drop(subscription)).detach();
 757    }
 758
 759    pub fn editor(&self) -> Option<Entity<Editor>> {
 760        self.editor.upgrade()
 761    }
 762
 763    pub fn workspace(&self, window: &mut Window) -> Option<Entity<Workspace>> {
 764        window.root::<Workspace>().flatten()
 765    }
 766
 767    pub fn pane(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Entity<Pane>> {
 768        self.workspace(window)
 769            .map(|workspace| workspace.read(cx).focused_pane(window, cx))
 770    }
 771
 772    pub fn enabled(cx: &mut App) -> bool {
 773        VimModeSetting::get_global(cx).0 || HelixModeSetting::get_global(cx).0
 774    }
 775
 776    /// Called whenever an keystroke is typed so vim can observe all actions
 777    /// and keystrokes accordingly.
 778    fn observe_keystrokes(
 779        &mut self,
 780        keystroke_event: &KeystrokeEvent,
 781        window: &mut Window,
 782        cx: &mut Context<Self>,
 783    ) {
 784        if self.exit_temporary_mode {
 785            self.exit_temporary_mode = false;
 786            // Don't switch to insert mode if the action is temporary_normal.
 787            if let Some(action) = keystroke_event.action.as_ref() {
 788                if action.as_any().downcast_ref::<TemporaryNormal>().is_some() {
 789                    return;
 790                }
 791            }
 792            self.switch_mode(Mode::Insert, false, window, cx)
 793        }
 794        if let Some(action) = keystroke_event.action.as_ref() {
 795            // Keystroke is handled by the vim system, so continue forward
 796            if action.name().starts_with("vim::") {
 797                self.update_editor(window, cx, |_, editor, _, cx| {
 798                    editor.hide_mouse_cursor(HideMouseCursorOrigin::MovementAction, cx)
 799                });
 800                return;
 801            }
 802        } else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
 803        {
 804            return;
 805        }
 806
 807        if let Some(operator) = self.active_operator() {
 808            match operator {
 809                Operator::Literal { prefix } => {
 810                    self.handle_literal_keystroke(
 811                        keystroke_event,
 812                        prefix.unwrap_or_default(),
 813                        window,
 814                        cx,
 815                    );
 816                }
 817                _ if !operator.is_waiting(self.mode) => {
 818                    self.clear_operator(window, cx);
 819                    self.stop_recording_immediately(Box::new(ClearOperators), cx)
 820                }
 821                _ => {}
 822            }
 823        }
 824    }
 825
 826    fn handle_editor_event(
 827        &mut self,
 828        event: &EditorEvent,
 829        window: &mut Window,
 830        cx: &mut Context<Self>,
 831    ) {
 832        match event {
 833            EditorEvent::Focused => self.focused(true, window, cx),
 834            EditorEvent::Blurred => self.blurred(window, cx),
 835            EditorEvent::SelectionsChanged { local: true } => {
 836                self.local_selections_changed(window, cx);
 837            }
 838            EditorEvent::InputIgnored { text } => {
 839                self.input_ignored(text.clone(), window, cx);
 840                Vim::globals(cx).observe_insertion(text, None)
 841            }
 842            EditorEvent::InputHandled {
 843                text,
 844                utf16_range_to_replace: range_to_replace,
 845            } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
 846            EditorEvent::TransactionBegun { transaction_id } => {
 847                self.transaction_begun(*transaction_id, window, cx)
 848            }
 849            EditorEvent::TransactionUndone { transaction_id } => {
 850                self.transaction_undone(transaction_id, window, cx)
 851            }
 852            EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
 853            EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
 854            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
 855            EditorEvent::PushedToNavHistory {
 856                anchor,
 857                is_deactivate,
 858            } => {
 859                self.update_editor(window, cx, |vim, editor, window, cx| {
 860                    let mark = if *is_deactivate {
 861                        "\"".to_string()
 862                    } else {
 863                        "'".to_string()
 864                    };
 865                    vim.set_mark(mark, vec![*anchor], editor.buffer(), window, cx);
 866                });
 867            }
 868            _ => {}
 869        }
 870    }
 871
 872    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
 873        if operator.starts_dot_recording() {
 874            self.start_recording(cx);
 875        }
 876        // Since these operations can only be entered with pre-operators,
 877        // we need to clear the previous operators when pushing,
 878        // so that the current stack is the most correct
 879        if matches!(
 880            operator,
 881            Operator::AddSurrounds { .. }
 882                | Operator::ChangeSurrounds { .. }
 883                | Operator::DeleteSurrounds
 884                | Operator::Exchange
 885        ) {
 886            self.operator_stack.clear();
 887        };
 888        self.operator_stack.push(operator);
 889        self.sync_vim_settings(window, cx);
 890    }
 891
 892    pub fn switch_mode(
 893        &mut self,
 894        mode: Mode,
 895        leave_selections: bool,
 896        window: &mut Window,
 897        cx: &mut Context<Self>,
 898    ) {
 899        if self.temp_mode && mode == Mode::Normal {
 900            self.temp_mode = false;
 901            self.switch_mode(Mode::Normal, leave_selections, window, cx);
 902            self.switch_mode(Mode::Insert, false, window, cx);
 903            return;
 904        } else if self.temp_mode
 905            && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
 906        {
 907            self.temp_mode = false;
 908        }
 909
 910        let last_mode = self.mode;
 911        let prior_mode = self.last_mode;
 912        let prior_tx = self.current_tx;
 913        self.status_label.take();
 914        self.last_mode = last_mode;
 915        self.mode = mode;
 916        self.operator_stack.clear();
 917        self.selected_register.take();
 918        self.cancel_running_command(window, cx);
 919        if mode == Mode::Normal || mode != last_mode {
 920            self.current_tx.take();
 921            self.current_anchor.take();
 922            self.update_editor(window, cx, |_, editor, _, _| {
 923                editor.clear_selection_drag_state();
 924            });
 925        }
 926        Vim::take_forced_motion(cx);
 927        if mode != Mode::Insert && mode != Mode::Replace {
 928            Vim::take_count(cx);
 929        }
 930
 931        // Sync editor settings like clip mode
 932        self.sync_vim_settings(window, cx);
 933
 934        if VimSettings::get_global(cx).toggle_relative_line_numbers
 935            && self.mode != self.last_mode
 936            && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
 937        {
 938            self.update_editor(window, cx, |vim, editor, _, cx| {
 939                let is_relative = vim.mode != Mode::Insert;
 940                editor.set_relative_line_number(Some(is_relative), cx)
 941            });
 942        }
 943
 944        if leave_selections {
 945            return;
 946        }
 947
 948        if !mode.is_visual() && last_mode.is_visual() {
 949            self.create_visual_marks(last_mode, window, cx);
 950        }
 951
 952        // Adjust selections
 953        self.update_editor(window, cx, |vim, editor, window, cx| {
 954            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 955            {
 956                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
 957                    Some((point, goal))
 958                })
 959            }
 960            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 961                if let Some(prior_tx) = prior_tx {
 962                    editor.group_until_transaction(prior_tx, cx)
 963                }
 964            }
 965
 966            editor.change_selections(None, window, cx, |s| {
 967                // we cheat with visual block mode and use multiple cursors.
 968                // the cost of this cheat is we need to convert back to a single
 969                // cursor whenever vim would.
 970                if last_mode == Mode::VisualBlock
 971                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 972                {
 973                    let tail = s.oldest_anchor().tail();
 974                    let head = s.newest_anchor().head();
 975                    s.select_anchor_ranges(vec![tail..head]);
 976                } else if last_mode == Mode::Insert
 977                    && prior_mode == Mode::VisualBlock
 978                    && mode != Mode::VisualBlock
 979                {
 980                    let pos = s.first_anchor().head();
 981                    s.select_anchor_ranges(vec![pos..pos])
 982                }
 983
 984                let snapshot = s.display_map();
 985                if let Some(pending) = s.pending.as_mut() {
 986                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 987                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 988                        end = snapshot
 989                            .buffer_snapshot
 990                            .clip_point(end + Point::new(0, 1), Bias::Right);
 991                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 992                    }
 993                }
 994
 995                s.move_with(|map, selection| {
 996                    if last_mode.is_visual() && !mode.is_visual() {
 997                        let mut point = selection.head();
 998                        if !selection.reversed && !selection.is_empty() {
 999                            point = movement::left(map, selection.head());
1000                        }
1001                        selection.collapse_to(point, selection.goal)
1002                    } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
1003                        selection.end = movement::right(map, selection.start);
1004                    }
1005                });
1006            })
1007        });
1008    }
1009
1010    pub fn take_count(cx: &mut App) -> Option<usize> {
1011        let global_state = cx.global_mut::<VimGlobals>();
1012        if global_state.dot_replaying {
1013            return global_state.recorded_count;
1014        }
1015
1016        let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
1017            return None;
1018        } else {
1019            Some(
1020                global_state.post_count.take().unwrap_or(1)
1021                    * global_state.pre_count.take().unwrap_or(1),
1022            )
1023        };
1024
1025        if global_state.dot_recording {
1026            global_state.recorded_count = count;
1027        }
1028        count
1029    }
1030
1031    pub fn take_forced_motion(cx: &mut App) -> bool {
1032        let global_state = cx.global_mut::<VimGlobals>();
1033        let forced_motion = global_state.forced_motion;
1034        global_state.forced_motion = false;
1035        forced_motion
1036    }
1037
1038    pub fn cursor_shape(&self, cx: &mut App) -> CursorShape {
1039        let cursor_shape = VimSettings::get_global(cx).cursor_shape;
1040        match self.mode {
1041            Mode::Normal => {
1042                if let Some(operator) = self.operator_stack.last() {
1043                    match operator {
1044                        // Navigation operators -> Block cursor
1045                        Operator::FindForward { .. }
1046                        | Operator::FindBackward { .. }
1047                        | Operator::Mark
1048                        | Operator::Jump { .. }
1049                        | Operator::Register
1050                        | Operator::RecordRegister
1051                        | Operator::ReplayRegister => CursorShape::Block,
1052
1053                        // All other operators -> Underline cursor
1054                        _ => CursorShape::Underline,
1055                    }
1056                } else {
1057                    cursor_shape.normal.unwrap_or(CursorShape::Block)
1058                }
1059            }
1060            Mode::HelixNormal => cursor_shape.normal.unwrap_or(CursorShape::Block),
1061            Mode::Replace => cursor_shape.replace.unwrap_or(CursorShape::Underline),
1062            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1063                cursor_shape.visual.unwrap_or(CursorShape::Block)
1064            }
1065            Mode::Insert => cursor_shape.insert.unwrap_or({
1066                let editor_settings = EditorSettings::get_global(cx);
1067                editor_settings.cursor_shape.unwrap_or_default()
1068            }),
1069        }
1070    }
1071
1072    pub fn editor_input_enabled(&self) -> bool {
1073        match self.mode {
1074            Mode::Insert => {
1075                if let Some(operator) = self.operator_stack.last() {
1076                    !operator.is_waiting(self.mode)
1077                } else {
1078                    true
1079                }
1080            }
1081            Mode::Normal
1082            | Mode::HelixNormal
1083            | Mode::Replace
1084            | Mode::Visual
1085            | Mode::VisualLine
1086            | Mode::VisualBlock => false,
1087        }
1088    }
1089
1090    pub fn should_autoindent(&self) -> bool {
1091        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1092    }
1093
1094    pub fn clip_at_line_ends(&self) -> bool {
1095        match self.mode {
1096            Mode::Insert
1097            | Mode::Visual
1098            | Mode::VisualLine
1099            | Mode::VisualBlock
1100            | Mode::Replace
1101            | Mode::HelixNormal => false,
1102            Mode::Normal => true,
1103        }
1104    }
1105
1106    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1107        let mut mode = match self.mode {
1108            Mode::Normal => "normal",
1109            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1110            Mode::Insert => "insert",
1111            Mode::Replace => "replace",
1112            Mode::HelixNormal => "helix_normal",
1113        }
1114        .to_string();
1115
1116        let mut operator_id = "none";
1117
1118        let active_operator = self.active_operator();
1119        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1120            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1121        {
1122            context.add("VimCount");
1123        }
1124
1125        if let Some(active_operator) = active_operator {
1126            if active_operator.is_waiting(self.mode) {
1127                if matches!(active_operator, Operator::Literal { .. }) {
1128                    mode = "literal".to_string();
1129                } else {
1130                    mode = "waiting".to_string();
1131                }
1132            } else {
1133                operator_id = active_operator.id();
1134                mode = "operator".to_string();
1135            }
1136        }
1137
1138        if mode == "normal" || mode == "visual" || mode == "operator" || mode == "helix_normal" {
1139            context.add("VimControl");
1140        }
1141        context.set("vim_mode", mode);
1142        context.set("vim_operator", operator_id);
1143    }
1144
1145    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1146        let Some(editor) = self.editor() else {
1147            return;
1148        };
1149        let newest_selection_empty = editor.update(cx, |editor, cx| {
1150            editor.selections.newest::<usize>(cx).is_empty()
1151        });
1152        let editor = editor.read(cx);
1153        let editor_mode = editor.mode();
1154
1155        if editor_mode.is_full()
1156            && !newest_selection_empty
1157            && self.mode == Mode::Normal
1158            // When following someone, don't switch vim mode.
1159            && editor.leader_id().is_none()
1160        {
1161            if preserve_selection {
1162                self.switch_mode(Mode::Visual, true, window, cx);
1163            } else {
1164                self.update_editor(window, cx, |_, editor, window, cx| {
1165                    editor.set_clip_at_line_ends(false, cx);
1166                    editor.change_selections(None, window, cx, |s| {
1167                        s.move_with(|_, selection| {
1168                            selection.collapse_to(selection.start, selection.goal)
1169                        })
1170                    });
1171                });
1172            }
1173        }
1174
1175        cx.emit(VimEvent::Focused);
1176        self.sync_vim_settings(window, cx);
1177
1178        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1179            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1180                if old_vim.entity_id() != cx.entity().entity_id() {
1181                    old_vim.update(cx, |vim, cx| {
1182                        vim.update_editor(window, cx, |_, editor, _, cx| {
1183                            editor.set_relative_line_number(None, cx)
1184                        });
1185                    });
1186
1187                    self.update_editor(window, cx, |vim, editor, _, cx| {
1188                        let is_relative = vim.mode != Mode::Insert;
1189                        editor.set_relative_line_number(Some(is_relative), cx)
1190                    });
1191                }
1192            } else {
1193                self.update_editor(window, cx, |vim, editor, _, cx| {
1194                    let is_relative = vim.mode != Mode::Insert;
1195                    editor.set_relative_line_number(Some(is_relative), cx)
1196                });
1197            }
1198        }
1199        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1200    }
1201
1202    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1203        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1204        self.store_visual_marks(window, cx);
1205        self.clear_operator(window, cx);
1206        self.update_editor(window, cx, |vim, editor, _, cx| {
1207            if vim.cursor_shape(cx) == CursorShape::Block {
1208                editor.set_cursor_shape(CursorShape::Hollow, cx);
1209            }
1210        });
1211    }
1212
1213    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1214        self.update_editor(window, cx, |vim, editor, _, cx| {
1215            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1216        });
1217    }
1218
1219    fn update_editor<S>(
1220        &mut self,
1221        window: &mut Window,
1222        cx: &mut Context<Self>,
1223        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1224    ) -> Option<S> {
1225        let editor = self.editor.upgrade()?;
1226        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1227    }
1228
1229    fn editor_selections(
1230        &mut self,
1231        window: &mut Window,
1232        cx: &mut Context<Self>,
1233    ) -> Vec<Range<Anchor>> {
1234        self.update_editor(window, cx, |_, editor, _, _| {
1235            editor
1236                .selections
1237                .disjoint_anchors()
1238                .iter()
1239                .map(|selection| selection.tail()..selection.head())
1240                .collect()
1241        })
1242        .unwrap_or_default()
1243    }
1244
1245    fn editor_cursor_word(
1246        &mut self,
1247        window: &mut Window,
1248        cx: &mut Context<Self>,
1249    ) -> Option<String> {
1250        self.update_editor(window, cx, |_, editor, window, cx| {
1251            let selection = editor.selections.newest::<usize>(cx);
1252
1253            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1254            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1255            if kind == Some(CharKind::Word) {
1256                let text: String = snapshot.text_for_range(range).collect();
1257                if !text.trim().is_empty() {
1258                    return Some(text);
1259                }
1260            }
1261
1262            None
1263        })
1264        .unwrap_or_default()
1265    }
1266
1267    /// When doing an action that modifies the buffer, we start recording so that `.`
1268    /// will replay the action.
1269    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1270        Vim::update_globals(cx, |globals, cx| {
1271            if !globals.dot_replaying {
1272                globals.dot_recording = true;
1273                globals.recording_actions = Default::default();
1274                globals.recorded_count = None;
1275
1276                let selections = self.editor().map(|editor| {
1277                    editor.update(cx, |editor, cx| {
1278                        (
1279                            editor.selections.oldest::<Point>(cx),
1280                            editor.selections.newest::<Point>(cx),
1281                        )
1282                    })
1283                });
1284
1285                if let Some((oldest, newest)) = selections {
1286                    globals.recorded_selection = match self.mode {
1287                        Mode::Visual if newest.end.row == newest.start.row => {
1288                            RecordedSelection::SingleLine {
1289                                cols: newest.end.column - newest.start.column,
1290                            }
1291                        }
1292                        Mode::Visual => RecordedSelection::Visual {
1293                            rows: newest.end.row - newest.start.row,
1294                            cols: newest.end.column,
1295                        },
1296                        Mode::VisualLine => RecordedSelection::VisualLine {
1297                            rows: newest.end.row - newest.start.row,
1298                        },
1299                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1300                            rows: newest.end.row.abs_diff(oldest.start.row),
1301                            cols: newest.end.column.abs_diff(oldest.start.column),
1302                        },
1303                        _ => RecordedSelection::None,
1304                    }
1305                } else {
1306                    globals.recorded_selection = RecordedSelection::None;
1307                }
1308            }
1309        })
1310    }
1311
1312    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1313        let globals = Vim::globals(cx);
1314        globals.dot_replaying = false;
1315        if let Some(replayer) = globals.replayer.take() {
1316            replayer.stop();
1317        }
1318    }
1319
1320    /// When finishing an action that modifies the buffer, stop recording.
1321    /// as you usually call this within a keystroke handler we also ensure that
1322    /// the current action is recorded.
1323    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1324        let globals = Vim::globals(cx);
1325        if globals.dot_recording {
1326            globals.stop_recording_after_next_action = true;
1327        }
1328        self.exit_temporary_mode = self.temp_mode;
1329    }
1330
1331    /// Stops recording actions immediately rather than waiting until after the
1332    /// next action to stop recording.
1333    ///
1334    /// This doesn't include the current action.
1335    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1336        let globals = Vim::globals(cx);
1337        if globals.dot_recording {
1338            globals
1339                .recording_actions
1340                .push(ReplayableAction::Action(action.boxed_clone()));
1341            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1342            globals.dot_recording = false;
1343            globals.stop_recording_after_next_action = false;
1344        }
1345        self.exit_temporary_mode = self.temp_mode;
1346    }
1347
1348    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1349    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1350        self.start_recording(cx);
1351        self.stop_recording(cx);
1352    }
1353
1354    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1355        if self.active_operator().is_some() {
1356            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1357
1358            Vim::globals(cx).post_count = Some(
1359                post_count
1360                    .checked_mul(10)
1361                    .and_then(|post_count| post_count.checked_add(number))
1362                    .unwrap_or(post_count),
1363            )
1364        } else {
1365            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1366
1367            Vim::globals(cx).pre_count = Some(
1368                pre_count
1369                    .checked_mul(10)
1370                    .and_then(|pre_count| pre_count.checked_add(number))
1371                    .unwrap_or(pre_count),
1372            )
1373        }
1374        // update the keymap so that 0 works
1375        self.sync_vim_settings(window, cx)
1376    }
1377
1378    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1379        if register.chars().count() == 1 {
1380            self.selected_register
1381                .replace(register.chars().next().unwrap());
1382        }
1383        self.operator_stack.clear();
1384        self.sync_vim_settings(window, cx);
1385    }
1386
1387    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1388        self.operator_stack.pop()
1389    }
1390
1391    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1392        let popped_operator = self.operator_stack.pop()
1393            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1394        self.sync_vim_settings(window, cx);
1395        popped_operator
1396    }
1397
1398    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1399        Vim::take_count(cx);
1400        Vim::take_forced_motion(cx);
1401        self.selected_register.take();
1402        self.operator_stack.clear();
1403        self.sync_vim_settings(window, cx);
1404    }
1405
1406    fn active_operator(&self) -> Option<Operator> {
1407        self.operator_stack.last().cloned()
1408    }
1409
1410    fn transaction_begun(
1411        &mut self,
1412        transaction_id: TransactionId,
1413        _window: &mut Window,
1414        _: &mut Context<Self>,
1415    ) {
1416        let mode = if (self.mode == Mode::Insert
1417            || self.mode == Mode::Replace
1418            || self.mode == Mode::Normal)
1419            && self.current_tx.is_none()
1420        {
1421            self.current_tx = Some(transaction_id);
1422            self.last_mode
1423        } else {
1424            self.mode
1425        };
1426        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1427            self.undo_modes.insert(transaction_id, mode);
1428        }
1429    }
1430
1431    fn transaction_undone(
1432        &mut self,
1433        transaction_id: &TransactionId,
1434        window: &mut Window,
1435        cx: &mut Context<Self>,
1436    ) {
1437        match self.mode {
1438            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1439                self.update_editor(window, cx, |vim, editor, window, cx| {
1440                    let original_mode = vim.undo_modes.get(transaction_id);
1441                    editor.change_selections(None, window, cx, |s| match original_mode {
1442                        Some(Mode::VisualLine) => {
1443                            s.move_with(|map, selection| {
1444                                selection.collapse_to(
1445                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1446                                    SelectionGoal::None,
1447                                )
1448                            });
1449                        }
1450                        Some(Mode::VisualBlock) => {
1451                            let mut first = s.first_anchor();
1452                            first.collapse_to(first.start, first.goal);
1453                            s.select_anchors(vec![first]);
1454                        }
1455                        _ => {
1456                            s.move_with(|map, selection| {
1457                                selection.collapse_to(
1458                                    map.clip_at_line_end(selection.start),
1459                                    selection.goal,
1460                                );
1461                            });
1462                        }
1463                    });
1464                });
1465                self.switch_mode(Mode::Normal, true, window, cx)
1466            }
1467            Mode::Normal => {
1468                self.update_editor(window, cx, |_, editor, window, cx| {
1469                    editor.change_selections(None, window, cx, |s| {
1470                        s.move_with(|map, selection| {
1471                            selection
1472                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1473                        })
1474                    })
1475                });
1476            }
1477            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1478        }
1479    }
1480
1481    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1482        let Some(editor) = self.editor() else { return };
1483
1484        if editor.read(cx).leader_id().is_some() {
1485            return;
1486        }
1487
1488        let newest = editor.read(cx).selections.newest_anchor().clone();
1489        let is_multicursor = editor.read(cx).selections.count() > 1;
1490        if self.mode == Mode::Insert && self.current_tx.is_some() {
1491            if self.current_anchor.is_none() {
1492                self.current_anchor = Some(newest);
1493            } else if self.current_anchor.as_ref().unwrap() != &newest {
1494                if let Some(tx_id) = self.current_tx.take() {
1495                    self.update_editor(window, cx, |_, editor, _, cx| {
1496                        editor.group_until_transaction(tx_id, cx)
1497                    });
1498                }
1499            }
1500        } else if self.mode == Mode::Normal && newest.start != newest.end {
1501            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1502                self.switch_mode(Mode::VisualBlock, false, window, cx);
1503            } else {
1504                self.switch_mode(Mode::Visual, false, window, cx)
1505            }
1506        } else if newest.start == newest.end
1507            && !is_multicursor
1508            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1509        {
1510            self.switch_mode(Mode::Normal, true, window, cx);
1511        }
1512    }
1513
1514    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1515        if text.is_empty() {
1516            return;
1517        }
1518
1519        match self.active_operator() {
1520            Some(Operator::FindForward { before, multiline }) => {
1521                let find = Motion::FindForward {
1522                    before,
1523                    char: text.chars().next().unwrap(),
1524                    mode: if multiline {
1525                        FindRange::MultiLine
1526                    } else {
1527                        FindRange::SingleLine
1528                    },
1529                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1530                };
1531                Vim::globals(cx).last_find = Some(find.clone());
1532                self.motion(find, window, cx)
1533            }
1534            Some(Operator::FindBackward { after, multiline }) => {
1535                let find = Motion::FindBackward {
1536                    after,
1537                    char: text.chars().next().unwrap(),
1538                    mode: if multiline {
1539                        FindRange::MultiLine
1540                    } else {
1541                        FindRange::SingleLine
1542                    },
1543                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1544                };
1545                Vim::globals(cx).last_find = Some(find.clone());
1546                self.motion(find, window, cx)
1547            }
1548            Some(Operator::Sneak { first_char }) => {
1549                if let Some(first_char) = first_char {
1550                    if let Some(second_char) = text.chars().next() {
1551                        let sneak = Motion::Sneak {
1552                            first_char,
1553                            second_char,
1554                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1555                        };
1556                        Vim::globals(cx).last_find = Some((&sneak).clone());
1557                        self.motion(sneak, window, cx)
1558                    }
1559                } else {
1560                    let first_char = text.chars().next();
1561                    self.pop_operator(window, cx);
1562                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1563                }
1564            }
1565            Some(Operator::SneakBackward { first_char }) => {
1566                if let Some(first_char) = first_char {
1567                    if let Some(second_char) = text.chars().next() {
1568                        let sneak = Motion::SneakBackward {
1569                            first_char,
1570                            second_char,
1571                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1572                        };
1573                        Vim::globals(cx).last_find = Some((&sneak).clone());
1574                        self.motion(sneak, window, cx)
1575                    }
1576                } else {
1577                    let first_char = text.chars().next();
1578                    self.pop_operator(window, cx);
1579                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1580                }
1581            }
1582            Some(Operator::Replace) => match self.mode {
1583                Mode::Normal => self.normal_replace(text, window, cx),
1584                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1585                    self.visual_replace(text, window, cx)
1586                }
1587                _ => self.clear_operator(window, cx),
1588            },
1589            Some(Operator::Digraph { first_char }) => {
1590                if let Some(first_char) = first_char {
1591                    if let Some(second_char) = text.chars().next() {
1592                        self.insert_digraph(first_char, second_char, window, cx);
1593                    }
1594                } else {
1595                    let first_char = text.chars().next();
1596                    self.pop_operator(window, cx);
1597                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1598                }
1599            }
1600            Some(Operator::Literal { prefix }) => {
1601                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1602            }
1603            Some(Operator::AddSurrounds { target }) => match self.mode {
1604                Mode::Normal => {
1605                    if let Some(target) = target {
1606                        self.add_surrounds(text, target, window, cx);
1607                        self.clear_operator(window, cx);
1608                    }
1609                }
1610                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1611                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1612                    self.clear_operator(window, cx);
1613                }
1614                _ => self.clear_operator(window, cx),
1615            },
1616            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1617                Mode::Normal => {
1618                    if let Some(target) = target {
1619                        self.change_surrounds(text, target, window, cx);
1620                        self.clear_operator(window, cx);
1621                    }
1622                }
1623                _ => self.clear_operator(window, cx),
1624            },
1625            Some(Operator::DeleteSurrounds) => match self.mode {
1626                Mode::Normal => {
1627                    self.delete_surrounds(text, window, cx);
1628                    self.clear_operator(window, cx);
1629                }
1630                _ => self.clear_operator(window, cx),
1631            },
1632            Some(Operator::Mark) => self.create_mark(text, window, cx),
1633            Some(Operator::RecordRegister) => {
1634                self.record_register(text.chars().next().unwrap(), window, cx)
1635            }
1636            Some(Operator::ReplayRegister) => {
1637                self.replay_register(text.chars().next().unwrap(), window, cx)
1638            }
1639            Some(Operator::Register) => match self.mode {
1640                Mode::Insert => {
1641                    self.update_editor(window, cx, |_, editor, window, cx| {
1642                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1643                            globals.read_register(text.chars().next(), Some(editor), cx)
1644                        }) {
1645                            editor.do_paste(
1646                                &register.text.to_string(),
1647                                register.clipboard_selections.clone(),
1648                                false,
1649                                window,
1650                                cx,
1651                            )
1652                        }
1653                    });
1654                    self.clear_operator(window, cx);
1655                }
1656                _ => {
1657                    self.select_register(text, window, cx);
1658                }
1659            },
1660            Some(Operator::Jump { line }) => self.jump(text, line, true, window, cx),
1661            _ => {
1662                if self.mode == Mode::Replace {
1663                    self.multi_replace(text, window, cx)
1664                }
1665
1666                if self.mode == Mode::Normal {
1667                    self.update_editor(window, cx, |_, editor, window, cx| {
1668                        editor.accept_edit_prediction(
1669                            &editor::actions::AcceptEditPrediction {},
1670                            window,
1671                            cx,
1672                        );
1673                    });
1674                }
1675            }
1676        }
1677    }
1678
1679    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1680        self.update_editor(window, cx, |vim, editor, window, cx| {
1681            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1682            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1683            editor.set_collapse_matches(true);
1684            editor.set_input_enabled(vim.editor_input_enabled());
1685            editor.set_autoindent(vim.should_autoindent());
1686            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1687
1688            let hide_inline_completions = match vim.mode {
1689                Mode::Insert | Mode::Replace => false,
1690                _ => true,
1691            };
1692            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1693        });
1694        cx.notify()
1695    }
1696}
1697
1698/// Controls when to use system clipboard.
1699#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1700#[serde(rename_all = "snake_case")]
1701pub enum UseSystemClipboard {
1702    /// Don't use system clipboard.
1703    Never,
1704    /// Use system clipboard.
1705    Always,
1706    /// Use system clipboard for yank operations.
1707    OnYank,
1708}
1709
1710/// The settings for cursor shape.
1711#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1712struct CursorShapeSettings {
1713    /// Cursor shape for the normal mode.
1714    ///
1715    /// Default: block
1716    pub normal: Option<CursorShape>,
1717    /// Cursor shape for the replace mode.
1718    ///
1719    /// Default: underline
1720    pub replace: Option<CursorShape>,
1721    /// Cursor shape for the visual mode.
1722    ///
1723    /// Default: block
1724    pub visual: Option<CursorShape>,
1725    /// Cursor shape for the insert mode.
1726    ///
1727    /// The default value follows the primary cursor_shape.
1728    pub insert: Option<CursorShape>,
1729}
1730
1731#[derive(Deserialize)]
1732struct VimSettings {
1733    pub default_mode: Mode,
1734    pub toggle_relative_line_numbers: bool,
1735    pub use_system_clipboard: UseSystemClipboard,
1736    pub use_smartcase_find: bool,
1737    pub custom_digraphs: HashMap<String, Arc<str>>,
1738    pub highlight_on_yank_duration: u64,
1739    pub cursor_shape: CursorShapeSettings,
1740}
1741
1742#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1743struct VimSettingsContent {
1744    pub default_mode: Option<ModeContent>,
1745    pub toggle_relative_line_numbers: Option<bool>,
1746    pub use_system_clipboard: Option<UseSystemClipboard>,
1747    pub use_smartcase_find: Option<bool>,
1748    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1749    pub highlight_on_yank_duration: Option<u64>,
1750    pub cursor_shape: Option<CursorShapeSettings>,
1751}
1752
1753#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1754#[serde(rename_all = "snake_case")]
1755pub enum ModeContent {
1756    #[default]
1757    Normal,
1758    Insert,
1759    Replace,
1760    Visual,
1761    VisualLine,
1762    VisualBlock,
1763    HelixNormal,
1764}
1765
1766impl From<ModeContent> for Mode {
1767    fn from(mode: ModeContent) -> Self {
1768        match mode {
1769            ModeContent::Normal => Self::Normal,
1770            ModeContent::Insert => Self::Insert,
1771            ModeContent::Replace => Self::Replace,
1772            ModeContent::Visual => Self::Visual,
1773            ModeContent::VisualLine => Self::VisualLine,
1774            ModeContent::VisualBlock => Self::VisualBlock,
1775            ModeContent::HelixNormal => Self::HelixNormal,
1776        }
1777    }
1778}
1779
1780impl Settings for VimSettings {
1781    const KEY: Option<&'static str> = Some("vim");
1782
1783    type FileContent = VimSettingsContent;
1784
1785    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1786        let settings: VimSettingsContent = sources.json_merge()?;
1787
1788        Ok(Self {
1789            default_mode: settings
1790                .default_mode
1791                .ok_or_else(Self::missing_default)?
1792                .into(),
1793            toggle_relative_line_numbers: settings
1794                .toggle_relative_line_numbers
1795                .ok_or_else(Self::missing_default)?,
1796            use_system_clipboard: settings
1797                .use_system_clipboard
1798                .ok_or_else(Self::missing_default)?,
1799            use_smartcase_find: settings
1800                .use_smartcase_find
1801                .ok_or_else(Self::missing_default)?,
1802            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1803            highlight_on_yank_duration: settings
1804                .highlight_on_yank_duration
1805                .ok_or_else(Self::missing_default)?,
1806            cursor_shape: settings.cursor_shape.ok_or_else(Self::missing_default)?,
1807        })
1808    }
1809
1810    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {
1811        // TODO: translate vim extension settings
1812    }
1813}