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