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