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