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