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