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        let cursor_shape = VimSettings::get_global(cx).cursor_shape;
1027        match self.mode {
1028            Mode::Normal => {
1029                if let Some(operator) = self.operator_stack.last() {
1030                    match operator {
1031                        // Navigation operators -> Block cursor
1032                        Operator::FindForward { .. }
1033                        | Operator::FindBackward { .. }
1034                        | Operator::Mark
1035                        | Operator::Jump { .. }
1036                        | Operator::Register
1037                        | Operator::RecordRegister
1038                        | Operator::ReplayRegister => CursorShape::Block,
1039
1040                        // All other operators -> Underline cursor
1041                        _ => CursorShape::Underline,
1042                    }
1043                } else {
1044                    cursor_shape.normal.unwrap_or(CursorShape::Block)
1045                }
1046            }
1047            Mode::HelixNormal => cursor_shape.normal.unwrap_or(CursorShape::Block),
1048            Mode::Replace => cursor_shape.replace.unwrap_or(CursorShape::Underline),
1049            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1050                cursor_shape.visual.unwrap_or(CursorShape::Block)
1051            }
1052            Mode::Insert => cursor_shape.insert.unwrap_or({
1053                let editor_settings = EditorSettings::get_global(cx);
1054                editor_settings.cursor_shape.unwrap_or_default()
1055            }),
1056        }
1057    }
1058
1059    pub fn editor_input_enabled(&self) -> bool {
1060        match self.mode {
1061            Mode::Insert => {
1062                if let Some(operator) = self.operator_stack.last() {
1063                    !operator.is_waiting(self.mode)
1064                } else {
1065                    true
1066                }
1067            }
1068            Mode::Normal
1069            | Mode::HelixNormal
1070            | Mode::Replace
1071            | Mode::Visual
1072            | Mode::VisualLine
1073            | Mode::VisualBlock => false,
1074        }
1075    }
1076
1077    pub fn should_autoindent(&self) -> bool {
1078        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1079    }
1080
1081    pub fn clip_at_line_ends(&self) -> bool {
1082        match self.mode {
1083            Mode::Insert
1084            | Mode::Visual
1085            | Mode::VisualLine
1086            | Mode::VisualBlock
1087            | Mode::Replace
1088            | Mode::HelixNormal => false,
1089            Mode::Normal => true,
1090        }
1091    }
1092
1093    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1094        let mut mode = match self.mode {
1095            Mode::Normal => "normal",
1096            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1097            Mode::Insert => "insert",
1098            Mode::Replace => "replace",
1099            Mode::HelixNormal => "helix_normal",
1100        }
1101        .to_string();
1102
1103        let mut operator_id = "none";
1104
1105        let active_operator = self.active_operator();
1106        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1107            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1108        {
1109            context.add("VimCount");
1110        }
1111
1112        if let Some(active_operator) = active_operator {
1113            if active_operator.is_waiting(self.mode) {
1114                if matches!(active_operator, Operator::Literal { .. }) {
1115                    mode = "literal".to_string();
1116                } else {
1117                    mode = "waiting".to_string();
1118                }
1119            } else {
1120                operator_id = active_operator.id();
1121                mode = "operator".to_string();
1122            }
1123        }
1124
1125        if mode == "normal" || mode == "visual" || mode == "operator" || mode == "helix_normal" {
1126            context.add("VimControl");
1127        }
1128        context.set("vim_mode", mode);
1129        context.set("vim_operator", operator_id);
1130    }
1131
1132    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1133        let Some(editor) = self.editor() else {
1134            return;
1135        };
1136        let newest_selection_empty = editor.update(cx, |editor, cx| {
1137            editor.selections.newest::<usize>(cx).is_empty()
1138        });
1139        let editor = editor.read(cx);
1140        let editor_mode = editor.mode();
1141
1142        if editor_mode.is_full()
1143            && !newest_selection_empty
1144            && self.mode == Mode::Normal
1145            // When following someone, don't switch vim mode.
1146            && editor.leader_id().is_none()
1147        {
1148            if preserve_selection {
1149                self.switch_mode(Mode::Visual, true, window, cx);
1150            } else {
1151                self.update_editor(window, cx, |_, editor, window, cx| {
1152                    editor.set_clip_at_line_ends(false, cx);
1153                    editor.change_selections(None, window, cx, |s| {
1154                        s.move_with(|_, selection| {
1155                            selection.collapse_to(selection.start, selection.goal)
1156                        })
1157                    });
1158                });
1159            }
1160        }
1161
1162        cx.emit(VimEvent::Focused);
1163        self.sync_vim_settings(window, cx);
1164
1165        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1166            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1167                if old_vim.entity_id() != cx.entity().entity_id() {
1168                    old_vim.update(cx, |vim, cx| {
1169                        vim.update_editor(window, cx, |_, editor, _, cx| {
1170                            editor.set_relative_line_number(None, cx)
1171                        });
1172                    });
1173
1174                    self.update_editor(window, cx, |vim, editor, _, cx| {
1175                        let is_relative = vim.mode != Mode::Insert;
1176                        editor.set_relative_line_number(Some(is_relative), cx)
1177                    });
1178                }
1179            } else {
1180                self.update_editor(window, cx, |vim, editor, _, cx| {
1181                    let is_relative = vim.mode != Mode::Insert;
1182                    editor.set_relative_line_number(Some(is_relative), cx)
1183                });
1184            }
1185        }
1186        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1187    }
1188
1189    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1190        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1191        self.store_visual_marks(window, cx);
1192        self.clear_operator(window, cx);
1193        self.update_editor(window, cx, |vim, editor, _, cx| {
1194            if vim.cursor_shape(cx) == CursorShape::Block {
1195                editor.set_cursor_shape(CursorShape::Hollow, cx);
1196            }
1197        });
1198    }
1199
1200    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1201        self.update_editor(window, cx, |vim, editor, _, cx| {
1202            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1203        });
1204    }
1205
1206    fn update_editor<S>(
1207        &mut self,
1208        window: &mut Window,
1209        cx: &mut Context<Self>,
1210        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1211    ) -> Option<S> {
1212        let editor = self.editor.upgrade()?;
1213        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1214    }
1215
1216    fn editor_selections(
1217        &mut self,
1218        window: &mut Window,
1219        cx: &mut Context<Self>,
1220    ) -> Vec<Range<Anchor>> {
1221        self.update_editor(window, cx, |_, editor, _, _| {
1222            editor
1223                .selections
1224                .disjoint_anchors()
1225                .iter()
1226                .map(|selection| selection.tail()..selection.head())
1227                .collect()
1228        })
1229        .unwrap_or_default()
1230    }
1231
1232    fn editor_cursor_word(
1233        &mut self,
1234        window: &mut Window,
1235        cx: &mut Context<Self>,
1236    ) -> Option<String> {
1237        self.update_editor(window, cx, |_, editor, window, cx| {
1238            let selection = editor.selections.newest::<usize>(cx);
1239
1240            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1241            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1242            if kind == Some(CharKind::Word) {
1243                let text: String = snapshot.text_for_range(range).collect();
1244                if !text.trim().is_empty() {
1245                    return Some(text);
1246                }
1247            }
1248
1249            None
1250        })
1251        .unwrap_or_default()
1252    }
1253
1254    /// When doing an action that modifies the buffer, we start recording so that `.`
1255    /// will replay the action.
1256    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1257        Vim::update_globals(cx, |globals, cx| {
1258            if !globals.dot_replaying {
1259                globals.dot_recording = true;
1260                globals.recording_actions = Default::default();
1261                globals.recorded_count = None;
1262
1263                let selections = self.editor().map(|editor| {
1264                    editor.update(cx, |editor, cx| {
1265                        (
1266                            editor.selections.oldest::<Point>(cx),
1267                            editor.selections.newest::<Point>(cx),
1268                        )
1269                    })
1270                });
1271
1272                if let Some((oldest, newest)) = selections {
1273                    globals.recorded_selection = match self.mode {
1274                        Mode::Visual if newest.end.row == newest.start.row => {
1275                            RecordedSelection::SingleLine {
1276                                cols: newest.end.column - newest.start.column,
1277                            }
1278                        }
1279                        Mode::Visual => RecordedSelection::Visual {
1280                            rows: newest.end.row - newest.start.row,
1281                            cols: newest.end.column,
1282                        },
1283                        Mode::VisualLine => RecordedSelection::VisualLine {
1284                            rows: newest.end.row - newest.start.row,
1285                        },
1286                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1287                            rows: newest.end.row.abs_diff(oldest.start.row),
1288                            cols: newest.end.column.abs_diff(oldest.start.column),
1289                        },
1290                        _ => RecordedSelection::None,
1291                    }
1292                } else {
1293                    globals.recorded_selection = RecordedSelection::None;
1294                }
1295            }
1296        })
1297    }
1298
1299    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1300        let globals = Vim::globals(cx);
1301        globals.dot_replaying = false;
1302        if let Some(replayer) = globals.replayer.take() {
1303            replayer.stop();
1304        }
1305    }
1306
1307    /// When finishing an action that modifies the buffer, stop recording.
1308    /// as you usually call this within a keystroke handler we also ensure that
1309    /// the current action is recorded.
1310    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1311        let globals = Vim::globals(cx);
1312        if globals.dot_recording {
1313            globals.stop_recording_after_next_action = true;
1314        }
1315        self.exit_temporary_mode = self.temp_mode;
1316    }
1317
1318    /// Stops recording actions immediately rather than waiting until after the
1319    /// next action to stop recording.
1320    ///
1321    /// This doesn't include the current action.
1322    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1323        let globals = Vim::globals(cx);
1324        if globals.dot_recording {
1325            globals
1326                .recording_actions
1327                .push(ReplayableAction::Action(action.boxed_clone()));
1328            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1329            globals.dot_recording = false;
1330            globals.stop_recording_after_next_action = false;
1331        }
1332        self.exit_temporary_mode = self.temp_mode;
1333    }
1334
1335    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1336    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1337        self.start_recording(cx);
1338        self.stop_recording(cx);
1339    }
1340
1341    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1342        if self.active_operator().is_some() {
1343            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1344
1345            Vim::globals(cx).post_count = Some(
1346                post_count
1347                    .checked_mul(10)
1348                    .and_then(|post_count| post_count.checked_add(number))
1349                    .unwrap_or(post_count),
1350            )
1351        } else {
1352            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1353
1354            Vim::globals(cx).pre_count = Some(
1355                pre_count
1356                    .checked_mul(10)
1357                    .and_then(|pre_count| pre_count.checked_add(number))
1358                    .unwrap_or(pre_count),
1359            )
1360        }
1361        // update the keymap so that 0 works
1362        self.sync_vim_settings(window, cx)
1363    }
1364
1365    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1366        if register.chars().count() == 1 {
1367            self.selected_register
1368                .replace(register.chars().next().unwrap());
1369        }
1370        self.operator_stack.clear();
1371        self.sync_vim_settings(window, cx);
1372    }
1373
1374    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1375        self.operator_stack.pop()
1376    }
1377
1378    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1379        let popped_operator = self.operator_stack.pop()
1380            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1381        self.sync_vim_settings(window, cx);
1382        popped_operator
1383    }
1384
1385    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1386        Vim::take_count(cx);
1387        Vim::take_forced_motion(cx);
1388        self.selected_register.take();
1389        self.operator_stack.clear();
1390        self.sync_vim_settings(window, cx);
1391    }
1392
1393    fn active_operator(&self) -> Option<Operator> {
1394        self.operator_stack.last().cloned()
1395    }
1396
1397    fn transaction_begun(
1398        &mut self,
1399        transaction_id: TransactionId,
1400        _window: &mut Window,
1401        _: &mut Context<Self>,
1402    ) {
1403        let mode = if (self.mode == Mode::Insert
1404            || self.mode == Mode::Replace
1405            || self.mode == Mode::Normal)
1406            && self.current_tx.is_none()
1407        {
1408            self.current_tx = Some(transaction_id);
1409            self.last_mode
1410        } else {
1411            self.mode
1412        };
1413        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1414            self.undo_modes.insert(transaction_id, mode);
1415        }
1416    }
1417
1418    fn transaction_undone(
1419        &mut self,
1420        transaction_id: &TransactionId,
1421        window: &mut Window,
1422        cx: &mut Context<Self>,
1423    ) {
1424        match self.mode {
1425            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1426                self.update_editor(window, cx, |vim, editor, window, cx| {
1427                    let original_mode = vim.undo_modes.get(transaction_id);
1428                    editor.change_selections(None, window, cx, |s| match original_mode {
1429                        Some(Mode::VisualLine) => {
1430                            s.move_with(|map, selection| {
1431                                selection.collapse_to(
1432                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1433                                    SelectionGoal::None,
1434                                )
1435                            });
1436                        }
1437                        Some(Mode::VisualBlock) => {
1438                            let mut first = s.first_anchor();
1439                            first.collapse_to(first.start, first.goal);
1440                            s.select_anchors(vec![first]);
1441                        }
1442                        _ => {
1443                            s.move_with(|map, selection| {
1444                                selection.collapse_to(
1445                                    map.clip_at_line_end(selection.start),
1446                                    selection.goal,
1447                                );
1448                            });
1449                        }
1450                    });
1451                });
1452                self.switch_mode(Mode::Normal, true, window, cx)
1453            }
1454            Mode::Normal => {
1455                self.update_editor(window, cx, |_, editor, window, cx| {
1456                    editor.change_selections(None, window, cx, |s| {
1457                        s.move_with(|map, selection| {
1458                            selection
1459                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1460                        })
1461                    })
1462                });
1463            }
1464            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1465        }
1466    }
1467
1468    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1469        let Some(editor) = self.editor() else { return };
1470
1471        if editor.read(cx).leader_id().is_some() {
1472            return;
1473        }
1474
1475        let newest = editor.read(cx).selections.newest_anchor().clone();
1476        let is_multicursor = editor.read(cx).selections.count() > 1;
1477        if self.mode == Mode::Insert && self.current_tx.is_some() {
1478            if self.current_anchor.is_none() {
1479                self.current_anchor = Some(newest);
1480            } else if self.current_anchor.as_ref().unwrap() != &newest {
1481                if let Some(tx_id) = self.current_tx.take() {
1482                    self.update_editor(window, cx, |_, editor, _, cx| {
1483                        editor.group_until_transaction(tx_id, cx)
1484                    });
1485                }
1486            }
1487        } else if self.mode == Mode::Normal && newest.start != newest.end {
1488            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1489                self.switch_mode(Mode::VisualBlock, false, window, cx);
1490            } else {
1491                self.switch_mode(Mode::Visual, false, window, cx)
1492            }
1493        } else if newest.start == newest.end
1494            && !is_multicursor
1495            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1496        {
1497            self.switch_mode(Mode::Normal, true, window, cx);
1498        }
1499    }
1500
1501    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1502        if text.is_empty() {
1503            return;
1504        }
1505
1506        match self.active_operator() {
1507            Some(Operator::FindForward { before }) => {
1508                let find = Motion::FindForward {
1509                    before,
1510                    char: text.chars().next().unwrap(),
1511                    mode: if VimSettings::get_global(cx).use_multiline_find {
1512                        FindRange::MultiLine
1513                    } else {
1514                        FindRange::SingleLine
1515                    },
1516                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1517                };
1518                Vim::globals(cx).last_find = Some(find.clone());
1519                self.motion(find, window, cx)
1520            }
1521            Some(Operator::FindBackward { after }) => {
1522                let find = Motion::FindBackward {
1523                    after,
1524                    char: text.chars().next().unwrap(),
1525                    mode: if VimSettings::get_global(cx).use_multiline_find {
1526                        FindRange::MultiLine
1527                    } else {
1528                        FindRange::SingleLine
1529                    },
1530                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1531                };
1532                Vim::globals(cx).last_find = Some(find.clone());
1533                self.motion(find, window, cx)
1534            }
1535            Some(Operator::Sneak { first_char }) => {
1536                if let Some(first_char) = first_char {
1537                    if let Some(second_char) = text.chars().next() {
1538                        let sneak = Motion::Sneak {
1539                            first_char,
1540                            second_char,
1541                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1542                        };
1543                        Vim::globals(cx).last_find = Some((&sneak).clone());
1544                        self.motion(sneak, window, cx)
1545                    }
1546                } else {
1547                    let first_char = text.chars().next();
1548                    self.pop_operator(window, cx);
1549                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1550                }
1551            }
1552            Some(Operator::SneakBackward { first_char }) => {
1553                if let Some(first_char) = first_char {
1554                    if let Some(second_char) = text.chars().next() {
1555                        let sneak = Motion::SneakBackward {
1556                            first_char,
1557                            second_char,
1558                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1559                        };
1560                        Vim::globals(cx).last_find = Some((&sneak).clone());
1561                        self.motion(sneak, window, cx)
1562                    }
1563                } else {
1564                    let first_char = text.chars().next();
1565                    self.pop_operator(window, cx);
1566                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1567                }
1568            }
1569            Some(Operator::Replace) => match self.mode {
1570                Mode::Normal => self.normal_replace(text, window, cx),
1571                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1572                    self.visual_replace(text, window, cx)
1573                }
1574                _ => self.clear_operator(window, cx),
1575            },
1576            Some(Operator::Digraph { first_char }) => {
1577                if let Some(first_char) = first_char {
1578                    if let Some(second_char) = text.chars().next() {
1579                        self.insert_digraph(first_char, second_char, window, cx);
1580                    }
1581                } else {
1582                    let first_char = text.chars().next();
1583                    self.pop_operator(window, cx);
1584                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1585                }
1586            }
1587            Some(Operator::Literal { prefix }) => {
1588                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1589            }
1590            Some(Operator::AddSurrounds { target }) => match self.mode {
1591                Mode::Normal => {
1592                    if let Some(target) = target {
1593                        self.add_surrounds(text, target, window, cx);
1594                        self.clear_operator(window, cx);
1595                    }
1596                }
1597                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1598                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1599                    self.clear_operator(window, cx);
1600                }
1601                _ => self.clear_operator(window, cx),
1602            },
1603            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1604                Mode::Normal => {
1605                    if let Some(target) = target {
1606                        self.change_surrounds(text, target, window, cx);
1607                        self.clear_operator(window, cx);
1608                    }
1609                }
1610                _ => self.clear_operator(window, cx),
1611            },
1612            Some(Operator::DeleteSurrounds) => match self.mode {
1613                Mode::Normal => {
1614                    self.delete_surrounds(text, window, cx);
1615                    self.clear_operator(window, cx);
1616                }
1617                _ => self.clear_operator(window, cx),
1618            },
1619            Some(Operator::Mark) => self.create_mark(text, window, cx),
1620            Some(Operator::RecordRegister) => {
1621                self.record_register(text.chars().next().unwrap(), window, cx)
1622            }
1623            Some(Operator::ReplayRegister) => {
1624                self.replay_register(text.chars().next().unwrap(), window, cx)
1625            }
1626            Some(Operator::Register) => match self.mode {
1627                Mode::Insert => {
1628                    self.update_editor(window, cx, |_, editor, window, cx| {
1629                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1630                            globals.read_register(text.chars().next(), Some(editor), cx)
1631                        }) {
1632                            editor.do_paste(
1633                                &register.text.to_string(),
1634                                register.clipboard_selections.clone(),
1635                                false,
1636                                window,
1637                                cx,
1638                            )
1639                        }
1640                    });
1641                    self.clear_operator(window, cx);
1642                }
1643                _ => {
1644                    self.select_register(text, window, cx);
1645                }
1646            },
1647            Some(Operator::Jump { line }) => self.jump(text, line, true, window, cx),
1648            _ => {
1649                if self.mode == Mode::Replace {
1650                    self.multi_replace(text, window, cx)
1651                }
1652
1653                if self.mode == Mode::Normal {
1654                    self.update_editor(window, cx, |_, editor, window, cx| {
1655                        editor.accept_edit_prediction(
1656                            &editor::actions::AcceptEditPrediction {},
1657                            window,
1658                            cx,
1659                        );
1660                    });
1661                }
1662            }
1663        }
1664    }
1665
1666    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1667        self.update_editor(window, cx, |vim, editor, window, cx| {
1668            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1669            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1670            editor.set_collapse_matches(true);
1671            editor.set_input_enabled(vim.editor_input_enabled());
1672            editor.set_autoindent(vim.should_autoindent());
1673            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1674
1675            let hide_inline_completions = match vim.mode {
1676                Mode::Insert | Mode::Replace => false,
1677                _ => true,
1678            };
1679            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1680        });
1681        cx.notify()
1682    }
1683}
1684
1685/// Controls when to use system clipboard.
1686#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1687#[serde(rename_all = "snake_case")]
1688pub enum UseSystemClipboard {
1689    /// Don't use system clipboard.
1690    Never,
1691    /// Use system clipboard.
1692    Always,
1693    /// Use system clipboard for yank operations.
1694    OnYank,
1695}
1696
1697/// The settings for cursor shape.
1698#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1699struct CursorShapeSettings {
1700    /// Cursor shape for the normal mode.
1701    ///
1702    /// Default: block
1703    pub normal: Option<CursorShape>,
1704    /// Cursor shape for the replace mode.
1705    ///
1706    /// Default: underline
1707    pub replace: Option<CursorShape>,
1708    /// Cursor shape for the visual mode.
1709    ///
1710    /// Default: block
1711    pub visual: Option<CursorShape>,
1712    /// Cursor shape for the insert mode.
1713    ///
1714    /// The default value follows the primary cursor_shape.
1715    pub insert: Option<CursorShape>,
1716}
1717
1718#[derive(Deserialize)]
1719struct VimSettings {
1720    pub default_mode: Mode,
1721    pub toggle_relative_line_numbers: bool,
1722    pub use_system_clipboard: UseSystemClipboard,
1723    pub use_multiline_find: bool,
1724    pub use_smartcase_find: bool,
1725    pub custom_digraphs: HashMap<String, Arc<str>>,
1726    pub highlight_on_yank_duration: u64,
1727    pub cursor_shape: CursorShapeSettings,
1728}
1729
1730#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1731struct VimSettingsContent {
1732    pub default_mode: Option<ModeContent>,
1733    pub toggle_relative_line_numbers: Option<bool>,
1734    pub use_system_clipboard: Option<UseSystemClipboard>,
1735    pub use_multiline_find: Option<bool>,
1736    pub use_smartcase_find: Option<bool>,
1737    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1738    pub highlight_on_yank_duration: Option<u64>,
1739    pub cursor_shape: Option<CursorShapeSettings>,
1740}
1741
1742#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1743#[serde(rename_all = "snake_case")]
1744pub enum ModeContent {
1745    #[default]
1746    Normal,
1747    Insert,
1748    Replace,
1749    Visual,
1750    VisualLine,
1751    VisualBlock,
1752    HelixNormal,
1753}
1754
1755impl From<ModeContent> for Mode {
1756    fn from(mode: ModeContent) -> Self {
1757        match mode {
1758            ModeContent::Normal => Self::Normal,
1759            ModeContent::Insert => Self::Insert,
1760            ModeContent::Replace => Self::Replace,
1761            ModeContent::Visual => Self::Visual,
1762            ModeContent::VisualLine => Self::VisualLine,
1763            ModeContent::VisualBlock => Self::VisualBlock,
1764            ModeContent::HelixNormal => Self::HelixNormal,
1765        }
1766    }
1767}
1768
1769impl Settings for VimSettings {
1770    const KEY: Option<&'static str> = Some("vim");
1771
1772    type FileContent = VimSettingsContent;
1773
1774    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1775        let settings: VimSettingsContent = sources.json_merge()?;
1776
1777        Ok(Self {
1778            default_mode: settings
1779                .default_mode
1780                .ok_or_else(Self::missing_default)?
1781                .into(),
1782            toggle_relative_line_numbers: settings
1783                .toggle_relative_line_numbers
1784                .ok_or_else(Self::missing_default)?,
1785            use_system_clipboard: settings
1786                .use_system_clipboard
1787                .ok_or_else(Self::missing_default)?,
1788            use_multiline_find: settings
1789                .use_multiline_find
1790                .ok_or_else(Self::missing_default)?,
1791            use_smartcase_find: settings
1792                .use_smartcase_find
1793                .ok_or_else(Self::missing_default)?,
1794            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1795            highlight_on_yank_duration: settings
1796                .highlight_on_yank_duration
1797                .ok_or_else(Self::missing_default)?,
1798            cursor_shape: settings.cursor_shape.ok_or_else(Self::missing_default)?,
1799        })
1800    }
1801
1802    fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {
1803        // TODO: translate vim extension settings
1804    }
1805}