vim.rs

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