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 operator.starts_dot_recording() {
 831            self.start_recording(cx);
 832        }
 833        // Since these operations can only be entered with pre-operators,
 834        // we need to clear the previous operators when pushing,
 835        // so that the current stack is the most correct
 836        if matches!(
 837            operator,
 838            Operator::AddSurrounds { .. }
 839                | Operator::ChangeSurrounds { .. }
 840                | Operator::DeleteSurrounds
 841        ) {
 842            self.operator_stack.clear();
 843        };
 844        self.operator_stack.push(operator);
 845        self.sync_vim_settings(window, cx);
 846    }
 847
 848    pub fn switch_mode(
 849        &mut self,
 850        mode: Mode,
 851        leave_selections: bool,
 852        window: &mut Window,
 853        cx: &mut Context<Self>,
 854    ) {
 855        if self.temp_mode && mode == Mode::Normal {
 856            self.temp_mode = false;
 857            self.switch_mode(Mode::Normal, leave_selections, window, cx);
 858            self.switch_mode(Mode::Insert, false, window, cx);
 859            return;
 860        } else if self.temp_mode
 861            && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
 862        {
 863            self.temp_mode = false;
 864        }
 865
 866        let last_mode = self.mode;
 867        let prior_mode = self.last_mode;
 868        let prior_tx = self.current_tx;
 869        self.status_label.take();
 870        self.last_mode = last_mode;
 871        self.mode = mode;
 872        self.operator_stack.clear();
 873        self.selected_register.take();
 874        self.cancel_running_command(window, cx);
 875        if mode == Mode::Normal || mode != last_mode {
 876            self.current_tx.take();
 877            self.current_anchor.take();
 878        }
 879        if mode != Mode::Insert && mode != Mode::Replace {
 880            Vim::take_count(cx);
 881        }
 882
 883        // Sync editor settings like clip mode
 884        self.sync_vim_settings(window, cx);
 885
 886        if VimSettings::get_global(cx).toggle_relative_line_numbers
 887            && self.mode != self.last_mode
 888            && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
 889        {
 890            self.update_editor(window, cx, |vim, editor, _, cx| {
 891                let is_relative = vim.mode != Mode::Insert;
 892                editor.set_relative_line_number(Some(is_relative), cx)
 893            });
 894        }
 895
 896        if leave_selections {
 897            return;
 898        }
 899
 900        if !mode.is_visual() && last_mode.is_visual() {
 901            self.create_visual_marks(last_mode, window, cx);
 902        }
 903
 904        // Adjust selections
 905        self.update_editor(window, cx, |vim, editor, window, cx| {
 906            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 907            {
 908                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
 909                    Some((point, goal))
 910                })
 911            }
 912            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 913                if let Some(prior_tx) = prior_tx {
 914                    editor.group_until_transaction(prior_tx, cx)
 915                }
 916            }
 917
 918            editor.change_selections(None, window, cx, |s| {
 919                // we cheat with visual block mode and use multiple cursors.
 920                // the cost of this cheat is we need to convert back to a single
 921                // cursor whenever vim would.
 922                if last_mode == Mode::VisualBlock
 923                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 924                {
 925                    let tail = s.oldest_anchor().tail();
 926                    let head = s.newest_anchor().head();
 927                    s.select_anchor_ranges(vec![tail..head]);
 928                } else if last_mode == Mode::Insert
 929                    && prior_mode == Mode::VisualBlock
 930                    && mode != Mode::VisualBlock
 931                {
 932                    let pos = s.first_anchor().head();
 933                    s.select_anchor_ranges(vec![pos..pos])
 934                }
 935
 936                let snapshot = s.display_map();
 937                if let Some(pending) = s.pending.as_mut() {
 938                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 939                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 940                        end = snapshot
 941                            .buffer_snapshot
 942                            .clip_point(end + Point::new(0, 1), Bias::Right);
 943                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 944                    }
 945                }
 946
 947                s.move_with(|map, selection| {
 948                    if last_mode.is_visual() && !mode.is_visual() {
 949                        let mut point = selection.head();
 950                        if !selection.reversed && !selection.is_empty() {
 951                            point = movement::left(map, selection.head());
 952                        }
 953                        selection.collapse_to(point, selection.goal)
 954                    } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
 955                        selection.end = movement::right(map, selection.start);
 956                    }
 957                });
 958            })
 959        });
 960    }
 961
 962    pub fn take_count(cx: &mut App) -> Option<usize> {
 963        let global_state = cx.global_mut::<VimGlobals>();
 964        if global_state.dot_replaying {
 965            return global_state.recorded_count;
 966        }
 967
 968        let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
 969            return None;
 970        } else {
 971            Some(
 972                global_state.post_count.take().unwrap_or(1)
 973                    * global_state.pre_count.take().unwrap_or(1),
 974            )
 975        };
 976
 977        if global_state.dot_recording {
 978            global_state.recorded_count = count;
 979        }
 980        count
 981    }
 982
 983    pub fn cursor_shape(&self) -> CursorShape {
 984        match self.mode {
 985            Mode::Normal => {
 986                if let Some(operator) = self.operator_stack.last() {
 987                    match operator {
 988                        // Navigation operators -> Block cursor
 989                        Operator::FindForward { .. }
 990                        | Operator::FindBackward { .. }
 991                        | Operator::Mark
 992                        | Operator::Jump { .. }
 993                        | Operator::Register
 994                        | Operator::RecordRegister
 995                        | Operator::ReplayRegister => CursorShape::Block,
 996
 997                        // All other operators -> Underline cursor
 998                        _ => CursorShape::Underline,
 999                    }
1000                } else {
1001                    // No operator active -> Block cursor
1002                    CursorShape::Block
1003                }
1004            }
1005            Mode::Replace => CursorShape::Underline,
1006            Mode::HelixNormal | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1007                CursorShape::Block
1008            }
1009            Mode::Insert => CursorShape::Bar,
1010        }
1011    }
1012
1013    pub fn editor_input_enabled(&self) -> bool {
1014        match self.mode {
1015            Mode::Insert => {
1016                if let Some(operator) = self.operator_stack.last() {
1017                    !operator.is_waiting(self.mode)
1018                } else {
1019                    true
1020                }
1021            }
1022            Mode::Normal
1023            | Mode::HelixNormal
1024            | Mode::Replace
1025            | Mode::Visual
1026            | Mode::VisualLine
1027            | Mode::VisualBlock => false,
1028        }
1029    }
1030
1031    pub fn should_autoindent(&self) -> bool {
1032        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1033    }
1034
1035    pub fn clip_at_line_ends(&self) -> bool {
1036        match self.mode {
1037            Mode::Insert
1038            | Mode::Visual
1039            | Mode::VisualLine
1040            | Mode::VisualBlock
1041            | Mode::Replace
1042            | Mode::HelixNormal => false,
1043            Mode::Normal => true,
1044        }
1045    }
1046
1047    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1048        let mut mode = match self.mode {
1049            Mode::Normal => "normal",
1050            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1051            Mode::Insert => "insert",
1052            Mode::Replace => "replace",
1053            Mode::HelixNormal => "helix_normal",
1054        }
1055        .to_string();
1056
1057        let mut operator_id = "none";
1058
1059        let active_operator = self.active_operator();
1060        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1061            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1062        {
1063            context.add("VimCount");
1064        }
1065
1066        if let Some(active_operator) = active_operator {
1067            if active_operator.is_waiting(self.mode) {
1068                if matches!(active_operator, Operator::Literal { .. }) {
1069                    mode = "literal".to_string();
1070                } else {
1071                    mode = "waiting".to_string();
1072                }
1073            } else {
1074                operator_id = active_operator.id();
1075                mode = "operator".to_string();
1076            }
1077        }
1078
1079        if mode == "normal" || mode == "visual" || mode == "operator" {
1080            context.add("VimControl");
1081        }
1082        context.set("vim_mode", mode);
1083        context.set("vim_operator", operator_id);
1084    }
1085
1086    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1087        let Some(editor) = self.editor() else {
1088            return;
1089        };
1090        let newest_selection_empty = editor.update(cx, |editor, cx| {
1091            editor.selections.newest::<usize>(cx).is_empty()
1092        });
1093        let editor = editor.read(cx);
1094        let editor_mode = editor.mode();
1095
1096        if editor_mode == EditorMode::Full
1097                && !newest_selection_empty
1098                && self.mode == Mode::Normal
1099                // When following someone, don't switch vim mode.
1100                && editor.leader_peer_id().is_none()
1101        {
1102            if preserve_selection {
1103                self.switch_mode(Mode::Visual, true, window, cx);
1104            } else {
1105                self.update_editor(window, cx, |_, editor, window, cx| {
1106                    editor.set_clip_at_line_ends(false, cx);
1107                    editor.change_selections(None, window, cx, |s| {
1108                        s.move_with(|_, selection| {
1109                            selection.collapse_to(selection.start, selection.goal)
1110                        })
1111                    });
1112                });
1113            }
1114        }
1115
1116        cx.emit(VimEvent::Focused);
1117        self.sync_vim_settings(window, cx);
1118
1119        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1120            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1121                if old_vim.entity_id() != cx.entity().entity_id() {
1122                    old_vim.update(cx, |vim, cx| {
1123                        vim.update_editor(window, cx, |_, editor, _, cx| {
1124                            editor.set_relative_line_number(None, cx)
1125                        });
1126                    });
1127
1128                    self.update_editor(window, cx, |vim, editor, _, cx| {
1129                        let is_relative = vim.mode != Mode::Insert;
1130                        editor.set_relative_line_number(Some(is_relative), cx)
1131                    });
1132                }
1133            } else {
1134                self.update_editor(window, cx, |vim, editor, _, cx| {
1135                    let is_relative = vim.mode != Mode::Insert;
1136                    editor.set_relative_line_number(Some(is_relative), cx)
1137                });
1138            }
1139        }
1140        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1141    }
1142
1143    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1144        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1145        self.store_visual_marks(window, cx);
1146        self.clear_operator(window, cx);
1147        self.update_editor(window, cx, |vim, editor, _, cx| {
1148            if vim.cursor_shape() == CursorShape::Block {
1149                editor.set_cursor_shape(CursorShape::Hollow, cx);
1150            }
1151        });
1152    }
1153
1154    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1155        self.update_editor(window, cx, |vim, editor, _, cx| {
1156            editor.set_cursor_shape(vim.cursor_shape(), cx);
1157        });
1158    }
1159
1160    fn update_editor<S>(
1161        &mut self,
1162        window: &mut Window,
1163        cx: &mut Context<Self>,
1164        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1165    ) -> Option<S> {
1166        let editor = self.editor.upgrade()?;
1167        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1168    }
1169
1170    fn editor_selections(
1171        &mut self,
1172        window: &mut Window,
1173        cx: &mut Context<Self>,
1174    ) -> Vec<Range<Anchor>> {
1175        self.update_editor(window, cx, |_, editor, _, _| {
1176            editor
1177                .selections
1178                .disjoint_anchors()
1179                .iter()
1180                .map(|selection| selection.tail()..selection.head())
1181                .collect()
1182        })
1183        .unwrap_or_default()
1184    }
1185
1186    fn editor_cursor_word(
1187        &mut self,
1188        window: &mut Window,
1189        cx: &mut Context<Self>,
1190    ) -> Option<String> {
1191        self.update_editor(window, cx, |_, editor, window, cx| {
1192            let selection = editor.selections.newest::<usize>(cx);
1193
1194            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1195            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1196            if kind == Some(CharKind::Word) {
1197                let text: String = snapshot.text_for_range(range).collect();
1198                if !text.trim().is_empty() {
1199                    return Some(text);
1200                }
1201            }
1202
1203            None
1204        })
1205        .unwrap_or_default()
1206    }
1207
1208    /// When doing an action that modifies the buffer, we start recording so that `.`
1209    /// will replay the action.
1210    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1211        Vim::update_globals(cx, |globals, cx| {
1212            if !globals.dot_replaying {
1213                globals.dot_recording = true;
1214                globals.recording_actions = Default::default();
1215                globals.recorded_count = None;
1216
1217                let selections = self.editor().map(|editor| {
1218                    editor.update(cx, |editor, cx| {
1219                        (
1220                            editor.selections.oldest::<Point>(cx),
1221                            editor.selections.newest::<Point>(cx),
1222                        )
1223                    })
1224                });
1225
1226                if let Some((oldest, newest)) = selections {
1227                    globals.recorded_selection = match self.mode {
1228                        Mode::Visual if newest.end.row == newest.start.row => {
1229                            RecordedSelection::SingleLine {
1230                                cols: newest.end.column - newest.start.column,
1231                            }
1232                        }
1233                        Mode::Visual => RecordedSelection::Visual {
1234                            rows: newest.end.row - newest.start.row,
1235                            cols: newest.end.column,
1236                        },
1237                        Mode::VisualLine => RecordedSelection::VisualLine {
1238                            rows: newest.end.row - newest.start.row,
1239                        },
1240                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1241                            rows: newest.end.row.abs_diff(oldest.start.row),
1242                            cols: newest.end.column.abs_diff(oldest.start.column),
1243                        },
1244                        _ => RecordedSelection::None,
1245                    }
1246                } else {
1247                    globals.recorded_selection = RecordedSelection::None;
1248                }
1249            }
1250        })
1251    }
1252
1253    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1254        let globals = Vim::globals(cx);
1255        globals.dot_replaying = false;
1256        if let Some(replayer) = globals.replayer.take() {
1257            replayer.stop();
1258        }
1259    }
1260
1261    /// When finishing an action that modifies the buffer, stop recording.
1262    /// as you usually call this within a keystroke handler we also ensure that
1263    /// the current action is recorded.
1264    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1265        let globals = Vim::globals(cx);
1266        if globals.dot_recording {
1267            globals.stop_recording_after_next_action = true;
1268        }
1269        self.exit_temporary_mode = self.temp_mode;
1270    }
1271
1272    /// Stops recording actions immediately rather than waiting until after the
1273    /// next action to stop recording.
1274    ///
1275    /// This doesn't include the current action.
1276    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1277        let globals = Vim::globals(cx);
1278        if globals.dot_recording {
1279            globals
1280                .recording_actions
1281                .push(ReplayableAction::Action(action.boxed_clone()));
1282            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1283            globals.dot_recording = false;
1284            globals.stop_recording_after_next_action = false;
1285        }
1286        self.exit_temporary_mode = self.temp_mode;
1287    }
1288
1289    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1290    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1291        self.start_recording(cx);
1292        self.stop_recording(cx);
1293    }
1294
1295    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1296        if self.active_operator().is_some() {
1297            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1298
1299            Vim::globals(cx).post_count = Some(
1300                post_count
1301                    .checked_mul(10)
1302                    .and_then(|post_count| post_count.checked_add(number))
1303                    .unwrap_or(post_count),
1304            )
1305        } else {
1306            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1307
1308            Vim::globals(cx).pre_count = Some(
1309                pre_count
1310                    .checked_mul(10)
1311                    .and_then(|pre_count| pre_count.checked_add(number))
1312                    .unwrap_or(pre_count),
1313            )
1314        }
1315        // update the keymap so that 0 works
1316        self.sync_vim_settings(window, cx)
1317    }
1318
1319    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1320        if register.chars().count() == 1 {
1321            self.selected_register
1322                .replace(register.chars().next().unwrap());
1323        }
1324        self.operator_stack.clear();
1325        self.sync_vim_settings(window, cx);
1326    }
1327
1328    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1329        self.operator_stack.pop()
1330    }
1331
1332    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1333        let popped_operator = self.operator_stack.pop()
1334            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1335        self.sync_vim_settings(window, cx);
1336        popped_operator
1337    }
1338
1339    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1340        Vim::take_count(cx);
1341        self.selected_register.take();
1342        self.operator_stack.clear();
1343        self.sync_vim_settings(window, cx);
1344    }
1345
1346    fn active_operator(&self) -> Option<Operator> {
1347        self.operator_stack.last().cloned()
1348    }
1349
1350    fn transaction_begun(
1351        &mut self,
1352        transaction_id: TransactionId,
1353        _window: &mut Window,
1354        _: &mut Context<Self>,
1355    ) {
1356        let mode = if (self.mode == Mode::Insert
1357            || self.mode == Mode::Replace
1358            || self.mode == Mode::Normal)
1359            && self.current_tx.is_none()
1360        {
1361            self.current_tx = Some(transaction_id);
1362            self.last_mode
1363        } else {
1364            self.mode
1365        };
1366        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1367            self.undo_modes.insert(transaction_id, mode);
1368        }
1369    }
1370
1371    fn transaction_undone(
1372        &mut self,
1373        transaction_id: &TransactionId,
1374        window: &mut Window,
1375        cx: &mut Context<Self>,
1376    ) {
1377        match self.mode {
1378            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1379                self.update_editor(window, cx, |vim, editor, window, cx| {
1380                    let original_mode = vim.undo_modes.get(transaction_id);
1381                    editor.change_selections(None, window, cx, |s| match original_mode {
1382                        Some(Mode::VisualLine) => {
1383                            s.move_with(|map, selection| {
1384                                selection.collapse_to(
1385                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1386                                    SelectionGoal::None,
1387                                )
1388                            });
1389                        }
1390                        Some(Mode::VisualBlock) => {
1391                            let mut first = s.first_anchor();
1392                            first.collapse_to(first.start, first.goal);
1393                            s.select_anchors(vec![first]);
1394                        }
1395                        _ => {
1396                            s.move_with(|map, selection| {
1397                                selection.collapse_to(
1398                                    map.clip_at_line_end(selection.start),
1399                                    selection.goal,
1400                                );
1401                            });
1402                        }
1403                    });
1404                });
1405                self.switch_mode(Mode::Normal, true, window, cx)
1406            }
1407            Mode::Normal => {
1408                self.update_editor(window, cx, |_, editor, window, cx| {
1409                    editor.change_selections(None, window, cx, |s| {
1410                        s.move_with(|map, selection| {
1411                            selection
1412                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1413                        })
1414                    })
1415                });
1416            }
1417            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1418        }
1419    }
1420
1421    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1422        let Some(editor) = self.editor() else { return };
1423
1424        if editor.read(cx).leader_peer_id().is_some() {
1425            return;
1426        }
1427
1428        let newest = editor.read(cx).selections.newest_anchor().clone();
1429        let is_multicursor = editor.read(cx).selections.count() > 1;
1430        if self.mode == Mode::Insert && self.current_tx.is_some() {
1431            if self.current_anchor.is_none() {
1432                self.current_anchor = Some(newest);
1433            } else if self.current_anchor.as_ref().unwrap() != &newest {
1434                if let Some(tx_id) = self.current_tx.take() {
1435                    self.update_editor(window, cx, |_, editor, _, cx| {
1436                        editor.group_until_transaction(tx_id, cx)
1437                    });
1438                }
1439            }
1440        } else if self.mode == Mode::Normal && newest.start != newest.end {
1441            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1442                self.switch_mode(Mode::VisualBlock, false, window, cx);
1443            } else {
1444                self.switch_mode(Mode::Visual, false, window, cx)
1445            }
1446        } else if newest.start == newest.end
1447            && !is_multicursor
1448            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1449        {
1450            self.switch_mode(Mode::Normal, true, window, cx);
1451        }
1452    }
1453
1454    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1455        if text.is_empty() {
1456            return;
1457        }
1458
1459        match self.active_operator() {
1460            Some(Operator::FindForward { before }) => {
1461                let find = Motion::FindForward {
1462                    before,
1463                    char: text.chars().next().unwrap(),
1464                    mode: if VimSettings::get_global(cx).use_multiline_find {
1465                        FindRange::MultiLine
1466                    } else {
1467                        FindRange::SingleLine
1468                    },
1469                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1470                };
1471                Vim::globals(cx).last_find = Some(find.clone());
1472                self.motion(find, window, cx)
1473            }
1474            Some(Operator::FindBackward { after }) => {
1475                let find = Motion::FindBackward {
1476                    after,
1477                    char: text.chars().next().unwrap(),
1478                    mode: if VimSettings::get_global(cx).use_multiline_find {
1479                        FindRange::MultiLine
1480                    } else {
1481                        FindRange::SingleLine
1482                    },
1483                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1484                };
1485                Vim::globals(cx).last_find = Some(find.clone());
1486                self.motion(find, window, cx)
1487            }
1488            Some(Operator::Sneak { first_char }) => {
1489                if let Some(first_char) = first_char {
1490                    if let Some(second_char) = text.chars().next() {
1491                        let sneak = Motion::Sneak {
1492                            first_char,
1493                            second_char,
1494                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1495                        };
1496                        Vim::globals(cx).last_find = Some((&sneak).clone());
1497                        self.motion(sneak, window, cx)
1498                    }
1499                } else {
1500                    let first_char = text.chars().next();
1501                    self.pop_operator(window, cx);
1502                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1503                }
1504            }
1505            Some(Operator::SneakBackward { first_char }) => {
1506                if let Some(first_char) = first_char {
1507                    if let Some(second_char) = text.chars().next() {
1508                        let sneak = Motion::SneakBackward {
1509                            first_char,
1510                            second_char,
1511                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1512                        };
1513                        Vim::globals(cx).last_find = Some((&sneak).clone());
1514                        self.motion(sneak, window, cx)
1515                    }
1516                } else {
1517                    let first_char = text.chars().next();
1518                    self.pop_operator(window, cx);
1519                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1520                }
1521            }
1522            Some(Operator::Replace) => match self.mode {
1523                Mode::Normal => self.normal_replace(text, window, cx),
1524                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1525                    self.visual_replace(text, window, cx)
1526                }
1527                _ => self.clear_operator(window, cx),
1528            },
1529            Some(Operator::Digraph { first_char }) => {
1530                if let Some(first_char) = first_char {
1531                    if let Some(second_char) = text.chars().next() {
1532                        self.insert_digraph(first_char, second_char, window, cx);
1533                    }
1534                } else {
1535                    let first_char = text.chars().next();
1536                    self.pop_operator(window, cx);
1537                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1538                }
1539            }
1540            Some(Operator::Literal { prefix }) => {
1541                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1542            }
1543            Some(Operator::AddSurrounds { target }) => match self.mode {
1544                Mode::Normal => {
1545                    if let Some(target) = target {
1546                        self.add_surrounds(text, target, window, cx);
1547                        self.clear_operator(window, cx);
1548                    }
1549                }
1550                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1551                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1552                    self.clear_operator(window, cx);
1553                }
1554                _ => self.clear_operator(window, cx),
1555            },
1556            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1557                Mode::Normal => {
1558                    if let Some(target) = target {
1559                        self.change_surrounds(text, target, window, cx);
1560                        self.clear_operator(window, cx);
1561                    }
1562                }
1563                _ => self.clear_operator(window, cx),
1564            },
1565            Some(Operator::DeleteSurrounds) => match self.mode {
1566                Mode::Normal => {
1567                    self.delete_surrounds(text, window, cx);
1568                    self.clear_operator(window, cx);
1569                }
1570                _ => self.clear_operator(window, cx),
1571            },
1572            Some(Operator::Mark) => self.create_mark(text, false, window, cx),
1573            Some(Operator::RecordRegister) => {
1574                self.record_register(text.chars().next().unwrap(), window, cx)
1575            }
1576            Some(Operator::ReplayRegister) => {
1577                self.replay_register(text.chars().next().unwrap(), window, cx)
1578            }
1579            Some(Operator::Register) => match self.mode {
1580                Mode::Insert => {
1581                    self.update_editor(window, cx, |_, editor, window, cx| {
1582                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1583                            globals.read_register(text.chars().next(), Some(editor), cx)
1584                        }) {
1585                            editor.do_paste(
1586                                &register.text.to_string(),
1587                                register.clipboard_selections.clone(),
1588                                false,
1589                                window,
1590                                cx,
1591                            )
1592                        }
1593                    });
1594                    self.clear_operator(window, cx);
1595                }
1596                _ => {
1597                    self.select_register(text, window, cx);
1598                }
1599            },
1600            Some(Operator::Jump { line }) => self.jump(text, line, window, cx),
1601            _ => {
1602                if self.mode == Mode::Replace {
1603                    self.multi_replace(text, window, cx)
1604                }
1605
1606                if self.mode == Mode::Normal {
1607                    self.update_editor(window, cx, |_, editor, window, cx| {
1608                        editor.accept_edit_prediction(
1609                            &editor::actions::AcceptEditPrediction {},
1610                            window,
1611                            cx,
1612                        );
1613                    });
1614                }
1615            }
1616        }
1617    }
1618
1619    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1620        self.update_editor(window, cx, |vim, editor, window, cx| {
1621            editor.set_cursor_shape(vim.cursor_shape(), cx);
1622            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1623            editor.set_collapse_matches(true);
1624            editor.set_input_enabled(vim.editor_input_enabled());
1625            editor.set_autoindent(vim.should_autoindent());
1626            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1627
1628            let hide_inline_completions = match vim.mode {
1629                Mode::Insert | Mode::Replace => false,
1630                _ => true,
1631            };
1632            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1633        });
1634        cx.notify()
1635    }
1636}
1637
1638/// Controls when to use system clipboard.
1639#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1640#[serde(rename_all = "snake_case")]
1641pub enum UseSystemClipboard {
1642    /// Don't use system clipboard.
1643    Never,
1644    /// Use system clipboard.
1645    Always,
1646    /// Use system clipboard for yank operations.
1647    OnYank,
1648}
1649
1650#[derive(Deserialize)]
1651struct VimSettings {
1652    pub default_mode: Mode,
1653    pub toggle_relative_line_numbers: bool,
1654    pub use_system_clipboard: UseSystemClipboard,
1655    pub use_multiline_find: bool,
1656    pub use_smartcase_find: bool,
1657    pub custom_digraphs: HashMap<String, Arc<str>>,
1658    pub highlight_on_yank_duration: u64,
1659}
1660
1661#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1662struct VimSettingsContent {
1663    pub default_mode: Option<ModeContent>,
1664    pub toggle_relative_line_numbers: Option<bool>,
1665    pub use_system_clipboard: Option<UseSystemClipboard>,
1666    pub use_multiline_find: Option<bool>,
1667    pub use_smartcase_find: Option<bool>,
1668    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1669    pub highlight_on_yank_duration: Option<u64>,
1670}
1671
1672#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1673#[serde(rename_all = "snake_case")]
1674pub enum ModeContent {
1675    #[default]
1676    Normal,
1677    Insert,
1678    Replace,
1679    Visual,
1680    VisualLine,
1681    VisualBlock,
1682    HelixNormal,
1683}
1684
1685impl From<ModeContent> for Mode {
1686    fn from(mode: ModeContent) -> Self {
1687        match mode {
1688            ModeContent::Normal => Self::Normal,
1689            ModeContent::Insert => Self::Insert,
1690            ModeContent::Replace => Self::Replace,
1691            ModeContent::Visual => Self::Visual,
1692            ModeContent::VisualLine => Self::VisualLine,
1693            ModeContent::VisualBlock => Self::VisualBlock,
1694            ModeContent::HelixNormal => Self::HelixNormal,
1695        }
1696    }
1697}
1698
1699impl Settings for VimSettings {
1700    const KEY: Option<&'static str> = Some("vim");
1701
1702    type FileContent = VimSettingsContent;
1703
1704    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1705        let settings: VimSettingsContent = sources.json_merge()?;
1706
1707        Ok(Self {
1708            default_mode: settings
1709                .default_mode
1710                .ok_or_else(Self::missing_default)?
1711                .into(),
1712            toggle_relative_line_numbers: settings
1713                .toggle_relative_line_numbers
1714                .ok_or_else(Self::missing_default)?,
1715            use_system_clipboard: settings
1716                .use_system_clipboard
1717                .ok_or_else(Self::missing_default)?,
1718            use_multiline_find: settings
1719                .use_multiline_find
1720                .ok_or_else(Self::missing_default)?,
1721            use_smartcase_find: settings
1722                .use_smartcase_find
1723                .ok_or_else(Self::missing_default)?,
1724            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1725            highlight_on_yank_duration: settings
1726                .highlight_on_yank_duration
1727                .ok_or_else(Self::missing_default)?,
1728        })
1729    }
1730}