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