vim.rs

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