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