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