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::{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    /// When doing an action that modifies the buffer, we start recording so that `.`
1203    /// will replay the action.
1204    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1205        Vim::update_globals(cx, |globals, cx| {
1206            if !globals.dot_replaying {
1207                globals.dot_recording = true;
1208                globals.recording_actions = Default::default();
1209                globals.recorded_count = None;
1210
1211                let selections = self.editor().map(|editor| {
1212                    editor.update(cx, |editor, cx| {
1213                        (
1214                            editor.selections.oldest::<Point>(cx),
1215                            editor.selections.newest::<Point>(cx),
1216                        )
1217                    })
1218                });
1219
1220                if let Some((oldest, newest)) = selections {
1221                    globals.recorded_selection = match self.mode {
1222                        Mode::Visual if newest.end.row == newest.start.row => {
1223                            RecordedSelection::SingleLine {
1224                                cols: newest.end.column - newest.start.column,
1225                            }
1226                        }
1227                        Mode::Visual => RecordedSelection::Visual {
1228                            rows: newest.end.row - newest.start.row,
1229                            cols: newest.end.column,
1230                        },
1231                        Mode::VisualLine => RecordedSelection::VisualLine {
1232                            rows: newest.end.row - newest.start.row,
1233                        },
1234                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1235                            rows: newest.end.row.abs_diff(oldest.start.row),
1236                            cols: newest.end.column.abs_diff(oldest.start.column),
1237                        },
1238                        _ => RecordedSelection::None,
1239                    }
1240                } else {
1241                    globals.recorded_selection = RecordedSelection::None;
1242                }
1243            }
1244        })
1245    }
1246
1247    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1248        let globals = Vim::globals(cx);
1249        globals.dot_replaying = false;
1250        if let Some(replayer) = globals.replayer.take() {
1251            replayer.stop();
1252        }
1253    }
1254
1255    /// When finishing an action that modifies the buffer, stop recording.
1256    /// as you usually call this within a keystroke handler we also ensure that
1257    /// the current action is recorded.
1258    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1259        let globals = Vim::globals(cx);
1260        if globals.dot_recording {
1261            globals.stop_recording_after_next_action = true;
1262        }
1263        self.exit_temporary_mode = self.temp_mode;
1264    }
1265
1266    /// Stops recording actions immediately rather than waiting until after the
1267    /// next action to stop recording.
1268    ///
1269    /// This doesn't include the current action.
1270    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1271        let globals = Vim::globals(cx);
1272        if globals.dot_recording {
1273            globals
1274                .recording_actions
1275                .push(ReplayableAction::Action(action.boxed_clone()));
1276            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1277            globals.dot_recording = false;
1278            globals.stop_recording_after_next_action = false;
1279        }
1280        self.exit_temporary_mode = self.temp_mode;
1281    }
1282
1283    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1284    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1285        self.start_recording(cx);
1286        self.stop_recording(cx);
1287    }
1288
1289    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1290        if self.active_operator().is_some() {
1291            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1292
1293            Vim::globals(cx).post_count = Some(
1294                post_count
1295                    .checked_mul(10)
1296                    .and_then(|post_count| post_count.checked_add(number))
1297                    .unwrap_or(post_count),
1298            )
1299        } else {
1300            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1301
1302            Vim::globals(cx).pre_count = Some(
1303                pre_count
1304                    .checked_mul(10)
1305                    .and_then(|pre_count| pre_count.checked_add(number))
1306                    .unwrap_or(pre_count),
1307            )
1308        }
1309        // update the keymap so that 0 works
1310        self.sync_vim_settings(window, cx)
1311    }
1312
1313    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1314        if register.chars().count() == 1 {
1315            self.selected_register
1316                .replace(register.chars().next().unwrap());
1317        }
1318        self.operator_stack.clear();
1319        self.sync_vim_settings(window, cx);
1320    }
1321
1322    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1323        self.operator_stack.pop()
1324    }
1325
1326    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1327        let popped_operator = self.operator_stack.pop()
1328            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1329        self.sync_vim_settings(window, cx);
1330        popped_operator
1331    }
1332
1333    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1334        Vim::take_count(cx);
1335        self.selected_register.take();
1336        self.operator_stack.clear();
1337        self.sync_vim_settings(window, cx);
1338    }
1339
1340    fn active_operator(&self) -> Option<Operator> {
1341        self.operator_stack.last().cloned()
1342    }
1343
1344    fn transaction_begun(
1345        &mut self,
1346        transaction_id: TransactionId,
1347        _window: &mut Window,
1348        _: &mut Context<Self>,
1349    ) {
1350        let mode = if (self.mode == Mode::Insert
1351            || self.mode == Mode::Replace
1352            || self.mode == Mode::Normal)
1353            && self.current_tx.is_none()
1354        {
1355            self.current_tx = Some(transaction_id);
1356            self.last_mode
1357        } else {
1358            self.mode
1359        };
1360        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1361            self.undo_modes.insert(transaction_id, mode);
1362        }
1363    }
1364
1365    fn transaction_undone(
1366        &mut self,
1367        transaction_id: &TransactionId,
1368        window: &mut Window,
1369        cx: &mut Context<Self>,
1370    ) {
1371        match self.mode {
1372            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1373                self.update_editor(window, cx, |vim, editor, window, cx| {
1374                    let original_mode = vim.undo_modes.get(transaction_id);
1375                    editor.change_selections(None, window, cx, |s| match original_mode {
1376                        Some(Mode::VisualLine) => {
1377                            s.move_with(|map, selection| {
1378                                selection.collapse_to(
1379                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1380                                    SelectionGoal::None,
1381                                )
1382                            });
1383                        }
1384                        Some(Mode::VisualBlock) => {
1385                            let mut first = s.first_anchor();
1386                            first.collapse_to(first.start, first.goal);
1387                            s.select_anchors(vec![first]);
1388                        }
1389                        _ => {
1390                            s.move_with(|map, selection| {
1391                                selection.collapse_to(
1392                                    map.clip_at_line_end(selection.start),
1393                                    selection.goal,
1394                                );
1395                            });
1396                        }
1397                    });
1398                });
1399                self.switch_mode(Mode::Normal, true, window, cx)
1400            }
1401            Mode::Normal => {
1402                self.update_editor(window, cx, |_, editor, window, cx| {
1403                    editor.change_selections(None, window, cx, |s| {
1404                        s.move_with(|map, selection| {
1405                            selection
1406                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1407                        })
1408                    })
1409                });
1410            }
1411            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1412        }
1413    }
1414
1415    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1416        let Some(editor) = self.editor() else { return };
1417
1418        if editor.read(cx).leader_peer_id().is_some() {
1419            return;
1420        }
1421
1422        let newest = editor.read(cx).selections.newest_anchor().clone();
1423        let is_multicursor = editor.read(cx).selections.count() > 1;
1424        if self.mode == Mode::Insert && self.current_tx.is_some() {
1425            if self.current_anchor.is_none() {
1426                self.current_anchor = Some(newest);
1427            } else if self.current_anchor.as_ref().unwrap() != &newest {
1428                if let Some(tx_id) = self.current_tx.take() {
1429                    self.update_editor(window, cx, |_, editor, _, cx| {
1430                        editor.group_until_transaction(tx_id, cx)
1431                    });
1432                }
1433            }
1434        } else if self.mode == Mode::Normal && newest.start != newest.end {
1435            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1436                self.switch_mode(Mode::VisualBlock, false, window, cx);
1437            } else {
1438                self.switch_mode(Mode::Visual, false, window, cx)
1439            }
1440        } else if newest.start == newest.end
1441            && !is_multicursor
1442            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1443        {
1444            self.switch_mode(Mode::Normal, true, window, cx);
1445        }
1446    }
1447
1448    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1449        if text.is_empty() {
1450            return;
1451        }
1452
1453        match self.active_operator() {
1454            Some(Operator::FindForward { before }) => {
1455                let find = Motion::FindForward {
1456                    before,
1457                    char: text.chars().next().unwrap(),
1458                    mode: if VimSettings::get_global(cx).use_multiline_find {
1459                        FindRange::MultiLine
1460                    } else {
1461                        FindRange::SingleLine
1462                    },
1463                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1464                };
1465                Vim::globals(cx).last_find = Some(find.clone());
1466                self.motion(find, window, cx)
1467            }
1468            Some(Operator::FindBackward { after }) => {
1469                let find = Motion::FindBackward {
1470                    after,
1471                    char: text.chars().next().unwrap(),
1472                    mode: if VimSettings::get_global(cx).use_multiline_find {
1473                        FindRange::MultiLine
1474                    } else {
1475                        FindRange::SingleLine
1476                    },
1477                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1478                };
1479                Vim::globals(cx).last_find = Some(find.clone());
1480                self.motion(find, window, cx)
1481            }
1482            Some(Operator::Sneak { first_char }) => {
1483                if let Some(first_char) = first_char {
1484                    if let Some(second_char) = text.chars().next() {
1485                        let sneak = Motion::Sneak {
1486                            first_char,
1487                            second_char,
1488                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1489                        };
1490                        Vim::globals(cx).last_find = Some((&sneak).clone());
1491                        self.motion(sneak, window, cx)
1492                    }
1493                } else {
1494                    let first_char = text.chars().next();
1495                    self.pop_operator(window, cx);
1496                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1497                }
1498            }
1499            Some(Operator::SneakBackward { first_char }) => {
1500                if let Some(first_char) = first_char {
1501                    if let Some(second_char) = text.chars().next() {
1502                        let sneak = Motion::SneakBackward {
1503                            first_char,
1504                            second_char,
1505                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1506                        };
1507                        Vim::globals(cx).last_find = Some((&sneak).clone());
1508                        self.motion(sneak, window, cx)
1509                    }
1510                } else {
1511                    let first_char = text.chars().next();
1512                    self.pop_operator(window, cx);
1513                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1514                }
1515            }
1516            Some(Operator::Replace) => match self.mode {
1517                Mode::Normal => self.normal_replace(text, window, cx),
1518                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1519                    self.visual_replace(text, window, cx)
1520                }
1521                _ => self.clear_operator(window, cx),
1522            },
1523            Some(Operator::Digraph { first_char }) => {
1524                if let Some(first_char) = first_char {
1525                    if let Some(second_char) = text.chars().next() {
1526                        self.insert_digraph(first_char, second_char, window, cx);
1527                    }
1528                } else {
1529                    let first_char = text.chars().next();
1530                    self.pop_operator(window, cx);
1531                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1532                }
1533            }
1534            Some(Operator::Literal { prefix }) => {
1535                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1536            }
1537            Some(Operator::AddSurrounds { target }) => match self.mode {
1538                Mode::Normal => {
1539                    if let Some(target) = target {
1540                        self.add_surrounds(text, target, window, cx);
1541                        self.clear_operator(window, cx);
1542                    }
1543                }
1544                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1545                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1546                    self.clear_operator(window, cx);
1547                }
1548                _ => self.clear_operator(window, cx),
1549            },
1550            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1551                Mode::Normal => {
1552                    if let Some(target) = target {
1553                        self.change_surrounds(text, target, window, cx);
1554                        self.clear_operator(window, cx);
1555                    }
1556                }
1557                _ => self.clear_operator(window, cx),
1558            },
1559            Some(Operator::DeleteSurrounds) => match self.mode {
1560                Mode::Normal => {
1561                    self.delete_surrounds(text, window, cx);
1562                    self.clear_operator(window, cx);
1563                }
1564                _ => self.clear_operator(window, cx),
1565            },
1566            Some(Operator::Mark) => self.create_mark(text, false, window, cx),
1567            Some(Operator::RecordRegister) => {
1568                self.record_register(text.chars().next().unwrap(), window, cx)
1569            }
1570            Some(Operator::ReplayRegister) => {
1571                self.replay_register(text.chars().next().unwrap(), window, cx)
1572            }
1573            Some(Operator::Register) => match self.mode {
1574                Mode::Insert => {
1575                    self.update_editor(window, cx, |_, editor, window, cx| {
1576                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1577                            globals.read_register(text.chars().next(), Some(editor), cx)
1578                        }) {
1579                            editor.do_paste(
1580                                &register.text.to_string(),
1581                                register.clipboard_selections.clone(),
1582                                false,
1583                                window,
1584                                cx,
1585                            )
1586                        }
1587                    });
1588                    self.clear_operator(window, cx);
1589                }
1590                _ => {
1591                    self.select_register(text, window, cx);
1592                }
1593            },
1594            Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
1595            _ => {
1596                if self.mode == Mode::Replace {
1597                    self.multi_replace(text, window, cx)
1598                }
1599
1600                if self.mode == Mode::Normal {
1601                    self.update_editor(window, cx, |_, editor, window, cx| {
1602                        editor.accept_edit_prediction(
1603                            &editor::actions::AcceptEditPrediction {},
1604                            window,
1605                            cx,
1606                        );
1607                    });
1608                }
1609            }
1610        }
1611    }
1612
1613    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1614        self.update_editor(window, cx, |vim, editor, window, cx| {
1615            editor.set_cursor_shape(vim.cursor_shape(), cx);
1616            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1617            editor.set_collapse_matches(true);
1618            editor.set_input_enabled(vim.editor_input_enabled());
1619            editor.set_autoindent(vim.should_autoindent());
1620            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1621
1622            let hide_inline_completions = match vim.mode {
1623                Mode::Insert | Mode::Replace => false,
1624                _ => true,
1625            };
1626            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1627        });
1628        cx.notify()
1629    }
1630}
1631
1632/// Controls when to use system clipboard.
1633#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1634#[serde(rename_all = "snake_case")]
1635pub enum UseSystemClipboard {
1636    /// Don't use system clipboard.
1637    Never,
1638    /// Use system clipboard.
1639    Always,
1640    /// Use system clipboard for yank operations.
1641    OnYank,
1642}
1643
1644#[derive(Deserialize)]
1645struct VimSettings {
1646    pub default_mode: Mode,
1647    pub toggle_relative_line_numbers: bool,
1648    pub use_system_clipboard: UseSystemClipboard,
1649    pub use_multiline_find: bool,
1650    pub use_smartcase_find: bool,
1651    pub custom_digraphs: HashMap<String, Arc<str>>,
1652    pub highlight_on_yank_duration: u64,
1653}
1654
1655#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1656struct VimSettingsContent {
1657    pub default_mode: Option<ModeContent>,
1658    pub toggle_relative_line_numbers: Option<bool>,
1659    pub use_system_clipboard: Option<UseSystemClipboard>,
1660    pub use_multiline_find: Option<bool>,
1661    pub use_smartcase_find: Option<bool>,
1662    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1663    pub highlight_on_yank_duration: Option<u64>,
1664}
1665
1666#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1667#[serde(rename_all = "snake_case")]
1668pub enum ModeContent {
1669    #[default]
1670    Normal,
1671    Insert,
1672    Replace,
1673    Visual,
1674    VisualLine,
1675    VisualBlock,
1676    HelixNormal,
1677}
1678
1679impl From<ModeContent> for Mode {
1680    fn from(mode: ModeContent) -> Self {
1681        match mode {
1682            ModeContent::Normal => Self::Normal,
1683            ModeContent::Insert => Self::Insert,
1684            ModeContent::Replace => Self::Replace,
1685            ModeContent::Visual => Self::Visual,
1686            ModeContent::VisualLine => Self::VisualLine,
1687            ModeContent::VisualBlock => Self::VisualBlock,
1688            ModeContent::HelixNormal => Self::HelixNormal,
1689        }
1690    }
1691}
1692
1693impl Settings for VimSettings {
1694    const KEY: Option<&'static str> = Some("vim");
1695
1696    type FileContent = VimSettingsContent;
1697
1698    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1699        let settings: VimSettingsContent = sources.json_merge()?;
1700
1701        Ok(Self {
1702            default_mode: settings
1703                .default_mode
1704                .ok_or_else(Self::missing_default)?
1705                .into(),
1706            toggle_relative_line_numbers: settings
1707                .toggle_relative_line_numbers
1708                .ok_or_else(Self::missing_default)?,
1709            use_system_clipboard: settings
1710                .use_system_clipboard
1711                .ok_or_else(Self::missing_default)?,
1712            use_multiline_find: settings
1713                .use_multiline_find
1714                .ok_or_else(Self::missing_default)?,
1715            use_smartcase_find: settings
1716                .use_smartcase_find
1717                .ok_or_else(Self::missing_default)?,
1718            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1719            highlight_on_yank_duration: settings
1720                .highlight_on_yank_duration
1721                .ok_or_else(Self::missing_default)?,
1722        })
1723    }
1724}