vim.rs

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