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