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    movement::{self, FindRange},
  26    Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
  27};
  28use gpui::{
  29    actions, impl_actions, Action, App, AppContext as _, Axis, Context, Entity, EventEmitter,
  30    KeyContext, KeystrokeEvent, Render, Subscription, Task, WeakEntity, Window,
  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::{update_settings_file, Settings, SettingsSources, SettingsStore};
  42use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
  43use std::{mem, ops::Range, sync::Arc};
  44use surrounds::SurroundsType;
  45use theme::ThemeSettings;
  46use ui::{px, IntoElement, SharedString};
  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        PushIndent,
 148        PushOutdent,
 149        PushAutoIndent,
 150        PushRewrap,
 151        PushShellCommand,
 152        PushLowercase,
 153        PushUppercase,
 154        PushOppositeCase,
 155        PushRegister,
 156        PushRecordRegister,
 157        PushReplayRegister,
 158        PushReplaceWithRegister,
 159        PushToggleComments,
 160    ]
 161);
 162
 163// in the workspace namespace so it's not filtered out when vim is disabled.
 164actions!(workspace, [ToggleVimMode,]);
 165
 166impl_actions!(
 167    vim,
 168    [
 169        Number,
 170        SelectRegister,
 171        PushObject,
 172        PushFindForward,
 173        PushFindBackward,
 174        PushSneak,
 175        PushSneakBackward,
 176        PushAddSurrounds,
 177        PushChangeSurrounds,
 178        PushJump,
 179        PushDigraph,
 180        PushLiteral
 181    ]
 182);
 183
 184/// Initializes the `vim` crate.
 185pub fn init(cx: &mut App) {
 186    vim_mode_setting::init(cx);
 187    VimSettings::register(cx);
 188    VimGlobals::register(cx);
 189
 190    cx.observe_new(Vim::register).detach();
 191
 192    cx.observe_new(|workspace: &mut Workspace, _, _| {
 193        workspace.register_action(|workspace, _: &ToggleVimMode, _, cx| {
 194            let fs = workspace.app_state().fs.clone();
 195            let currently_enabled = Vim::enabled(cx);
 196            update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
 197                *setting = Some(!currently_enabled)
 198            })
 199        });
 200
 201        workspace.register_action(|_, _: &OpenDefaultKeymap, _, cx| {
 202            cx.emit(workspace::Event::OpenBundledFile {
 203                text: settings::vim_keymap(),
 204                title: "Default Vim Bindings",
 205                language: "JSON",
 206            });
 207        });
 208
 209        workspace.register_action(|workspace, _: &ResetPaneSizes, _, cx| {
 210            workspace.reset_pane_sizes(cx);
 211        });
 212
 213        workspace.register_action(|workspace, _: &MaximizePane, window, cx| {
 214            let pane = workspace.active_pane();
 215            let Some(size) = workspace.bounding_box_for_pane(&pane) else {
 216                return;
 217            };
 218
 219            let theme = ThemeSettings::get_global(cx);
 220            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 221
 222            let desired_size = if let Some(count) = Vim::take_count(cx) {
 223                height * count
 224            } else {
 225                px(10000.)
 226            };
 227            workspace.resize_pane(Axis::Vertical, desired_size - size.size.height, window, cx)
 228        });
 229
 230        workspace.register_action(|workspace, _: &ResizePaneRight, window, cx| {
 231            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 232            let theme = ThemeSettings::get_global(cx);
 233            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
 234                return;
 235            };
 236            let Ok(width) = window
 237                .text_system()
 238                .advance(font_id, theme.buffer_font_size(cx), 'm')
 239            else {
 240                return;
 241            };
 242            workspace.resize_pane(Axis::Horizontal, width.width * count, window, cx);
 243        });
 244
 245        workspace.register_action(|workspace, _: &ResizePaneLeft, window, cx| {
 246            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 247            let theme = ThemeSettings::get_global(cx);
 248            let Ok(font_id) = window.text_system().font_id(&theme.buffer_font) else {
 249                return;
 250            };
 251            let Ok(width) = window
 252                .text_system()
 253                .advance(font_id, theme.buffer_font_size(cx), 'm')
 254            else {
 255                return;
 256            };
 257            workspace.resize_pane(Axis::Horizontal, -width.width * count, window, cx);
 258        });
 259
 260        workspace.register_action(|workspace, _: &ResizePaneUp, window, cx| {
 261            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 262            let theme = ThemeSettings::get_global(cx);
 263            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 264            workspace.resize_pane(Axis::Vertical, height * count, window, cx);
 265        });
 266
 267        workspace.register_action(|workspace, _: &ResizePaneDown, window, cx| {
 268            let count = Vim::take_count(cx).unwrap_or(1) as f32;
 269            let theme = ThemeSettings::get_global(cx);
 270            let height = theme.buffer_font_size(cx) * theme.buffer_line_height.value();
 271            workspace.resize_pane(Axis::Vertical, -height * count, window, cx);
 272        });
 273
 274        workspace.register_action(|workspace, _: &SearchSubmit, window, cx| {
 275            let vim = workspace
 276                .focused_pane(window, cx)
 277                .read(cx)
 278                .active_item()
 279                .and_then(|item| item.act_as::<Editor>(cx))
 280                .and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned());
 281            let Some(vim) = vim else { return };
 282            vim.entity.update(cx, |_, cx| {
 283                cx.defer_in(window, |vim, window, cx| vim.search_submit(window, cx))
 284            })
 285        });
 286    })
 287    .detach();
 288}
 289
 290#[derive(Clone)]
 291pub(crate) struct VimAddon {
 292    pub(crate) entity: Entity<Vim>,
 293}
 294
 295impl editor::Addon for VimAddon {
 296    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &App) {
 297        self.entity.read(cx).extend_key_context(key_context, cx)
 298    }
 299
 300    fn to_any(&self) -> &dyn std::any::Any {
 301        self
 302    }
 303}
 304
 305/// The state pertaining to Vim mode.
 306pub(crate) struct Vim {
 307    pub(crate) mode: Mode,
 308    pub last_mode: Mode,
 309    pub temp_mode: bool,
 310    pub status_label: Option<SharedString>,
 311    pub exit_temporary_mode: bool,
 312
 313    operator_stack: Vec<Operator>,
 314    pub(crate) replacements: Vec<(Range<editor::Anchor>, String)>,
 315
 316    pub(crate) marks: HashMap<String, Vec<Anchor>>,
 317    pub(crate) stored_visual_mode: Option<(Mode, Vec<bool>)>,
 318    pub(crate) change_list: Vec<Vec<Anchor>>,
 319    pub(crate) change_list_position: Option<usize>,
 320
 321    pub(crate) current_tx: Option<TransactionId>,
 322    pub(crate) current_anchor: Option<Selection<Anchor>>,
 323    pub(crate) undo_modes: HashMap<TransactionId, Mode>,
 324
 325    selected_register: Option<char>,
 326    pub search: SearchState,
 327
 328    editor: WeakEntity<Editor>,
 329
 330    last_command: Option<String>,
 331    running_command: Option<Task<()>>,
 332    _subscriptions: Vec<Subscription>,
 333}
 334
 335// Hack: Vim intercepts events dispatched to a window and updates the view in response.
 336// This means it needs a VisualContext. The easiest way to satisfy that constraint is
 337// to make Vim a "View" that is just never actually rendered.
 338impl Render for Vim {
 339    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 340        gpui::Empty
 341    }
 342}
 343
 344enum VimEvent {
 345    Focused,
 346}
 347impl EventEmitter<VimEvent> for Vim {}
 348
 349impl Vim {
 350    /// The namespace for Vim actions.
 351    const NAMESPACE: &'static str = "vim";
 352
 353    pub fn new(window: &mut Window, cx: &mut Context<Editor>) -> Entity<Self> {
 354        let editor = cx.entity().clone();
 355
 356        cx.new(|cx| Vim {
 357            mode: VimSettings::get_global(cx).default_mode,
 358            last_mode: Mode::Normal,
 359            temp_mode: false,
 360            exit_temporary_mode: false,
 361            operator_stack: Vec::new(),
 362            replacements: Vec::new(),
 363
 364            marks: HashMap::default(),
 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                return;
 771            }
 772        } else if window.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress()
 773        {
 774            return;
 775        }
 776
 777        if let Some(operator) = self.active_operator() {
 778            match operator {
 779                Operator::Literal { prefix } => {
 780                    self.handle_literal_keystroke(
 781                        keystroke_event,
 782                        prefix.unwrap_or_default(),
 783                        window,
 784                        cx,
 785                    );
 786                }
 787                _ if !operator.is_waiting(self.mode) => {
 788                    self.clear_operator(window, cx);
 789                    self.stop_recording_immediately(Box::new(ClearOperators), cx)
 790                }
 791                _ => {}
 792            }
 793        }
 794    }
 795
 796    fn handle_editor_event(
 797        &mut self,
 798        event: &EditorEvent,
 799        window: &mut Window,
 800        cx: &mut Context<Self>,
 801    ) {
 802        match event {
 803            EditorEvent::Focused => self.focused(true, window, cx),
 804            EditorEvent::Blurred => self.blurred(window, cx),
 805            EditorEvent::SelectionsChanged { local: true } => {
 806                self.local_selections_changed(window, cx);
 807            }
 808            EditorEvent::InputIgnored { text } => {
 809                self.input_ignored(text.clone(), window, cx);
 810                Vim::globals(cx).observe_insertion(text, None)
 811            }
 812            EditorEvent::InputHandled {
 813                text,
 814                utf16_range_to_replace: range_to_replace,
 815            } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
 816            EditorEvent::TransactionBegun { transaction_id } => {
 817                self.transaction_begun(*transaction_id, window, cx)
 818            }
 819            EditorEvent::TransactionUndone { transaction_id } => {
 820                self.transaction_undone(transaction_id, window, cx)
 821            }
 822            EditorEvent::Edited { .. } => self.push_to_change_list(window, cx),
 823            EditorEvent::FocusedIn => self.sync_vim_settings(window, cx),
 824            EditorEvent::CursorShapeChanged => self.cursor_shape_changed(window, cx),
 825            _ => {}
 826        }
 827    }
 828
 829    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
 830        if matches!(
 831            operator,
 832            Operator::Change
 833                | Operator::Delete
 834                | Operator::Replace
 835                | Operator::Indent
 836                | Operator::Outdent
 837                | Operator::AutoIndent
 838                | Operator::Lowercase
 839                | Operator::Uppercase
 840                | Operator::OppositeCase
 841                | Operator::ToggleComments
 842                | Operator::ReplaceWithRegister
 843        ) {
 844            self.start_recording(cx)
 845        };
 846        // Since these operations can only be entered with pre-operators,
 847        // we need to clear the previous operators when pushing,
 848        // so that the current stack is the most correct
 849        if matches!(
 850            operator,
 851            Operator::AddSurrounds { .. }
 852                | Operator::ChangeSurrounds { .. }
 853                | Operator::DeleteSurrounds
 854        ) {
 855            self.operator_stack.clear();
 856            if let Operator::AddSurrounds { target: None } = operator {
 857                self.start_recording(cx);
 858            }
 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) -> 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 => CursorShape::Bar,
1026        }
1027    }
1028
1029    pub fn editor_input_enabled(&self) -> bool {
1030        match self.mode {
1031            Mode::Insert => {
1032                if let Some(operator) = self.operator_stack.last() {
1033                    !operator.is_waiting(self.mode)
1034                } else {
1035                    true
1036                }
1037            }
1038            Mode::Normal
1039            | Mode::HelixNormal
1040            | Mode::Replace
1041            | Mode::Visual
1042            | Mode::VisualLine
1043            | Mode::VisualBlock => false,
1044        }
1045    }
1046
1047    pub fn should_autoindent(&self) -> bool {
1048        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1049    }
1050
1051    pub fn clip_at_line_ends(&self) -> bool {
1052        match self.mode {
1053            Mode::Insert
1054            | Mode::Visual
1055            | Mode::VisualLine
1056            | Mode::VisualBlock
1057            | Mode::Replace
1058            | Mode::HelixNormal => false,
1059            Mode::Normal => true,
1060        }
1061    }
1062
1063    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1064        let mut mode = match self.mode {
1065            Mode::Normal => "normal",
1066            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1067            Mode::Insert => "insert",
1068            Mode::Replace => "replace",
1069            Mode::HelixNormal => "helix_normal",
1070        }
1071        .to_string();
1072
1073        let mut operator_id = "none";
1074
1075        let active_operator = self.active_operator();
1076        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1077            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1078        {
1079            context.add("VimCount");
1080        }
1081
1082        if let Some(active_operator) = active_operator {
1083            if active_operator.is_waiting(self.mode) {
1084                if matches!(active_operator, Operator::Literal { .. }) {
1085                    mode = "literal".to_string();
1086                } else {
1087                    mode = "waiting".to_string();
1088                }
1089            } else {
1090                operator_id = active_operator.id();
1091                mode = "operator".to_string();
1092            }
1093        }
1094
1095        if mode == "normal" || mode == "visual" || mode == "operator" {
1096            context.add("VimControl");
1097        }
1098        context.set("vim_mode", mode);
1099        context.set("vim_operator", operator_id);
1100    }
1101
1102    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1103        let Some(editor) = self.editor() else {
1104            return;
1105        };
1106        let newest_selection_empty = editor.update(cx, |editor, cx| {
1107            editor.selections.newest::<usize>(cx).is_empty()
1108        });
1109        let editor = editor.read(cx);
1110        let editor_mode = editor.mode();
1111
1112        if editor_mode == EditorMode::Full
1113                && !newest_selection_empty
1114                && self.mode == Mode::Normal
1115                // When following someone, don't switch vim mode.
1116                && editor.leader_peer_id().is_none()
1117        {
1118            if preserve_selection {
1119                self.switch_mode(Mode::Visual, true, window, cx);
1120            } else {
1121                self.update_editor(window, cx, |_, editor, window, cx| {
1122                    editor.set_clip_at_line_ends(false, cx);
1123                    editor.change_selections(None, window, cx, |s| {
1124                        s.move_with(|_, selection| {
1125                            selection.collapse_to(selection.start, selection.goal)
1126                        })
1127                    });
1128                });
1129            }
1130        }
1131
1132        cx.emit(VimEvent::Focused);
1133        self.sync_vim_settings(window, cx);
1134
1135        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1136            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1137                if old_vim.entity_id() != cx.entity().entity_id() {
1138                    old_vim.update(cx, |vim, cx| {
1139                        vim.update_editor(window, cx, |_, editor, _, cx| {
1140                            editor.set_relative_line_number(None, cx)
1141                        });
1142                    });
1143
1144                    self.update_editor(window, cx, |vim, editor, _, cx| {
1145                        let is_relative = vim.mode != Mode::Insert;
1146                        editor.set_relative_line_number(Some(is_relative), cx)
1147                    });
1148                }
1149            } else {
1150                self.update_editor(window, cx, |vim, editor, _, cx| {
1151                    let is_relative = vim.mode != Mode::Insert;
1152                    editor.set_relative_line_number(Some(is_relative), cx)
1153                });
1154            }
1155        }
1156        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1157    }
1158
1159    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1160        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1161        self.store_visual_marks(window, cx);
1162        self.clear_operator(window, cx);
1163        self.update_editor(window, cx, |vim, editor, _, cx| {
1164            if vim.cursor_shape() == CursorShape::Block {
1165                editor.set_cursor_shape(CursorShape::Hollow, cx);
1166            }
1167        });
1168    }
1169
1170    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1171        self.update_editor(window, cx, |vim, editor, _, cx| {
1172            editor.set_cursor_shape(vim.cursor_shape(), cx);
1173        });
1174    }
1175
1176    fn update_editor<S>(
1177        &mut self,
1178        window: &mut Window,
1179        cx: &mut Context<Self>,
1180        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1181    ) -> Option<S> {
1182        let editor = self.editor.upgrade()?;
1183        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1184    }
1185
1186    fn editor_selections(
1187        &mut self,
1188        window: &mut Window,
1189        cx: &mut Context<Self>,
1190    ) -> Vec<Range<Anchor>> {
1191        self.update_editor(window, cx, |_, editor, _, _| {
1192            editor
1193                .selections
1194                .disjoint_anchors()
1195                .iter()
1196                .map(|selection| selection.tail()..selection.head())
1197                .collect()
1198        })
1199        .unwrap_or_default()
1200    }
1201
1202    fn editor_cursor_word(
1203        &mut self,
1204        window: &mut Window,
1205        cx: &mut Context<Self>,
1206    ) -> Option<String> {
1207        self.update_editor(window, cx, |_, editor, window, cx| {
1208            let selection = editor.selections.newest::<usize>(cx);
1209
1210            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1211            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1212            if kind == Some(CharKind::Word) {
1213                let text: String = snapshot.text_for_range(range).collect();
1214                if !text.trim().is_empty() {
1215                    return Some(text);
1216                }
1217            }
1218
1219            None
1220        })
1221        .unwrap_or_default()
1222    }
1223
1224    /// When doing an action that modifies the buffer, we start recording so that `.`
1225    /// will replay the action.
1226    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1227        Vim::update_globals(cx, |globals, cx| {
1228            if !globals.dot_replaying {
1229                globals.dot_recording = true;
1230                globals.recording_actions = Default::default();
1231                globals.recorded_count = None;
1232
1233                let selections = self.editor().map(|editor| {
1234                    editor.update(cx, |editor, cx| {
1235                        (
1236                            editor.selections.oldest::<Point>(cx),
1237                            editor.selections.newest::<Point>(cx),
1238                        )
1239                    })
1240                });
1241
1242                if let Some((oldest, newest)) = selections {
1243                    globals.recorded_selection = match self.mode {
1244                        Mode::Visual if newest.end.row == newest.start.row => {
1245                            RecordedSelection::SingleLine {
1246                                cols: newest.end.column - newest.start.column,
1247                            }
1248                        }
1249                        Mode::Visual => RecordedSelection::Visual {
1250                            rows: newest.end.row - newest.start.row,
1251                            cols: newest.end.column,
1252                        },
1253                        Mode::VisualLine => RecordedSelection::VisualLine {
1254                            rows: newest.end.row - newest.start.row,
1255                        },
1256                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1257                            rows: newest.end.row.abs_diff(oldest.start.row),
1258                            cols: newest.end.column.abs_diff(oldest.start.column),
1259                        },
1260                        _ => RecordedSelection::None,
1261                    }
1262                } else {
1263                    globals.recorded_selection = RecordedSelection::None;
1264                }
1265            }
1266        })
1267    }
1268
1269    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1270        let globals = Vim::globals(cx);
1271        globals.dot_replaying = false;
1272        if let Some(replayer) = globals.replayer.take() {
1273            replayer.stop();
1274        }
1275    }
1276
1277    /// When finishing an action that modifies the buffer, stop recording.
1278    /// as you usually call this within a keystroke handler we also ensure that
1279    /// the current action is recorded.
1280    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1281        let globals = Vim::globals(cx);
1282        if globals.dot_recording {
1283            globals.stop_recording_after_next_action = true;
1284        }
1285        self.exit_temporary_mode = self.temp_mode;
1286    }
1287
1288    /// Stops recording actions immediately rather than waiting until after the
1289    /// next action to stop recording.
1290    ///
1291    /// This doesn't include the current action.
1292    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1293        let globals = Vim::globals(cx);
1294        if globals.dot_recording {
1295            globals
1296                .recording_actions
1297                .push(ReplayableAction::Action(action.boxed_clone()));
1298            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1299            globals.dot_recording = false;
1300            globals.stop_recording_after_next_action = false;
1301        }
1302        self.exit_temporary_mode = self.temp_mode;
1303    }
1304
1305    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1306    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1307        self.start_recording(cx);
1308        self.stop_recording(cx);
1309    }
1310
1311    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1312        if self.active_operator().is_some() {
1313            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1314
1315            Vim::globals(cx).post_count = Some(
1316                post_count
1317                    .checked_mul(10)
1318                    .and_then(|post_count| post_count.checked_add(number))
1319                    .unwrap_or(post_count),
1320            )
1321        } else {
1322            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1323
1324            Vim::globals(cx).pre_count = Some(
1325                pre_count
1326                    .checked_mul(10)
1327                    .and_then(|pre_count| pre_count.checked_add(number))
1328                    .unwrap_or(pre_count),
1329            )
1330        }
1331        // update the keymap so that 0 works
1332        self.sync_vim_settings(window, cx)
1333    }
1334
1335    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1336        if register.chars().count() == 1 {
1337            self.selected_register
1338                .replace(register.chars().next().unwrap());
1339        }
1340        self.operator_stack.clear();
1341        self.sync_vim_settings(window, cx);
1342    }
1343
1344    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1345        self.operator_stack.pop()
1346    }
1347
1348    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1349        let popped_operator = self.operator_stack.pop()
1350            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1351        self.sync_vim_settings(window, cx);
1352        popped_operator
1353    }
1354
1355    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1356        Vim::take_count(cx);
1357        self.selected_register.take();
1358        self.operator_stack.clear();
1359        self.sync_vim_settings(window, cx);
1360    }
1361
1362    fn active_operator(&self) -> Option<Operator> {
1363        self.operator_stack.last().cloned()
1364    }
1365
1366    fn transaction_begun(
1367        &mut self,
1368        transaction_id: TransactionId,
1369        _window: &mut Window,
1370        _: &mut Context<Self>,
1371    ) {
1372        let mode = if (self.mode == Mode::Insert
1373            || self.mode == Mode::Replace
1374            || self.mode == Mode::Normal)
1375            && self.current_tx.is_none()
1376        {
1377            self.current_tx = Some(transaction_id);
1378            self.last_mode
1379        } else {
1380            self.mode
1381        };
1382        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1383            self.undo_modes.insert(transaction_id, mode);
1384        }
1385    }
1386
1387    fn transaction_undone(
1388        &mut self,
1389        transaction_id: &TransactionId,
1390        window: &mut Window,
1391        cx: &mut Context<Self>,
1392    ) {
1393        match self.mode {
1394            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1395                self.update_editor(window, cx, |vim, editor, window, cx| {
1396                    let original_mode = vim.undo_modes.get(transaction_id);
1397                    editor.change_selections(None, window, cx, |s| match original_mode {
1398                        Some(Mode::VisualLine) => {
1399                            s.move_with(|map, selection| {
1400                                selection.collapse_to(
1401                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1402                                    SelectionGoal::None,
1403                                )
1404                            });
1405                        }
1406                        Some(Mode::VisualBlock) => {
1407                            let mut first = s.first_anchor();
1408                            first.collapse_to(first.start, first.goal);
1409                            s.select_anchors(vec![first]);
1410                        }
1411                        _ => {
1412                            s.move_with(|map, selection| {
1413                                selection.collapse_to(
1414                                    map.clip_at_line_end(selection.start),
1415                                    selection.goal,
1416                                );
1417                            });
1418                        }
1419                    });
1420                });
1421                self.switch_mode(Mode::Normal, true, window, cx)
1422            }
1423            Mode::Normal => {
1424                self.update_editor(window, cx, |_, editor, window, cx| {
1425                    editor.change_selections(None, window, cx, |s| {
1426                        s.move_with(|map, selection| {
1427                            selection
1428                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1429                        })
1430                    })
1431                });
1432            }
1433            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1434        }
1435    }
1436
1437    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1438        let Some(editor) = self.editor() else { return };
1439
1440        if editor.read(cx).leader_peer_id().is_some() {
1441            return;
1442        }
1443
1444        let newest = editor.read(cx).selections.newest_anchor().clone();
1445        let is_multicursor = editor.read(cx).selections.count() > 1;
1446        if self.mode == Mode::Insert && self.current_tx.is_some() {
1447            if self.current_anchor.is_none() {
1448                self.current_anchor = Some(newest);
1449            } else if self.current_anchor.as_ref().unwrap() != &newest {
1450                if let Some(tx_id) = self.current_tx.take() {
1451                    self.update_editor(window, cx, |_, editor, _, cx| {
1452                        editor.group_until_transaction(tx_id, cx)
1453                    });
1454                }
1455            }
1456        } else if self.mode == Mode::Normal && newest.start != newest.end {
1457            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1458                self.switch_mode(Mode::VisualBlock, false, window, cx);
1459            } else {
1460                self.switch_mode(Mode::Visual, false, window, cx)
1461            }
1462        } else if newest.start == newest.end
1463            && !is_multicursor
1464            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1465        {
1466            self.switch_mode(Mode::Normal, true, window, cx);
1467        }
1468    }
1469
1470    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1471        if text.is_empty() {
1472            return;
1473        }
1474
1475        match self.active_operator() {
1476            Some(Operator::FindForward { before }) => {
1477                let find = Motion::FindForward {
1478                    before,
1479                    char: text.chars().next().unwrap(),
1480                    mode: if VimSettings::get_global(cx).use_multiline_find {
1481                        FindRange::MultiLine
1482                    } else {
1483                        FindRange::SingleLine
1484                    },
1485                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1486                };
1487                Vim::globals(cx).last_find = Some(find.clone());
1488                self.motion(find, window, cx)
1489            }
1490            Some(Operator::FindBackward { after }) => {
1491                let find = Motion::FindBackward {
1492                    after,
1493                    char: text.chars().next().unwrap(),
1494                    mode: if VimSettings::get_global(cx).use_multiline_find {
1495                        FindRange::MultiLine
1496                    } else {
1497                        FindRange::SingleLine
1498                    },
1499                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1500                };
1501                Vim::globals(cx).last_find = Some(find.clone());
1502                self.motion(find, window, cx)
1503            }
1504            Some(Operator::Sneak { first_char }) => {
1505                if let Some(first_char) = first_char {
1506                    if let Some(second_char) = text.chars().next() {
1507                        let sneak = Motion::Sneak {
1508                            first_char,
1509                            second_char,
1510                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1511                        };
1512                        Vim::globals(cx).last_find = Some((&sneak).clone());
1513                        self.motion(sneak, window, cx)
1514                    }
1515                } else {
1516                    let first_char = text.chars().next();
1517                    self.pop_operator(window, cx);
1518                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1519                }
1520            }
1521            Some(Operator::SneakBackward { first_char }) => {
1522                if let Some(first_char) = first_char {
1523                    if let Some(second_char) = text.chars().next() {
1524                        let sneak = Motion::SneakBackward {
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::SneakBackward { first_char }, window, cx);
1536                }
1537            }
1538            Some(Operator::Replace) => match self.mode {
1539                Mode::Normal => self.normal_replace(text, window, cx),
1540                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1541                    self.visual_replace(text, window, cx)
1542                }
1543                _ => self.clear_operator(window, cx),
1544            },
1545            Some(Operator::Digraph { first_char }) => {
1546                if let Some(first_char) = first_char {
1547                    if let Some(second_char) = text.chars().next() {
1548                        self.insert_digraph(first_char, second_char, window, cx);
1549                    }
1550                } else {
1551                    let first_char = text.chars().next();
1552                    self.pop_operator(window, cx);
1553                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1554                }
1555            }
1556            Some(Operator::Literal { prefix }) => {
1557                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1558            }
1559            Some(Operator::AddSurrounds { target }) => match self.mode {
1560                Mode::Normal => {
1561                    if let Some(target) = target {
1562                        self.add_surrounds(text, target, window, cx);
1563                        self.clear_operator(window, cx);
1564                    }
1565                }
1566                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1567                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1568                    self.clear_operator(window, cx);
1569                }
1570                _ => self.clear_operator(window, cx),
1571            },
1572            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1573                Mode::Normal => {
1574                    if let Some(target) = target {
1575                        self.change_surrounds(text, target, window, cx);
1576                        self.clear_operator(window, cx);
1577                    }
1578                }
1579                _ => self.clear_operator(window, cx),
1580            },
1581            Some(Operator::DeleteSurrounds) => match self.mode {
1582                Mode::Normal => {
1583                    self.delete_surrounds(text, window, cx);
1584                    self.clear_operator(window, cx);
1585                }
1586                _ => self.clear_operator(window, cx),
1587            },
1588            Some(Operator::Mark) => self.create_mark(text, false, window, cx),
1589            Some(Operator::RecordRegister) => {
1590                self.record_register(text.chars().next().unwrap(), window, cx)
1591            }
1592            Some(Operator::ReplayRegister) => {
1593                self.replay_register(text.chars().next().unwrap(), window, cx)
1594            }
1595            Some(Operator::Register) => match self.mode {
1596                Mode::Insert => {
1597                    self.update_editor(window, cx, |_, editor, window, cx| {
1598                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1599                            globals.read_register(text.chars().next(), Some(editor), cx)
1600                        }) {
1601                            editor.do_paste(
1602                                &register.text.to_string(),
1603                                register.clipboard_selections.clone(),
1604                                false,
1605                                window,
1606                                cx,
1607                            )
1608                        }
1609                    });
1610                    self.clear_operator(window, cx);
1611                }
1612                _ => {
1613                    self.select_register(text, window, cx);
1614                }
1615            },
1616            Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
1617            _ => {
1618                if self.mode == Mode::Replace {
1619                    self.multi_replace(text, window, cx)
1620                }
1621
1622                if self.mode == Mode::Normal {
1623                    self.update_editor(window, cx, |_, editor, window, cx| {
1624                        editor.accept_edit_prediction(
1625                            &editor::actions::AcceptEditPrediction {},
1626                            window,
1627                            cx,
1628                        );
1629                    });
1630                }
1631            }
1632        }
1633    }
1634
1635    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1636        self.update_editor(window, cx, |vim, editor, window, cx| {
1637            editor.set_cursor_shape(vim.cursor_shape(), cx);
1638            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1639            editor.set_collapse_matches(true);
1640            editor.set_input_enabled(vim.editor_input_enabled());
1641            editor.set_autoindent(vim.should_autoindent());
1642            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1643
1644            let hide_inline_completions = match vim.mode {
1645                Mode::Insert | Mode::Replace => false,
1646                _ => true,
1647            };
1648            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1649        });
1650        cx.notify()
1651    }
1652}
1653
1654/// Controls when to use system clipboard.
1655#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1656#[serde(rename_all = "snake_case")]
1657pub enum UseSystemClipboard {
1658    /// Don't use system clipboard.
1659    Never,
1660    /// Use system clipboard.
1661    Always,
1662    /// Use system clipboard for yank operations.
1663    OnYank,
1664}
1665
1666#[derive(Deserialize)]
1667struct VimSettings {
1668    pub default_mode: Mode,
1669    pub toggle_relative_line_numbers: bool,
1670    pub use_system_clipboard: UseSystemClipboard,
1671    pub use_multiline_find: bool,
1672    pub use_smartcase_find: bool,
1673    pub custom_digraphs: HashMap<String, Arc<str>>,
1674    pub highlight_on_yank_duration: u64,
1675}
1676
1677#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1678struct VimSettingsContent {
1679    pub default_mode: Option<ModeContent>,
1680    pub toggle_relative_line_numbers: Option<bool>,
1681    pub use_system_clipboard: Option<UseSystemClipboard>,
1682    pub use_multiline_find: Option<bool>,
1683    pub use_smartcase_find: Option<bool>,
1684    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1685    pub highlight_on_yank_duration: Option<u64>,
1686}
1687
1688#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1689#[serde(rename_all = "snake_case")]
1690pub enum ModeContent {
1691    #[default]
1692    Normal,
1693    Insert,
1694    Replace,
1695    Visual,
1696    VisualLine,
1697    VisualBlock,
1698    HelixNormal,
1699}
1700
1701impl From<ModeContent> for Mode {
1702    fn from(mode: ModeContent) -> Self {
1703        match mode {
1704            ModeContent::Normal => Self::Normal,
1705            ModeContent::Insert => Self::Insert,
1706            ModeContent::Replace => Self::Replace,
1707            ModeContent::Visual => Self::Visual,
1708            ModeContent::VisualLine => Self::VisualLine,
1709            ModeContent::VisualBlock => Self::VisualBlock,
1710            ModeContent::HelixNormal => Self::HelixNormal,
1711        }
1712    }
1713}
1714
1715impl Settings for VimSettings {
1716    const KEY: Option<&'static str> = Some("vim");
1717
1718    type FileContent = VimSettingsContent;
1719
1720    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1721        let settings: VimSettingsContent = sources.json_merge()?;
1722
1723        Ok(Self {
1724            default_mode: settings
1725                .default_mode
1726                .ok_or_else(Self::missing_default)?
1727                .into(),
1728            toggle_relative_line_numbers: settings
1729                .toggle_relative_line_numbers
1730                .ok_or_else(Self::missing_default)?,
1731            use_system_clipboard: settings
1732                .use_system_clipboard
1733                .ok_or_else(Self::missing_default)?,
1734            use_multiline_find: settings
1735                .use_multiline_find
1736                .ok_or_else(Self::missing_default)?,
1737            use_smartcase_find: settings
1738                .use_smartcase_find
1739                .ok_or_else(Self::missing_default)?,
1740            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1741            highlight_on_yank_duration: settings
1742                .highlight_on_yank_duration
1743                .ok_or_else(Self::missing_default)?,
1744        })
1745    }
1746}