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