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