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(vim.default_mode(cx), 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 default_mode(&self, cx: &App) -> Mode {
 743        VimSettings::get_global(cx).default_mode
 744    }
 745
 746    pub fn editor(&self) -> Option<Entity<Editor>> {
 747        self.editor.upgrade()
 748    }
 749
 750    pub fn workspace(&self, window: &mut Window) -> Option<Entity<Workspace>> {
 751        window.root::<Workspace>().flatten()
 752    }
 753
 754    pub fn pane(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Entity<Pane>> {
 755        self.workspace(window)
 756            .map(|workspace| workspace.read(cx).focused_pane(window, cx))
 757    }
 758
 759    pub fn enabled(cx: &mut App) -> bool {
 760        VimModeSetting::get_global(cx).0
 761    }
 762
 763    /// Called whenever an keystroke is typed so vim can observe all actions
 764    /// and keystrokes accordingly.
 765    fn observe_keystrokes(
 766        &mut self,
 767        keystroke_event: &KeystrokeEvent,
 768        window: &mut Window,
 769        cx: &mut Context<Self>,
 770    ) {
 771        if self.exit_temporary_mode {
 772            self.exit_temporary_mode = false;
 773            // Don't switch to insert mode if the action is temporary_normal.
 774            if let Some(action) = keystroke_event.action.as_ref() {
 775                if action.as_any().downcast_ref::<TemporaryNormal>().is_some() {
 776                    return;
 777                }
 778            }
 779            self.switch_mode(Mode::Insert, false, window, cx)
 780        }
 781        if let Some(action) = keystroke_event.action.as_ref() {
 782            // Keystroke is handled by the vim system, so continue forward
 783            if action.name().starts_with("vim::") {
 784                self.update_editor(window, cx, |_, editor, _, _| {
 785                    editor.hide_mouse_cursor(&HideMouseCursorOrigin::MovementAction)
 786                });
 787                return;
 788            }
 789        } else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
 790        {
 791            return;
 792        }
 793
 794        if let Some(operator) = self.active_operator() {
 795            match operator {
 796                Operator::Literal { prefix } => {
 797                    self.handle_literal_keystroke(
 798                        keystroke_event,
 799                        prefix.unwrap_or_default(),
 800                        window,
 801                        cx,
 802                    );
 803                }
 804                _ if !operator.is_waiting(self.mode) => {
 805                    self.clear_operator(window, cx);
 806                    self.stop_recording_immediately(Box::new(ClearOperators), cx)
 807                }
 808                _ => {}
 809            }
 810        }
 811    }
 812
 813    fn handle_editor_event(
 814        &mut self,
 815        event: &EditorEvent,
 816        window: &mut Window,
 817        cx: &mut Context<Self>,
 818    ) {
 819        match event {
 820            EditorEvent::Focused => self.focused(true, window, cx),
 821            EditorEvent::Blurred => self.blurred(window, cx),
 822            EditorEvent::SelectionsChanged { local: true } => {
 823                self.local_selections_changed(window, cx);
 824            }
 825            EditorEvent::InputIgnored { text } => {
 826                self.input_ignored(text.clone(), window, cx);
 827                Vim::globals(cx).observe_insertion(text, None)
 828            }
 829            EditorEvent::InputHandled {
 830                text,
 831                utf16_range_to_replace: range_to_replace,
 832            } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
 833            EditorEvent::TransactionBegun { transaction_id } => {
 834                self.transaction_begun(*transaction_id, window, cx)
 835            }
 836            EditorEvent::TransactionUndone { transaction_id } => {
 837                self.transaction_undone(transaction_id, window, cx)
 838            }
 839            EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
 840            EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
 841            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
 842            EditorEvent::PushedToNavHistory {
 843                anchor,
 844                is_deactivate,
 845            } => {
 846                self.update_editor(window, cx, |vim, editor, window, cx| {
 847                    let mark = if *is_deactivate {
 848                        "\"".to_string()
 849                    } else {
 850                        "'".to_string()
 851                    };
 852                    vim.set_mark(mark, vec![*anchor], editor.buffer(), window, cx);
 853                });
 854            }
 855            _ => {}
 856        }
 857    }
 858
 859    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
 860        if operator.starts_dot_recording() {
 861            self.start_recording(cx);
 862        }
 863        // Since these operations can only be entered with pre-operators,
 864        // we need to clear the previous operators when pushing,
 865        // so that the current stack is the most correct
 866        if matches!(
 867            operator,
 868            Operator::AddSurrounds { .. }
 869                | Operator::ChangeSurrounds { .. }
 870                | Operator::DeleteSurrounds
 871                | Operator::Exchange
 872        ) {
 873            self.operator_stack.clear();
 874        };
 875        self.operator_stack.push(operator);
 876        self.sync_vim_settings(window, cx);
 877    }
 878
 879    pub fn switch_mode(
 880        &mut self,
 881        mode: Mode,
 882        leave_selections: bool,
 883        window: &mut Window,
 884        cx: &mut Context<Self>,
 885    ) {
 886        if self.temp_mode && mode == Mode::Normal {
 887            self.temp_mode = false;
 888            self.switch_mode(Mode::Normal, leave_selections, window, cx);
 889            self.switch_mode(Mode::Insert, false, window, cx);
 890            return;
 891        } else if self.temp_mode
 892            && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
 893        {
 894            self.temp_mode = false;
 895        }
 896
 897        let last_mode = self.mode;
 898        let prior_mode = self.last_mode;
 899        let prior_tx = self.current_tx;
 900        self.status_label.take();
 901        self.last_mode = last_mode;
 902        self.mode = mode;
 903        self.operator_stack.clear();
 904        self.selected_register.take();
 905        self.cancel_running_command(window, cx);
 906        if mode == Mode::Normal || mode != last_mode {
 907            self.current_tx.take();
 908            self.current_anchor.take();
 909        }
 910        if mode != Mode::Insert && mode != Mode::Replace {
 911            Vim::take_count(cx);
 912        }
 913
 914        // Sync editor settings like clip mode
 915        self.sync_vim_settings(window, cx);
 916
 917        if VimSettings::get_global(cx).toggle_relative_line_numbers
 918            && self.mode != self.last_mode
 919            && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
 920        {
 921            self.update_editor(window, cx, |vim, editor, _, cx| {
 922                let is_relative = vim.mode != Mode::Insert;
 923                editor.set_relative_line_number(Some(is_relative), cx)
 924            });
 925        }
 926
 927        if leave_selections {
 928            return;
 929        }
 930
 931        if !mode.is_visual() && last_mode.is_visual() {
 932            self.create_visual_marks(last_mode, window, cx);
 933        }
 934
 935        // Adjust selections
 936        self.update_editor(window, cx, |vim, editor, window, cx| {
 937            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 938            {
 939                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
 940                    Some((point, goal))
 941                })
 942            }
 943            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 944                if let Some(prior_tx) = prior_tx {
 945                    editor.group_until_transaction(prior_tx, cx)
 946                }
 947            }
 948
 949            editor.change_selections(None, window, cx, |s| {
 950                // we cheat with visual block mode and use multiple cursors.
 951                // the cost of this cheat is we need to convert back to a single
 952                // cursor whenever vim would.
 953                if last_mode == Mode::VisualBlock
 954                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 955                {
 956                    let tail = s.oldest_anchor().tail();
 957                    let head = s.newest_anchor().head();
 958                    s.select_anchor_ranges(vec![tail..head]);
 959                } else if last_mode == Mode::Insert
 960                    && prior_mode == Mode::VisualBlock
 961                    && mode != Mode::VisualBlock
 962                {
 963                    let pos = s.first_anchor().head();
 964                    s.select_anchor_ranges(vec![pos..pos])
 965                }
 966
 967                let snapshot = s.display_map();
 968                if let Some(pending) = s.pending.as_mut() {
 969                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 970                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 971                        end = snapshot
 972                            .buffer_snapshot
 973                            .clip_point(end + Point::new(0, 1), Bias::Right);
 974                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 975                    }
 976                }
 977
 978                s.move_with(|map, selection| {
 979                    if last_mode.is_visual() && !mode.is_visual() {
 980                        let mut point = selection.head();
 981                        if !selection.reversed && !selection.is_empty() {
 982                            point = movement::left(map, selection.head());
 983                        }
 984                        selection.collapse_to(point, selection.goal)
 985                    } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
 986                        selection.end = movement::right(map, selection.start);
 987                    }
 988                });
 989            })
 990        });
 991    }
 992
 993    pub fn take_count(cx: &mut App) -> Option<usize> {
 994        let global_state = cx.global_mut::<VimGlobals>();
 995        if global_state.dot_replaying {
 996            return global_state.recorded_count;
 997        }
 998
 999        let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
1000            return None;
1001        } else {
1002            Some(
1003                global_state.post_count.take().unwrap_or(1)
1004                    * global_state.pre_count.take().unwrap_or(1),
1005            )
1006        };
1007
1008        if global_state.dot_recording {
1009            global_state.recorded_count = count;
1010        }
1011        count
1012    }
1013
1014    pub fn cursor_shape(&self, cx: &mut App) -> CursorShape {
1015        match self.mode {
1016            Mode::Normal => {
1017                if let Some(operator) = self.operator_stack.last() {
1018                    match operator {
1019                        // Navigation operators -> Block cursor
1020                        Operator::FindForward { .. }
1021                        | Operator::FindBackward { .. }
1022                        | Operator::Mark
1023                        | Operator::Jump { .. }
1024                        | Operator::Register
1025                        | Operator::RecordRegister
1026                        | Operator::ReplayRegister => CursorShape::Block,
1027
1028                        // All other operators -> Underline cursor
1029                        _ => CursorShape::Underline,
1030                    }
1031                } else {
1032                    // No operator active -> Block cursor
1033                    CursorShape::Block
1034                }
1035            }
1036            Mode::Replace => CursorShape::Underline,
1037            Mode::HelixNormal | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1038                CursorShape::Block
1039            }
1040            Mode::Insert => {
1041                let editor_settings = EditorSettings::get_global(cx);
1042                editor_settings.cursor_shape.unwrap_or_default()
1043            }
1044        }
1045    }
1046
1047    pub fn editor_input_enabled(&self) -> bool {
1048        match self.mode {
1049            Mode::Insert => {
1050                if let Some(operator) = self.operator_stack.last() {
1051                    !operator.is_waiting(self.mode)
1052                } else {
1053                    true
1054                }
1055            }
1056            Mode::Normal
1057            | Mode::HelixNormal
1058            | Mode::Replace
1059            | Mode::Visual
1060            | Mode::VisualLine
1061            | Mode::VisualBlock => false,
1062        }
1063    }
1064
1065    pub fn should_autoindent(&self) -> bool {
1066        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1067    }
1068
1069    pub fn clip_at_line_ends(&self) -> bool {
1070        match self.mode {
1071            Mode::Insert
1072            | Mode::Visual
1073            | Mode::VisualLine
1074            | Mode::VisualBlock
1075            | Mode::Replace
1076            | Mode::HelixNormal => false,
1077            Mode::Normal => true,
1078        }
1079    }
1080
1081    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1082        let mut mode = match self.mode {
1083            Mode::Normal => "normal",
1084            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1085            Mode::Insert => "insert",
1086            Mode::Replace => "replace",
1087            Mode::HelixNormal => "helix_normal",
1088        }
1089        .to_string();
1090
1091        let mut operator_id = "none";
1092
1093        let active_operator = self.active_operator();
1094        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1095            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1096        {
1097            context.add("VimCount");
1098        }
1099
1100        if let Some(active_operator) = active_operator {
1101            if active_operator.is_waiting(self.mode) {
1102                if matches!(active_operator, Operator::Literal { .. }) {
1103                    mode = "literal".to_string();
1104                } else {
1105                    mode = "waiting".to_string();
1106                }
1107            } else {
1108                operator_id = active_operator.id();
1109                mode = "operator".to_string();
1110            }
1111        }
1112
1113        if mode == "normal" || mode == "visual" || mode == "operator" || mode == "helix_normal" {
1114            context.add("VimControl");
1115        }
1116        context.set("vim_mode", mode);
1117        context.set("vim_operator", operator_id);
1118    }
1119
1120    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1121        let Some(editor) = self.editor() else {
1122            return;
1123        };
1124        let newest_selection_empty = editor.update(cx, |editor, cx| {
1125            editor.selections.newest::<usize>(cx).is_empty()
1126        });
1127        let editor = editor.read(cx);
1128        let editor_mode = editor.mode();
1129
1130        if editor_mode == EditorMode::Full
1131                && !newest_selection_empty
1132                && self.mode == Mode::Normal
1133                // When following someone, don't switch vim mode.
1134                && editor.leader_peer_id().is_none()
1135        {
1136            if preserve_selection {
1137                self.switch_mode(Mode::Visual, true, window, cx);
1138            } else {
1139                self.update_editor(window, cx, |_, editor, window, cx| {
1140                    editor.set_clip_at_line_ends(false, cx);
1141                    editor.change_selections(None, window, cx, |s| {
1142                        s.move_with(|_, selection| {
1143                            selection.collapse_to(selection.start, selection.goal)
1144                        })
1145                    });
1146                });
1147            }
1148        }
1149
1150        cx.emit(VimEvent::Focused);
1151        self.sync_vim_settings(window, cx);
1152
1153        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1154            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1155                if old_vim.entity_id() != cx.entity().entity_id() {
1156                    old_vim.update(cx, |vim, cx| {
1157                        vim.update_editor(window, cx, |_, editor, _, cx| {
1158                            editor.set_relative_line_number(None, cx)
1159                        });
1160                    });
1161
1162                    self.update_editor(window, cx, |vim, editor, _, cx| {
1163                        let is_relative = vim.mode != Mode::Insert;
1164                        editor.set_relative_line_number(Some(is_relative), cx)
1165                    });
1166                }
1167            } else {
1168                self.update_editor(window, cx, |vim, editor, _, cx| {
1169                    let is_relative = vim.mode != Mode::Insert;
1170                    editor.set_relative_line_number(Some(is_relative), cx)
1171                });
1172            }
1173        }
1174        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1175    }
1176
1177    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1178        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1179        self.store_visual_marks(window, cx);
1180        self.clear_operator(window, cx);
1181        self.update_editor(window, cx, |vim, editor, _, cx| {
1182            if vim.cursor_shape(cx) == CursorShape::Block {
1183                editor.set_cursor_shape(CursorShape::Hollow, cx);
1184            }
1185        });
1186    }
1187
1188    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1189        self.update_editor(window, cx, |vim, editor, _, cx| {
1190            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1191        });
1192    }
1193
1194    fn update_editor<S>(
1195        &mut self,
1196        window: &mut Window,
1197        cx: &mut Context<Self>,
1198        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1199    ) -> Option<S> {
1200        let editor = self.editor.upgrade()?;
1201        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1202    }
1203
1204    fn editor_selections(
1205        &mut self,
1206        window: &mut Window,
1207        cx: &mut Context<Self>,
1208    ) -> Vec<Range<Anchor>> {
1209        self.update_editor(window, cx, |_, editor, _, _| {
1210            editor
1211                .selections
1212                .disjoint_anchors()
1213                .iter()
1214                .map(|selection| selection.tail()..selection.head())
1215                .collect()
1216        })
1217        .unwrap_or_default()
1218    }
1219
1220    fn editor_cursor_word(
1221        &mut self,
1222        window: &mut Window,
1223        cx: &mut Context<Self>,
1224    ) -> Option<String> {
1225        self.update_editor(window, cx, |_, editor, window, cx| {
1226            let selection = editor.selections.newest::<usize>(cx);
1227
1228            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1229            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1230            if kind == Some(CharKind::Word) {
1231                let text: String = snapshot.text_for_range(range).collect();
1232                if !text.trim().is_empty() {
1233                    return Some(text);
1234                }
1235            }
1236
1237            None
1238        })
1239        .unwrap_or_default()
1240    }
1241
1242    /// When doing an action that modifies the buffer, we start recording so that `.`
1243    /// will replay the action.
1244    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1245        Vim::update_globals(cx, |globals, cx| {
1246            if !globals.dot_replaying {
1247                globals.dot_recording = true;
1248                globals.recording_actions = Default::default();
1249                globals.recorded_count = None;
1250
1251                let selections = self.editor().map(|editor| {
1252                    editor.update(cx, |editor, cx| {
1253                        (
1254                            editor.selections.oldest::<Point>(cx),
1255                            editor.selections.newest::<Point>(cx),
1256                        )
1257                    })
1258                });
1259
1260                if let Some((oldest, newest)) = selections {
1261                    globals.recorded_selection = match self.mode {
1262                        Mode::Visual if newest.end.row == newest.start.row => {
1263                            RecordedSelection::SingleLine {
1264                                cols: newest.end.column - newest.start.column,
1265                            }
1266                        }
1267                        Mode::Visual => RecordedSelection::Visual {
1268                            rows: newest.end.row - newest.start.row,
1269                            cols: newest.end.column,
1270                        },
1271                        Mode::VisualLine => RecordedSelection::VisualLine {
1272                            rows: newest.end.row - newest.start.row,
1273                        },
1274                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1275                            rows: newest.end.row.abs_diff(oldest.start.row),
1276                            cols: newest.end.column.abs_diff(oldest.start.column),
1277                        },
1278                        _ => RecordedSelection::None,
1279                    }
1280                } else {
1281                    globals.recorded_selection = RecordedSelection::None;
1282                }
1283            }
1284        })
1285    }
1286
1287    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1288        let globals = Vim::globals(cx);
1289        globals.dot_replaying = false;
1290        if let Some(replayer) = globals.replayer.take() {
1291            replayer.stop();
1292        }
1293    }
1294
1295    /// When finishing an action that modifies the buffer, stop recording.
1296    /// as you usually call this within a keystroke handler we also ensure that
1297    /// the current action is recorded.
1298    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1299        let globals = Vim::globals(cx);
1300        if globals.dot_recording {
1301            globals.stop_recording_after_next_action = true;
1302        }
1303        self.exit_temporary_mode = self.temp_mode;
1304    }
1305
1306    /// Stops recording actions immediately rather than waiting until after the
1307    /// next action to stop recording.
1308    ///
1309    /// This doesn't include the current action.
1310    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1311        let globals = Vim::globals(cx);
1312        if globals.dot_recording {
1313            globals
1314                .recording_actions
1315                .push(ReplayableAction::Action(action.boxed_clone()));
1316            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1317            globals.dot_recording = false;
1318            globals.stop_recording_after_next_action = false;
1319        }
1320        self.exit_temporary_mode = self.temp_mode;
1321    }
1322
1323    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1324    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1325        self.start_recording(cx);
1326        self.stop_recording(cx);
1327    }
1328
1329    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1330        if self.active_operator().is_some() {
1331            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1332
1333            Vim::globals(cx).post_count = Some(
1334                post_count
1335                    .checked_mul(10)
1336                    .and_then(|post_count| post_count.checked_add(number))
1337                    .unwrap_or(post_count),
1338            )
1339        } else {
1340            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1341
1342            Vim::globals(cx).pre_count = Some(
1343                pre_count
1344                    .checked_mul(10)
1345                    .and_then(|pre_count| pre_count.checked_add(number))
1346                    .unwrap_or(pre_count),
1347            )
1348        }
1349        // update the keymap so that 0 works
1350        self.sync_vim_settings(window, cx)
1351    }
1352
1353    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1354        if register.chars().count() == 1 {
1355            self.selected_register
1356                .replace(register.chars().next().unwrap());
1357        }
1358        self.operator_stack.clear();
1359        self.sync_vim_settings(window, cx);
1360    }
1361
1362    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1363        self.operator_stack.pop()
1364    }
1365
1366    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1367        let popped_operator = self.operator_stack.pop()
1368            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1369        self.sync_vim_settings(window, cx);
1370        popped_operator
1371    }
1372
1373    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1374        Vim::take_count(cx);
1375        self.selected_register.take();
1376        self.operator_stack.clear();
1377        self.sync_vim_settings(window, cx);
1378    }
1379
1380    fn active_operator(&self) -> Option<Operator> {
1381        self.operator_stack.last().cloned()
1382    }
1383
1384    fn transaction_begun(
1385        &mut self,
1386        transaction_id: TransactionId,
1387        _window: &mut Window,
1388        _: &mut Context<Self>,
1389    ) {
1390        let mode = if (self.mode == Mode::Insert
1391            || self.mode == Mode::Replace
1392            || self.mode == Mode::Normal)
1393            && self.current_tx.is_none()
1394        {
1395            self.current_tx = Some(transaction_id);
1396            self.last_mode
1397        } else {
1398            self.mode
1399        };
1400        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1401            self.undo_modes.insert(transaction_id, mode);
1402        }
1403    }
1404
1405    fn transaction_undone(
1406        &mut self,
1407        transaction_id: &TransactionId,
1408        window: &mut Window,
1409        cx: &mut Context<Self>,
1410    ) {
1411        match self.mode {
1412            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1413                self.update_editor(window, cx, |vim, editor, window, cx| {
1414                    let original_mode = vim.undo_modes.get(transaction_id);
1415                    editor.change_selections(None, window, cx, |s| match original_mode {
1416                        Some(Mode::VisualLine) => {
1417                            s.move_with(|map, selection| {
1418                                selection.collapse_to(
1419                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1420                                    SelectionGoal::None,
1421                                )
1422                            });
1423                        }
1424                        Some(Mode::VisualBlock) => {
1425                            let mut first = s.first_anchor();
1426                            first.collapse_to(first.start, first.goal);
1427                            s.select_anchors(vec![first]);
1428                        }
1429                        _ => {
1430                            s.move_with(|map, selection| {
1431                                selection.collapse_to(
1432                                    map.clip_at_line_end(selection.start),
1433                                    selection.goal,
1434                                );
1435                            });
1436                        }
1437                    });
1438                });
1439                self.switch_mode(Mode::Normal, true, window, cx)
1440            }
1441            Mode::Normal => {
1442                self.update_editor(window, cx, |_, editor, window, cx| {
1443                    editor.change_selections(None, window, cx, |s| {
1444                        s.move_with(|map, selection| {
1445                            selection
1446                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1447                        })
1448                    })
1449                });
1450            }
1451            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1452        }
1453    }
1454
1455    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1456        let Some(editor) = self.editor() else { return };
1457
1458        if editor.read(cx).leader_peer_id().is_some() {
1459            return;
1460        }
1461
1462        let newest = editor.read(cx).selections.newest_anchor().clone();
1463        let is_multicursor = editor.read(cx).selections.count() > 1;
1464        if self.mode == Mode::Insert && self.current_tx.is_some() {
1465            if self.current_anchor.is_none() {
1466                self.current_anchor = Some(newest);
1467            } else if self.current_anchor.as_ref().unwrap() != &newest {
1468                if let Some(tx_id) = self.current_tx.take() {
1469                    self.update_editor(window, cx, |_, editor, _, cx| {
1470                        editor.group_until_transaction(tx_id, cx)
1471                    });
1472                }
1473            }
1474        } else if self.mode == Mode::Normal && newest.start != newest.end {
1475            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1476                self.switch_mode(Mode::VisualBlock, false, window, cx);
1477            } else {
1478                self.switch_mode(Mode::Visual, false, window, cx)
1479            }
1480        } else if newest.start == newest.end
1481            && !is_multicursor
1482            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1483        {
1484            self.switch_mode(Mode::Normal, true, window, cx);
1485        }
1486    }
1487
1488    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1489        if text.is_empty() {
1490            return;
1491        }
1492
1493        match self.active_operator() {
1494            Some(Operator::FindForward { before }) => {
1495                let find = Motion::FindForward {
1496                    before,
1497                    char: text.chars().next().unwrap(),
1498                    mode: if VimSettings::get_global(cx).use_multiline_find {
1499                        FindRange::MultiLine
1500                    } else {
1501                        FindRange::SingleLine
1502                    },
1503                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1504                };
1505                Vim::globals(cx).last_find = Some(find.clone());
1506                self.motion(find, window, cx)
1507            }
1508            Some(Operator::FindBackward { after }) => {
1509                let find = Motion::FindBackward {
1510                    after,
1511                    char: text.chars().next().unwrap(),
1512                    mode: if VimSettings::get_global(cx).use_multiline_find {
1513                        FindRange::MultiLine
1514                    } else {
1515                        FindRange::SingleLine
1516                    },
1517                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1518                };
1519                Vim::globals(cx).last_find = Some(find.clone());
1520                self.motion(find, window, cx)
1521            }
1522            Some(Operator::Sneak { first_char }) => {
1523                if let Some(first_char) = first_char {
1524                    if let Some(second_char) = text.chars().next() {
1525                        let sneak = Motion::Sneak {
1526                            first_char,
1527                            second_char,
1528                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1529                        };
1530                        Vim::globals(cx).last_find = Some((&sneak).clone());
1531                        self.motion(sneak, window, cx)
1532                    }
1533                } else {
1534                    let first_char = text.chars().next();
1535                    self.pop_operator(window, cx);
1536                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1537                }
1538            }
1539            Some(Operator::SneakBackward { first_char }) => {
1540                if let Some(first_char) = first_char {
1541                    if let Some(second_char) = text.chars().next() {
1542                        let sneak = Motion::SneakBackward {
1543                            first_char,
1544                            second_char,
1545                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1546                        };
1547                        Vim::globals(cx).last_find = Some((&sneak).clone());
1548                        self.motion(sneak, window, cx)
1549                    }
1550                } else {
1551                    let first_char = text.chars().next();
1552                    self.pop_operator(window, cx);
1553                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1554                }
1555            }
1556            Some(Operator::Replace) => match self.mode {
1557                Mode::Normal => self.normal_replace(text, window, cx),
1558                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1559                    self.visual_replace(text, window, cx)
1560                }
1561                _ => self.clear_operator(window, cx),
1562            },
1563            Some(Operator::Digraph { first_char }) => {
1564                if let Some(first_char) = first_char {
1565                    if let Some(second_char) = text.chars().next() {
1566                        self.insert_digraph(first_char, second_char, window, cx);
1567                    }
1568                } else {
1569                    let first_char = text.chars().next();
1570                    self.pop_operator(window, cx);
1571                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1572                }
1573            }
1574            Some(Operator::Literal { prefix }) => {
1575                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1576            }
1577            Some(Operator::AddSurrounds { target }) => match self.mode {
1578                Mode::Normal => {
1579                    if let Some(target) = target {
1580                        self.add_surrounds(text, target, window, cx);
1581                        self.clear_operator(window, cx);
1582                    }
1583                }
1584                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1585                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1586                    self.clear_operator(window, cx);
1587                }
1588                _ => self.clear_operator(window, cx),
1589            },
1590            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1591                Mode::Normal => {
1592                    if let Some(target) = target {
1593                        self.change_surrounds(text, target, window, cx);
1594                        self.clear_operator(window, cx);
1595                    }
1596                }
1597                _ => self.clear_operator(window, cx),
1598            },
1599            Some(Operator::DeleteSurrounds) => match self.mode {
1600                Mode::Normal => {
1601                    self.delete_surrounds(text, window, cx);
1602                    self.clear_operator(window, cx);
1603                }
1604                _ => self.clear_operator(window, cx),
1605            },
1606            Some(Operator::Mark) => self.create_mark(text, window, cx),
1607            Some(Operator::RecordRegister) => {
1608                self.record_register(text.chars().next().unwrap(), window, cx)
1609            }
1610            Some(Operator::ReplayRegister) => {
1611                self.replay_register(text.chars().next().unwrap(), window, cx)
1612            }
1613            Some(Operator::Register) => match self.mode {
1614                Mode::Insert => {
1615                    self.update_editor(window, cx, |_, editor, window, cx| {
1616                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1617                            globals.read_register(text.chars().next(), Some(editor), cx)
1618                        }) {
1619                            editor.do_paste(
1620                                &register.text.to_string(),
1621                                register.clipboard_selections.clone(),
1622                                false,
1623                                window,
1624                                cx,
1625                            )
1626                        }
1627                    });
1628                    self.clear_operator(window, cx);
1629                }
1630                _ => {
1631                    self.select_register(text, window, cx);
1632                }
1633            },
1634            Some(Operator::Jump { line }) => self.jump(text, line, true, window, cx),
1635            _ => {
1636                if self.mode == Mode::Replace {
1637                    self.multi_replace(text, window, cx)
1638                }
1639
1640                if self.mode == Mode::Normal {
1641                    self.update_editor(window, cx, |_, editor, window, cx| {
1642                        editor.accept_edit_prediction(
1643                            &editor::actions::AcceptEditPrediction {},
1644                            window,
1645                            cx,
1646                        );
1647                    });
1648                }
1649            }
1650        }
1651    }
1652
1653    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1654        self.update_editor(window, cx, |vim, editor, window, cx| {
1655            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1656            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1657            editor.set_collapse_matches(true);
1658            editor.set_input_enabled(vim.editor_input_enabled());
1659            editor.set_autoindent(vim.should_autoindent());
1660            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1661
1662            let hide_inline_completions = match vim.mode {
1663                Mode::Insert | Mode::Replace => false,
1664                _ => true,
1665            };
1666            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1667        });
1668        cx.notify()
1669    }
1670}
1671
1672/// Controls when to use system clipboard.
1673#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1674#[serde(rename_all = "snake_case")]
1675pub enum UseSystemClipboard {
1676    /// Don't use system clipboard.
1677    Never,
1678    /// Use system clipboard.
1679    Always,
1680    /// Use system clipboard for yank operations.
1681    OnYank,
1682}
1683
1684#[derive(Deserialize)]
1685struct VimSettings {
1686    pub default_mode: Mode,
1687    pub toggle_relative_line_numbers: bool,
1688    pub use_system_clipboard: UseSystemClipboard,
1689    pub use_multiline_find: bool,
1690    pub use_smartcase_find: bool,
1691    pub custom_digraphs: HashMap<String, Arc<str>>,
1692    pub highlight_on_yank_duration: u64,
1693}
1694
1695#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1696struct VimSettingsContent {
1697    pub default_mode: Option<ModeContent>,
1698    pub toggle_relative_line_numbers: Option<bool>,
1699    pub use_system_clipboard: Option<UseSystemClipboard>,
1700    pub use_multiline_find: Option<bool>,
1701    pub use_smartcase_find: Option<bool>,
1702    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1703    pub highlight_on_yank_duration: Option<u64>,
1704}
1705
1706#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1707#[serde(rename_all = "snake_case")]
1708pub enum ModeContent {
1709    #[default]
1710    Normal,
1711    Insert,
1712    Replace,
1713    Visual,
1714    VisualLine,
1715    VisualBlock,
1716    HelixNormal,
1717}
1718
1719impl From<ModeContent> for Mode {
1720    fn from(mode: ModeContent) -> Self {
1721        match mode {
1722            ModeContent::Normal => Self::Normal,
1723            ModeContent::Insert => Self::Insert,
1724            ModeContent::Replace => Self::Replace,
1725            ModeContent::Visual => Self::Visual,
1726            ModeContent::VisualLine => Self::VisualLine,
1727            ModeContent::VisualBlock => Self::VisualBlock,
1728            ModeContent::HelixNormal => Self::HelixNormal,
1729        }
1730    }
1731}
1732
1733impl Settings for VimSettings {
1734    const KEY: Option<&'static str> = Some("vim");
1735
1736    type FileContent = VimSettingsContent;
1737
1738    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1739        let settings: VimSettingsContent = sources.json_merge()?;
1740
1741        Ok(Self {
1742            default_mode: settings
1743                .default_mode
1744                .ok_or_else(Self::missing_default)?
1745                .into(),
1746            toggle_relative_line_numbers: settings
1747                .toggle_relative_line_numbers
1748                .ok_or_else(Self::missing_default)?,
1749            use_system_clipboard: settings
1750                .use_system_clipboard
1751                .ok_or_else(Self::missing_default)?,
1752            use_multiline_find: settings
1753                .use_multiline_find
1754                .ok_or_else(Self::missing_default)?,
1755            use_smartcase_find: settings
1756                .use_smartcase_find
1757                .ok_or_else(Self::missing_default)?,
1758            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1759            highlight_on_yank_duration: settings
1760                .highlight_on_yank_duration
1761                .ok_or_else(Self::missing_default)?,
1762        })
1763    }
1764}