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