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