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        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            _ => {}
 826        }
 827    }
 828
 829    fn push_operator(&mut self, operator: Operator, window: &mut Window, cx: &mut Context<Self>) {
 830        if operator.starts_dot_recording() {
 831            self.start_recording(cx);
 832        }
 833        // Since these operations can only be entered with pre-operators,
 834        // we need to clear the previous operators when pushing,
 835        // so that the current stack is the most correct
 836        if matches!(
 837            operator,
 838            Operator::AddSurrounds { .. }
 839                | Operator::ChangeSurrounds { .. }
 840                | Operator::DeleteSurrounds
 841        ) {
 842            self.operator_stack.clear();
 843        };
 844        self.operator_stack.push(operator);
 845        self.sync_vim_settings(window, cx);
 846    }
 847
 848    pub fn switch_mode(
 849        &mut self,
 850        mode: Mode,
 851        leave_selections: bool,
 852        window: &mut Window,
 853        cx: &mut Context<Self>,
 854    ) {
 855        if self.temp_mode && mode == Mode::Normal {
 856            self.temp_mode = false;
 857            self.switch_mode(Mode::Normal, leave_selections, window, cx);
 858            self.switch_mode(Mode::Insert, false, window, cx);
 859            return;
 860        } else if self.temp_mode
 861            && !matches!(mode, Mode::Visual | Mode::VisualLine | Mode::VisualBlock)
 862        {
 863            self.temp_mode = false;
 864        }
 865
 866        let last_mode = self.mode;
 867        let prior_mode = self.last_mode;
 868        let prior_tx = self.current_tx;
 869        self.status_label.take();
 870        self.last_mode = last_mode;
 871        self.mode = mode;
 872        self.operator_stack.clear();
 873        self.selected_register.take();
 874        self.cancel_running_command(window, cx);
 875        if mode == Mode::Normal || mode != last_mode {
 876            self.current_tx.take();
 877            self.current_anchor.take();
 878        }
 879        if mode != Mode::Insert && mode != Mode::Replace {
 880            Vim::take_count(cx);
 881        }
 882
 883        // Sync editor settings like clip mode
 884        self.sync_vim_settings(window, cx);
 885
 886        if VimSettings::get_global(cx).toggle_relative_line_numbers
 887            && self.mode != self.last_mode
 888            && (self.mode == Mode::Insert || self.last_mode == Mode::Insert)
 889        {
 890            self.update_editor(window, cx, |vim, editor, _, cx| {
 891                let is_relative = vim.mode != Mode::Insert;
 892                editor.set_relative_line_number(Some(is_relative), cx)
 893            });
 894        }
 895
 896        if leave_selections {
 897            return;
 898        }
 899
 900        if !mode.is_visual() && last_mode.is_visual() {
 901            self.create_visual_marks(last_mode, window, cx);
 902        }
 903
 904        // Adjust selections
 905        self.update_editor(window, cx, |vim, editor, window, cx| {
 906            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 907            {
 908                vim.visual_block_motion(true, editor, window, cx, |_, point, goal| {
 909                    Some((point, goal))
 910                })
 911            }
 912            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 913                if let Some(prior_tx) = prior_tx {
 914                    editor.group_until_transaction(prior_tx, cx)
 915                }
 916            }
 917
 918            editor.change_selections(None, window, cx, |s| {
 919                // we cheat with visual block mode and use multiple cursors.
 920                // the cost of this cheat is we need to convert back to a single
 921                // cursor whenever vim would.
 922                if last_mode == Mode::VisualBlock
 923                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 924                {
 925                    let tail = s.oldest_anchor().tail();
 926                    let head = s.newest_anchor().head();
 927                    s.select_anchor_ranges(vec![tail..head]);
 928                } else if last_mode == Mode::Insert
 929                    && prior_mode == Mode::VisualBlock
 930                    && mode != Mode::VisualBlock
 931                {
 932                    let pos = s.first_anchor().head();
 933                    s.select_anchor_ranges(vec![pos..pos])
 934                }
 935
 936                let snapshot = s.display_map();
 937                if let Some(pending) = s.pending.as_mut() {
 938                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 939                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 940                        end = snapshot
 941                            .buffer_snapshot
 942                            .clip_point(end + Point::new(0, 1), Bias::Right);
 943                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 944                    }
 945                }
 946
 947                s.move_with(|map, selection| {
 948                    if last_mode.is_visual() && !mode.is_visual() {
 949                        let mut point = selection.head();
 950                        if !selection.reversed && !selection.is_empty() {
 951                            point = movement::left(map, selection.head());
 952                        }
 953                        selection.collapse_to(point, selection.goal)
 954                    } else if !last_mode.is_visual() && mode.is_visual() && selection.is_empty() {
 955                        selection.end = movement::right(map, selection.start);
 956                    }
 957                });
 958            })
 959        });
 960    }
 961
 962    pub fn take_count(cx: &mut App) -> Option<usize> {
 963        let global_state = cx.global_mut::<VimGlobals>();
 964        if global_state.dot_replaying {
 965            return global_state.recorded_count;
 966        }
 967
 968        let count = if global_state.post_count.is_none() && global_state.pre_count.is_none() {
 969            return None;
 970        } else {
 971            Some(
 972                global_state.post_count.take().unwrap_or(1)
 973                    * global_state.pre_count.take().unwrap_or(1),
 974            )
 975        };
 976
 977        if global_state.dot_recording {
 978            global_state.recorded_count = count;
 979        }
 980        count
 981    }
 982
 983    pub fn cursor_shape(&self, cx: &mut App) -> CursorShape {
 984        match self.mode {
 985            Mode::Normal => {
 986                if let Some(operator) = self.operator_stack.last() {
 987                    match operator {
 988                        // Navigation operators -> Block cursor
 989                        Operator::FindForward { .. }
 990                        | Operator::FindBackward { .. }
 991                        | Operator::Mark
 992                        | Operator::Jump { .. }
 993                        | Operator::Register
 994                        | Operator::RecordRegister
 995                        | Operator::ReplayRegister => CursorShape::Block,
 996
 997                        // All other operators -> Underline cursor
 998                        _ => CursorShape::Underline,
 999                    }
1000                } else {
1001                    // No operator active -> Block cursor
1002                    CursorShape::Block
1003                }
1004            }
1005            Mode::Replace => CursorShape::Underline,
1006            Mode::HelixNormal | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1007                CursorShape::Block
1008            }
1009            Mode::Insert => {
1010                let editor_settings = EditorSettings::get_global(cx);
1011                editor_settings.cursor_shape.unwrap_or_default()
1012            }
1013        }
1014    }
1015
1016    pub fn editor_input_enabled(&self) -> bool {
1017        match self.mode {
1018            Mode::Insert => {
1019                if let Some(operator) = self.operator_stack.last() {
1020                    !operator.is_waiting(self.mode)
1021                } else {
1022                    true
1023                }
1024            }
1025            Mode::Normal
1026            | Mode::HelixNormal
1027            | Mode::Replace
1028            | Mode::Visual
1029            | Mode::VisualLine
1030            | Mode::VisualBlock => false,
1031        }
1032    }
1033
1034    pub fn should_autoindent(&self) -> bool {
1035        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
1036    }
1037
1038    pub fn clip_at_line_ends(&self) -> bool {
1039        match self.mode {
1040            Mode::Insert
1041            | Mode::Visual
1042            | Mode::VisualLine
1043            | Mode::VisualBlock
1044            | Mode::Replace
1045            | Mode::HelixNormal => false,
1046            Mode::Normal => true,
1047        }
1048    }
1049
1050    pub fn extend_key_context(&self, context: &mut KeyContext, cx: &App) {
1051        let mut mode = match self.mode {
1052            Mode::Normal => "normal",
1053            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
1054            Mode::Insert => "insert",
1055            Mode::Replace => "replace",
1056            Mode::HelixNormal => "helix_normal",
1057        }
1058        .to_string();
1059
1060        let mut operator_id = "none";
1061
1062        let active_operator = self.active_operator();
1063        if active_operator.is_none() && cx.global::<VimGlobals>().pre_count.is_some()
1064            || active_operator.is_some() && cx.global::<VimGlobals>().post_count.is_some()
1065        {
1066            context.add("VimCount");
1067        }
1068
1069        if let Some(active_operator) = active_operator {
1070            if active_operator.is_waiting(self.mode) {
1071                if matches!(active_operator, Operator::Literal { .. }) {
1072                    mode = "literal".to_string();
1073                } else {
1074                    mode = "waiting".to_string();
1075                }
1076            } else {
1077                operator_id = active_operator.id();
1078                mode = "operator".to_string();
1079            }
1080        }
1081
1082        if mode == "normal" || mode == "visual" || mode == "operator" {
1083            context.add("VimControl");
1084        }
1085        context.set("vim_mode", mode);
1086        context.set("vim_operator", operator_id);
1087    }
1088
1089    fn focused(&mut self, preserve_selection: bool, window: &mut Window, cx: &mut Context<Self>) {
1090        let Some(editor) = self.editor() else {
1091            return;
1092        };
1093        let newest_selection_empty = editor.update(cx, |editor, cx| {
1094            editor.selections.newest::<usize>(cx).is_empty()
1095        });
1096        let editor = editor.read(cx);
1097        let editor_mode = editor.mode();
1098
1099        if editor_mode == EditorMode::Full
1100                && !newest_selection_empty
1101                && self.mode == Mode::Normal
1102                // When following someone, don't switch vim mode.
1103                && editor.leader_peer_id().is_none()
1104        {
1105            if preserve_selection {
1106                self.switch_mode(Mode::Visual, true, window, cx);
1107            } else {
1108                self.update_editor(window, cx, |_, editor, window, cx| {
1109                    editor.set_clip_at_line_ends(false, cx);
1110                    editor.change_selections(None, window, cx, |s| {
1111                        s.move_with(|_, selection| {
1112                            selection.collapse_to(selection.start, selection.goal)
1113                        })
1114                    });
1115                });
1116            }
1117        }
1118
1119        cx.emit(VimEvent::Focused);
1120        self.sync_vim_settings(window, cx);
1121
1122        if VimSettings::get_global(cx).toggle_relative_line_numbers {
1123            if let Some(old_vim) = Vim::globals(cx).focused_vim() {
1124                if old_vim.entity_id() != cx.entity().entity_id() {
1125                    old_vim.update(cx, |vim, cx| {
1126                        vim.update_editor(window, cx, |_, editor, _, cx| {
1127                            editor.set_relative_line_number(None, cx)
1128                        });
1129                    });
1130
1131                    self.update_editor(window, cx, |vim, editor, _, cx| {
1132                        let is_relative = vim.mode != Mode::Insert;
1133                        editor.set_relative_line_number(Some(is_relative), cx)
1134                    });
1135                }
1136            } else {
1137                self.update_editor(window, cx, |vim, editor, _, cx| {
1138                    let is_relative = vim.mode != Mode::Insert;
1139                    editor.set_relative_line_number(Some(is_relative), cx)
1140                });
1141            }
1142        }
1143        Vim::globals(cx).focused_vim = Some(cx.entity().downgrade());
1144    }
1145
1146    fn blurred(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1147        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
1148        self.store_visual_marks(window, cx);
1149        self.clear_operator(window, cx);
1150        self.update_editor(window, cx, |vim, editor, _, cx| {
1151            if vim.cursor_shape(cx) == CursorShape::Block {
1152                editor.set_cursor_shape(CursorShape::Hollow, cx);
1153            }
1154        });
1155    }
1156
1157    fn cursor_shape_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1158        self.update_editor(window, cx, |vim, editor, _, cx| {
1159            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1160        });
1161    }
1162
1163    fn update_editor<S>(
1164        &mut self,
1165        window: &mut Window,
1166        cx: &mut Context<Self>,
1167        update: impl FnOnce(&mut Self, &mut Editor, &mut Window, &mut Context<Editor>) -> S,
1168    ) -> Option<S> {
1169        let editor = self.editor.upgrade()?;
1170        Some(editor.update(cx, |editor, cx| update(self, editor, window, cx)))
1171    }
1172
1173    fn editor_selections(
1174        &mut self,
1175        window: &mut Window,
1176        cx: &mut Context<Self>,
1177    ) -> Vec<Range<Anchor>> {
1178        self.update_editor(window, cx, |_, editor, _, _| {
1179            editor
1180                .selections
1181                .disjoint_anchors()
1182                .iter()
1183                .map(|selection| selection.tail()..selection.head())
1184                .collect()
1185        })
1186        .unwrap_or_default()
1187    }
1188
1189    fn editor_cursor_word(
1190        &mut self,
1191        window: &mut Window,
1192        cx: &mut Context<Self>,
1193    ) -> Option<String> {
1194        self.update_editor(window, cx, |_, editor, window, cx| {
1195            let selection = editor.selections.newest::<usize>(cx);
1196
1197            let snapshot = &editor.snapshot(window, cx).buffer_snapshot;
1198            let (range, kind) = snapshot.surrounding_word(selection.start, true);
1199            if kind == Some(CharKind::Word) {
1200                let text: String = snapshot.text_for_range(range).collect();
1201                if !text.trim().is_empty() {
1202                    return Some(text);
1203                }
1204            }
1205
1206            None
1207        })
1208        .unwrap_or_default()
1209    }
1210
1211    /// When doing an action that modifies the buffer, we start recording so that `.`
1212    /// will replay the action.
1213    pub fn start_recording(&mut self, cx: &mut Context<Self>) {
1214        Vim::update_globals(cx, |globals, cx| {
1215            if !globals.dot_replaying {
1216                globals.dot_recording = true;
1217                globals.recording_actions = Default::default();
1218                globals.recorded_count = None;
1219
1220                let selections = self.editor().map(|editor| {
1221                    editor.update(cx, |editor, cx| {
1222                        (
1223                            editor.selections.oldest::<Point>(cx),
1224                            editor.selections.newest::<Point>(cx),
1225                        )
1226                    })
1227                });
1228
1229                if let Some((oldest, newest)) = selections {
1230                    globals.recorded_selection = match self.mode {
1231                        Mode::Visual if newest.end.row == newest.start.row => {
1232                            RecordedSelection::SingleLine {
1233                                cols: newest.end.column - newest.start.column,
1234                            }
1235                        }
1236                        Mode::Visual => RecordedSelection::Visual {
1237                            rows: newest.end.row - newest.start.row,
1238                            cols: newest.end.column,
1239                        },
1240                        Mode::VisualLine => RecordedSelection::VisualLine {
1241                            rows: newest.end.row - newest.start.row,
1242                        },
1243                        Mode::VisualBlock => RecordedSelection::VisualBlock {
1244                            rows: newest.end.row.abs_diff(oldest.start.row),
1245                            cols: newest.end.column.abs_diff(oldest.start.column),
1246                        },
1247                        _ => RecordedSelection::None,
1248                    }
1249                } else {
1250                    globals.recorded_selection = RecordedSelection::None;
1251                }
1252            }
1253        })
1254    }
1255
1256    pub fn stop_replaying(&mut self, cx: &mut Context<Self>) {
1257        let globals = Vim::globals(cx);
1258        globals.dot_replaying = false;
1259        if let Some(replayer) = globals.replayer.take() {
1260            replayer.stop();
1261        }
1262    }
1263
1264    /// When finishing an action that modifies the buffer, stop recording.
1265    /// as you usually call this within a keystroke handler we also ensure that
1266    /// the current action is recorded.
1267    pub fn stop_recording(&mut self, cx: &mut Context<Self>) {
1268        let globals = Vim::globals(cx);
1269        if globals.dot_recording {
1270            globals.stop_recording_after_next_action = true;
1271        }
1272        self.exit_temporary_mode = self.temp_mode;
1273    }
1274
1275    /// Stops recording actions immediately rather than waiting until after the
1276    /// next action to stop recording.
1277    ///
1278    /// This doesn't include the current action.
1279    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>, cx: &mut Context<Self>) {
1280        let globals = Vim::globals(cx);
1281        if globals.dot_recording {
1282            globals
1283                .recording_actions
1284                .push(ReplayableAction::Action(action.boxed_clone()));
1285            globals.recorded_actions = mem::take(&mut globals.recording_actions);
1286            globals.dot_recording = false;
1287            globals.stop_recording_after_next_action = false;
1288        }
1289        self.exit_temporary_mode = self.temp_mode;
1290    }
1291
1292    /// Explicitly record one action (equivalents to start_recording and stop_recording)
1293    pub fn record_current_action(&mut self, cx: &mut Context<Self>) {
1294        self.start_recording(cx);
1295        self.stop_recording(cx);
1296    }
1297
1298    fn push_count_digit(&mut self, number: usize, window: &mut Window, cx: &mut Context<Self>) {
1299        if self.active_operator().is_some() {
1300            let post_count = Vim::globals(cx).post_count.unwrap_or(0);
1301
1302            Vim::globals(cx).post_count = Some(
1303                post_count
1304                    .checked_mul(10)
1305                    .and_then(|post_count| post_count.checked_add(number))
1306                    .unwrap_or(post_count),
1307            )
1308        } else {
1309            let pre_count = Vim::globals(cx).pre_count.unwrap_or(0);
1310
1311            Vim::globals(cx).pre_count = Some(
1312                pre_count
1313                    .checked_mul(10)
1314                    .and_then(|pre_count| pre_count.checked_add(number))
1315                    .unwrap_or(pre_count),
1316            )
1317        }
1318        // update the keymap so that 0 works
1319        self.sync_vim_settings(window, cx)
1320    }
1321
1322    fn select_register(&mut self, register: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1323        if register.chars().count() == 1 {
1324            self.selected_register
1325                .replace(register.chars().next().unwrap());
1326        }
1327        self.operator_stack.clear();
1328        self.sync_vim_settings(window, cx);
1329    }
1330
1331    fn maybe_pop_operator(&mut self) -> Option<Operator> {
1332        self.operator_stack.pop()
1333    }
1334
1335    fn pop_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Operator {
1336        let popped_operator = self.operator_stack.pop()
1337            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
1338        self.sync_vim_settings(window, cx);
1339        popped_operator
1340    }
1341
1342    fn clear_operator(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1343        Vim::take_count(cx);
1344        self.selected_register.take();
1345        self.operator_stack.clear();
1346        self.sync_vim_settings(window, cx);
1347    }
1348
1349    fn active_operator(&self) -> Option<Operator> {
1350        self.operator_stack.last().cloned()
1351    }
1352
1353    fn transaction_begun(
1354        &mut self,
1355        transaction_id: TransactionId,
1356        _window: &mut Window,
1357        _: &mut Context<Self>,
1358    ) {
1359        let mode = if (self.mode == Mode::Insert
1360            || self.mode == Mode::Replace
1361            || self.mode == Mode::Normal)
1362            && self.current_tx.is_none()
1363        {
1364            self.current_tx = Some(transaction_id);
1365            self.last_mode
1366        } else {
1367            self.mode
1368        };
1369        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
1370            self.undo_modes.insert(transaction_id, mode);
1371        }
1372    }
1373
1374    fn transaction_undone(
1375        &mut self,
1376        transaction_id: &TransactionId,
1377        window: &mut Window,
1378        cx: &mut Context<Self>,
1379    ) {
1380        match self.mode {
1381            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
1382                self.update_editor(window, cx, |vim, editor, window, cx| {
1383                    let original_mode = vim.undo_modes.get(transaction_id);
1384                    editor.change_selections(None, window, cx, |s| match original_mode {
1385                        Some(Mode::VisualLine) => {
1386                            s.move_with(|map, selection| {
1387                                selection.collapse_to(
1388                                    map.prev_line_boundary(selection.start.to_point(map)).1,
1389                                    SelectionGoal::None,
1390                                )
1391                            });
1392                        }
1393                        Some(Mode::VisualBlock) => {
1394                            let mut first = s.first_anchor();
1395                            first.collapse_to(first.start, first.goal);
1396                            s.select_anchors(vec![first]);
1397                        }
1398                        _ => {
1399                            s.move_with(|map, selection| {
1400                                selection.collapse_to(
1401                                    map.clip_at_line_end(selection.start),
1402                                    selection.goal,
1403                                );
1404                            });
1405                        }
1406                    });
1407                });
1408                self.switch_mode(Mode::Normal, true, window, cx)
1409            }
1410            Mode::Normal => {
1411                self.update_editor(window, cx, |_, editor, window, cx| {
1412                    editor.change_selections(None, window, cx, |s| {
1413                        s.move_with(|map, selection| {
1414                            selection
1415                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
1416                        })
1417                    })
1418                });
1419            }
1420            Mode::Insert | Mode::Replace | Mode::HelixNormal => {}
1421        }
1422    }
1423
1424    fn local_selections_changed(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1425        let Some(editor) = self.editor() else { return };
1426
1427        if editor.read(cx).leader_peer_id().is_some() {
1428            return;
1429        }
1430
1431        let newest = editor.read(cx).selections.newest_anchor().clone();
1432        let is_multicursor = editor.read(cx).selections.count() > 1;
1433        if self.mode == Mode::Insert && self.current_tx.is_some() {
1434            if self.current_anchor.is_none() {
1435                self.current_anchor = Some(newest);
1436            } else if self.current_anchor.as_ref().unwrap() != &newest {
1437                if let Some(tx_id) = self.current_tx.take() {
1438                    self.update_editor(window, cx, |_, editor, _, cx| {
1439                        editor.group_until_transaction(tx_id, cx)
1440                    });
1441                }
1442            }
1443        } else if self.mode == Mode::Normal && newest.start != newest.end {
1444            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
1445                self.switch_mode(Mode::VisualBlock, false, window, cx);
1446            } else {
1447                self.switch_mode(Mode::Visual, false, window, cx)
1448            }
1449        } else if newest.start == newest.end
1450            && !is_multicursor
1451            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
1452        {
1453            self.switch_mode(Mode::Normal, true, window, cx);
1454        }
1455    }
1456
1457    fn input_ignored(&mut self, text: Arc<str>, window: &mut Window, cx: &mut Context<Self>) {
1458        if text.is_empty() {
1459            return;
1460        }
1461
1462        match self.active_operator() {
1463            Some(Operator::FindForward { before }) => {
1464                let find = Motion::FindForward {
1465                    before,
1466                    char: text.chars().next().unwrap(),
1467                    mode: if VimSettings::get_global(cx).use_multiline_find {
1468                        FindRange::MultiLine
1469                    } else {
1470                        FindRange::SingleLine
1471                    },
1472                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1473                };
1474                Vim::globals(cx).last_find = Some(find.clone());
1475                self.motion(find, window, cx)
1476            }
1477            Some(Operator::FindBackward { after }) => {
1478                let find = Motion::FindBackward {
1479                    after,
1480                    char: text.chars().next().unwrap(),
1481                    mode: if VimSettings::get_global(cx).use_multiline_find {
1482                        FindRange::MultiLine
1483                    } else {
1484                        FindRange::SingleLine
1485                    },
1486                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
1487                };
1488                Vim::globals(cx).last_find = Some(find.clone());
1489                self.motion(find, window, cx)
1490            }
1491            Some(Operator::Sneak { first_char }) => {
1492                if let Some(first_char) = first_char {
1493                    if let Some(second_char) = text.chars().next() {
1494                        let sneak = Motion::Sneak {
1495                            first_char,
1496                            second_char,
1497                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1498                        };
1499                        Vim::globals(cx).last_find = Some((&sneak).clone());
1500                        self.motion(sneak, window, cx)
1501                    }
1502                } else {
1503                    let first_char = text.chars().next();
1504                    self.pop_operator(window, cx);
1505                    self.push_operator(Operator::Sneak { first_char }, window, cx);
1506                }
1507            }
1508            Some(Operator::SneakBackward { first_char }) => {
1509                if let Some(first_char) = first_char {
1510                    if let Some(second_char) = text.chars().next() {
1511                        let sneak = Motion::SneakBackward {
1512                            first_char,
1513                            second_char,
1514                            smartcase: VimSettings::get_global(cx).use_smartcase_find,
1515                        };
1516                        Vim::globals(cx).last_find = Some((&sneak).clone());
1517                        self.motion(sneak, window, cx)
1518                    }
1519                } else {
1520                    let first_char = text.chars().next();
1521                    self.pop_operator(window, cx);
1522                    self.push_operator(Operator::SneakBackward { first_char }, window, cx);
1523                }
1524            }
1525            Some(Operator::Replace) => match self.mode {
1526                Mode::Normal => self.normal_replace(text, window, cx),
1527                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1528                    self.visual_replace(text, window, cx)
1529                }
1530                _ => self.clear_operator(window, cx),
1531            },
1532            Some(Operator::Digraph { first_char }) => {
1533                if let Some(first_char) = first_char {
1534                    if let Some(second_char) = text.chars().next() {
1535                        self.insert_digraph(first_char, second_char, window, cx);
1536                    }
1537                } else {
1538                    let first_char = text.chars().next();
1539                    self.pop_operator(window, cx);
1540                    self.push_operator(Operator::Digraph { first_char }, window, cx);
1541                }
1542            }
1543            Some(Operator::Literal { prefix }) => {
1544                self.handle_literal_input(prefix.unwrap_or_default(), &text, window, cx)
1545            }
1546            Some(Operator::AddSurrounds { target }) => match self.mode {
1547                Mode::Normal => {
1548                    if let Some(target) = target {
1549                        self.add_surrounds(text, target, window, cx);
1550                        self.clear_operator(window, cx);
1551                    }
1552                }
1553                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
1554                    self.add_surrounds(text, SurroundsType::Selection, window, cx);
1555                    self.clear_operator(window, cx);
1556                }
1557                _ => self.clear_operator(window, cx),
1558            },
1559            Some(Operator::ChangeSurrounds { target }) => match self.mode {
1560                Mode::Normal => {
1561                    if let Some(target) = target {
1562                        self.change_surrounds(text, target, window, cx);
1563                        self.clear_operator(window, cx);
1564                    }
1565                }
1566                _ => self.clear_operator(window, cx),
1567            },
1568            Some(Operator::DeleteSurrounds) => match self.mode {
1569                Mode::Normal => {
1570                    self.delete_surrounds(text, window, cx);
1571                    self.clear_operator(window, cx);
1572                }
1573                _ => self.clear_operator(window, cx),
1574            },
1575            Some(Operator::Mark) => self.create_mark(text, window, cx),
1576            Some(Operator::RecordRegister) => {
1577                self.record_register(text.chars().next().unwrap(), window, cx)
1578            }
1579            Some(Operator::ReplayRegister) => {
1580                self.replay_register(text.chars().next().unwrap(), window, cx)
1581            }
1582            Some(Operator::Register) => match self.mode {
1583                Mode::Insert => {
1584                    self.update_editor(window, cx, |_, editor, window, cx| {
1585                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
1586                            globals.read_register(text.chars().next(), Some(editor), cx)
1587                        }) {
1588                            editor.do_paste(
1589                                &register.text.to_string(),
1590                                register.clipboard_selections.clone(),
1591                                false,
1592                                window,
1593                                cx,
1594                            )
1595                        }
1596                    });
1597                    self.clear_operator(window, cx);
1598                }
1599                _ => {
1600                    self.select_register(text, window, cx);
1601                }
1602            },
1603            Some(Operator::Jump { line }) => self.jump(text, line, true, window, cx),
1604            _ => {
1605                if self.mode == Mode::Replace {
1606                    self.multi_replace(text, window, cx)
1607                }
1608
1609                if self.mode == Mode::Normal {
1610                    self.update_editor(window, cx, |_, editor, window, cx| {
1611                        editor.accept_edit_prediction(
1612                            &editor::actions::AcceptEditPrediction {},
1613                            window,
1614                            cx,
1615                        );
1616                    });
1617                }
1618            }
1619        }
1620    }
1621
1622    fn sync_vim_settings(&mut self, window: &mut Window, cx: &mut Context<Self>) {
1623        self.update_editor(window, cx, |vim, editor, window, cx| {
1624            editor.set_cursor_shape(vim.cursor_shape(cx), cx);
1625            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1626            editor.set_collapse_matches(true);
1627            editor.set_input_enabled(vim.editor_input_enabled());
1628            editor.set_autoindent(vim.should_autoindent());
1629            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1630
1631            let hide_inline_completions = match vim.mode {
1632                Mode::Insert | Mode::Replace => false,
1633                _ => true,
1634            };
1635            editor.set_inline_completions_hidden_for_vim_mode(hide_inline_completions, window, cx);
1636        });
1637        cx.notify()
1638    }
1639}
1640
1641/// Controls when to use system clipboard.
1642#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1643#[serde(rename_all = "snake_case")]
1644pub enum UseSystemClipboard {
1645    /// Don't use system clipboard.
1646    Never,
1647    /// Use system clipboard.
1648    Always,
1649    /// Use system clipboard for yank operations.
1650    OnYank,
1651}
1652
1653#[derive(Deserialize)]
1654struct VimSettings {
1655    pub default_mode: Mode,
1656    pub toggle_relative_line_numbers: bool,
1657    pub use_system_clipboard: UseSystemClipboard,
1658    pub use_multiline_find: bool,
1659    pub use_smartcase_find: bool,
1660    pub custom_digraphs: HashMap<String, Arc<str>>,
1661    pub highlight_on_yank_duration: u64,
1662}
1663
1664#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1665struct VimSettingsContent {
1666    pub default_mode: Option<ModeContent>,
1667    pub toggle_relative_line_numbers: Option<bool>,
1668    pub use_system_clipboard: Option<UseSystemClipboard>,
1669    pub use_multiline_find: Option<bool>,
1670    pub use_smartcase_find: Option<bool>,
1671    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1672    pub highlight_on_yank_duration: Option<u64>,
1673}
1674
1675#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1676#[serde(rename_all = "snake_case")]
1677pub enum ModeContent {
1678    #[default]
1679    Normal,
1680    Insert,
1681    Replace,
1682    Visual,
1683    VisualLine,
1684    VisualBlock,
1685    HelixNormal,
1686}
1687
1688impl From<ModeContent> for Mode {
1689    fn from(mode: ModeContent) -> Self {
1690        match mode {
1691            ModeContent::Normal => Self::Normal,
1692            ModeContent::Insert => Self::Insert,
1693            ModeContent::Replace => Self::Replace,
1694            ModeContent::Visual => Self::Visual,
1695            ModeContent::VisualLine => Self::VisualLine,
1696            ModeContent::VisualBlock => Self::VisualBlock,
1697            ModeContent::HelixNormal => Self::HelixNormal,
1698        }
1699    }
1700}
1701
1702impl Settings for VimSettings {
1703    const KEY: Option<&'static str> = Some("vim");
1704
1705    type FileContent = VimSettingsContent;
1706
1707    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
1708        let settings: VimSettingsContent = sources.json_merge()?;
1709
1710        Ok(Self {
1711            default_mode: settings
1712                .default_mode
1713                .ok_or_else(Self::missing_default)?
1714                .into(),
1715            toggle_relative_line_numbers: settings
1716                .toggle_relative_line_numbers
1717                .ok_or_else(Self::missing_default)?,
1718            use_system_clipboard: settings
1719                .use_system_clipboard
1720                .ok_or_else(Self::missing_default)?,
1721            use_multiline_find: settings
1722                .use_multiline_find
1723                .ok_or_else(Self::missing_default)?,
1724            use_smartcase_find: settings
1725                .use_smartcase_find
1726                .ok_or_else(Self::missing_default)?,
1727            custom_digraphs: settings.custom_digraphs.ok_or_else(Self::missing_default)?,
1728            highlight_on_yank_duration: settings
1729                .highlight_on_yank_duration
1730                .ok_or_else(Self::missing_default)?,
1731        })
1732    }
1733}