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