vim.rs

   1//! Vim support for Zed.
   2
   3#[cfg(test)]
   4mod test;
   5
   6mod change_list;
   7mod command;
   8mod digraph;
   9mod insert;
  10mod mode_indicator;
  11mod motion;
  12mod normal;
  13mod object;
  14mod replace;
  15mod state;
  16mod surrounds;
  17mod visual;
  18
  19use anyhow::Result;
  20use collections::HashMap;
  21use editor::{
  22    movement::{self, FindRange},
  23    Anchor, Bias, Editor, EditorEvent, EditorMode, ToPoint,
  24};
  25use gpui::{
  26    actions, impl_actions, Action, AppContext, EventEmitter, KeyContext, KeystrokeEvent, Render,
  27    View, ViewContext, WeakView,
  28};
  29use insert::NormalBefore;
  30use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
  31pub use mode_indicator::ModeIndicator;
  32use motion::Motion;
  33use normal::search::SearchSubmit;
  34use schemars::JsonSchema;
  35use serde::Deserialize;
  36use serde_derive::Serialize;
  37use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
  38use state::{Mode, Operator, RecordedSelection, SearchState, VimGlobals};
  39use std::{ops::Range, sync::Arc};
  40use surrounds::SurroundsType;
  41use ui::{IntoElement, VisualContext};
  42use workspace::{self, Pane, Workspace};
  43
  44use crate::state::ReplayableAction;
  45
  46/// Whether or not to enable Vim mode.
  47///
  48/// Default: false
  49pub struct VimModeSetting(pub bool);
  50
  51/// An Action to Switch between modes
  52#[derive(Clone, Deserialize, PartialEq)]
  53pub struct SwitchMode(pub Mode);
  54
  55/// PushOperator is used to put vim into a "minor" mode,
  56/// where it's waiting for a specific next set of keystrokes.
  57/// For example 'd' needs a motion to complete.
  58#[derive(Clone, Deserialize, PartialEq)]
  59pub struct PushOperator(pub Operator);
  60
  61/// Number is used to manage vim's count. Pushing a digit
  62/// multiplis the current value by 10 and adds the digit.
  63#[derive(Clone, Deserialize, PartialEq)]
  64struct Number(usize);
  65
  66#[derive(Clone, Deserialize, PartialEq)]
  67struct SelectRegister(String);
  68
  69actions!(
  70    vim,
  71    [
  72        ClearOperators,
  73        Tab,
  74        Enter,
  75        Object,
  76        InnerObject,
  77        FindForward,
  78        FindBackward,
  79        OpenDefaultKeymap
  80    ]
  81);
  82
  83// in the workspace namespace so it's not filtered out when vim is disabled.
  84actions!(workspace, [ToggleVimMode]);
  85
  86impl_actions!(vim, [SwitchMode, PushOperator, Number, SelectRegister]);
  87
  88/// Initializes the `vim` crate.
  89pub fn init(cx: &mut AppContext) {
  90    VimModeSetting::register(cx);
  91    VimSettings::register(cx);
  92    VimGlobals::register(cx);
  93
  94    cx.observe_new_views(|editor: &mut Editor, cx| Vim::register(editor, cx))
  95        .detach();
  96
  97    cx.observe_new_views(|workspace: &mut Workspace, _| {
  98        workspace.register_action(|workspace, _: &ToggleVimMode, cx| {
  99            let fs = workspace.app_state().fs.clone();
 100            let currently_enabled = Vim::enabled(cx);
 101            update_settings_file::<VimModeSetting>(fs, cx, move |setting, _| {
 102                *setting = Some(!currently_enabled)
 103            })
 104        });
 105
 106        workspace.register_action(|_, _: &OpenDefaultKeymap, cx| {
 107            cx.emit(workspace::Event::OpenBundledFile {
 108                text: settings::vim_keymap(),
 109                title: "Default Vim Bindings",
 110                language: "JSON",
 111            });
 112        });
 113
 114        workspace.register_action(|workspace, _: &SearchSubmit, cx| {
 115            let Some(vim) = workspace
 116                .active_item_as::<Editor>(cx)
 117                .and_then(|editor| editor.read(cx).addon::<VimAddon>().cloned())
 118            else {
 119                return;
 120            };
 121            vim.view
 122                .update(cx, |_, cx| cx.defer(|vim, cx| vim.search_submit(cx)))
 123        });
 124    })
 125    .detach();
 126}
 127
 128#[derive(Clone)]
 129pub(crate) struct VimAddon {
 130    pub(crate) view: View<Vim>,
 131}
 132
 133impl editor::Addon for VimAddon {
 134    fn extend_key_context(&self, key_context: &mut KeyContext, cx: &AppContext) {
 135        self.view.read(cx).extend_key_context(key_context)
 136    }
 137
 138    fn to_any(&self) -> &dyn std::any::Any {
 139        self
 140    }
 141}
 142
 143/// The state pertaining to Vim mode.
 144pub(crate) struct Vim {
 145    pub(crate) mode: Mode,
 146    pub last_mode: Mode,
 147
 148    /// pre_count is the number before an operator is specified (3 in 3d2d)
 149    pre_count: Option<usize>,
 150    /// post_count is the number after an operator is specified (2 in 3d2d)
 151    post_count: Option<usize>,
 152
 153    operator_stack: Vec<Operator>,
 154    pub(crate) replacements: Vec<(Range<editor::Anchor>, String)>,
 155
 156    pub(crate) marks: HashMap<String, Vec<Anchor>>,
 157    pub(crate) stored_visual_mode: Option<(Mode, Vec<bool>)>,
 158    pub(crate) change_list: Vec<Vec<Anchor>>,
 159    pub(crate) change_list_position: Option<usize>,
 160
 161    pub(crate) current_tx: Option<TransactionId>,
 162    pub(crate) current_anchor: Option<Selection<Anchor>>,
 163    pub(crate) undo_modes: HashMap<TransactionId, Mode>,
 164
 165    selected_register: Option<char>,
 166    pub search: SearchState,
 167
 168    editor: WeakView<Editor>,
 169}
 170
 171// Hack: Vim intercepts events dispatched to a window and updates the view in response.
 172// This means it needs a VisualContext. The easiest way to satisfy that constraint is
 173// to make Vim a "View" that is just never actually rendered.
 174impl Render for Vim {
 175    fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
 176        gpui::Empty
 177    }
 178}
 179
 180enum VimEvent {
 181    Focused,
 182}
 183impl EventEmitter<VimEvent> for Vim {}
 184
 185impl Vim {
 186    /// The namespace for Vim actions.
 187    const NAMESPACE: &'static str = "vim";
 188
 189    pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
 190        let editor = cx.view().clone();
 191
 192        cx.new_view(|cx: &mut ViewContext<Vim>| {
 193            cx.subscribe(&editor, |vim, _, event, cx| {
 194                vim.handle_editor_event(event, cx)
 195            })
 196            .detach();
 197
 198            let listener = cx.listener(Vim::observe_keystrokes);
 199            cx.observe_keystrokes(listener).detach();
 200
 201            Vim {
 202                mode: Mode::Normal,
 203                last_mode: Mode::Normal,
 204                pre_count: None,
 205                post_count: None,
 206                operator_stack: Vec::new(),
 207                replacements: Vec::new(),
 208
 209                marks: HashMap::default(),
 210                stored_visual_mode: None,
 211                change_list: Vec::new(),
 212                change_list_position: None,
 213                current_tx: None,
 214                current_anchor: None,
 215                undo_modes: HashMap::default(),
 216
 217                selected_register: None,
 218                search: SearchState::default(),
 219
 220                editor: editor.downgrade(),
 221            }
 222        })
 223    }
 224
 225    fn register(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
 226        if !editor.use_modal_editing() {
 227            return;
 228        }
 229
 230        let mut was_enabled = Vim::enabled(cx);
 231        cx.observe_global::<SettingsStore>(move |editor, cx| {
 232            let enabled = Vim::enabled(cx);
 233            if was_enabled == enabled {
 234                return;
 235            }
 236            was_enabled = enabled;
 237            if enabled {
 238                Self::activate(editor, cx)
 239            } else {
 240                Self::deactivate(editor, cx)
 241            }
 242        })
 243        .detach();
 244        if was_enabled {
 245            Self::activate(editor, cx)
 246        }
 247    }
 248
 249    fn activate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
 250        let vim = Vim::new(cx);
 251
 252        editor.register_addon(VimAddon { view: vim.clone() });
 253
 254        vim.update(cx, |_, cx| {
 255            Vim::action(editor, cx, |vim, action: &SwitchMode, cx| {
 256                vim.switch_mode(action.0, false, cx)
 257            });
 258
 259            Vim::action(editor, cx, |vim, action: &PushOperator, cx| {
 260                vim.push_operator(action.0.clone(), cx)
 261            });
 262
 263            Vim::action(editor, cx, |vim, _: &ClearOperators, cx| {
 264                vim.clear_operator(cx)
 265            });
 266            Vim::action(editor, cx, |vim, n: &Number, cx| {
 267                vim.push_count_digit(n.0, cx);
 268            });
 269            Vim::action(editor, cx, |vim, _: &Tab, cx| {
 270                vim.input_ignored(" ".into(), cx)
 271            });
 272            Vim::action(editor, cx, |vim, _: &Enter, cx| {
 273                vim.input_ignored("\n".into(), cx)
 274            });
 275
 276            normal::register(editor, cx);
 277            insert::register(editor, cx);
 278            motion::register(editor, cx);
 279            command::register(editor, cx);
 280            replace::register(editor, cx);
 281            object::register(editor, cx);
 282            visual::register(editor, cx);
 283            change_list::register(editor, cx);
 284
 285            cx.defer(|vim, cx| {
 286                vim.focused(false, cx);
 287            })
 288        })
 289    }
 290
 291    fn deactivate(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
 292        editor.set_cursor_shape(CursorShape::Bar, cx);
 293        editor.set_clip_at_line_ends(false, cx);
 294        editor.set_collapse_matches(false);
 295        editor.set_input_enabled(true);
 296        editor.set_autoindent(true);
 297        editor.selections.line_mode = false;
 298        editor.unregister_addon::<VimAddon>();
 299    }
 300
 301    /// Register an action on the editor.
 302    pub fn action<A: Action>(
 303        editor: &mut Editor,
 304        cx: &mut ViewContext<Vim>,
 305        f: impl Fn(&mut Vim, &A, &mut ViewContext<Vim>) + 'static,
 306    ) {
 307        let subscription = editor.register_action(cx.listener(f));
 308        cx.on_release(|_, _, _| drop(subscription)).detach();
 309    }
 310
 311    pub fn editor(&self) -> Option<View<Editor>> {
 312        self.editor.upgrade()
 313    }
 314
 315    pub fn workspace(&self, cx: &ViewContext<Self>) -> Option<View<Workspace>> {
 316        self.editor().and_then(|editor| editor.read(cx).workspace())
 317    }
 318
 319    pub fn pane(&self, cx: &ViewContext<Self>) -> Option<View<Pane>> {
 320        self.workspace(cx)
 321            .and_then(|workspace| workspace.read(cx).pane_for(&self.editor()?))
 322    }
 323
 324    pub fn enabled(cx: &mut AppContext) -> bool {
 325        VimModeSetting::get_global(cx).0
 326    }
 327
 328    /// Called whenever an keystroke is typed so vim can observe all actions
 329    /// and keystrokes accordingly.
 330    fn observe_keystrokes(&mut self, keystroke_event: &KeystrokeEvent, cx: &mut ViewContext<Self>) {
 331        if let Some(action) = keystroke_event.action.as_ref() {
 332            // Keystroke is handled by the vim system, so continue forward
 333            if action.name().starts_with("vim::") {
 334                return;
 335            }
 336        } else if cx.has_pending_keystrokes() || keystroke_event.keystroke.is_ime_in_progress() {
 337            return;
 338        }
 339
 340        if let Some(operator) = self.active_operator() {
 341            if !operator.is_waiting(self.mode) {
 342                self.clear_operator(cx);
 343                self.stop_recording_immediately(Box::new(ClearOperators), cx)
 344            }
 345        }
 346    }
 347
 348    fn handle_editor_event(&mut self, event: &EditorEvent, cx: &mut ViewContext<Self>) {
 349        match event {
 350            EditorEvent::Focused => self.focused(true, cx),
 351            EditorEvent::Blurred => self.blurred(cx),
 352            EditorEvent::SelectionsChanged { local: true } => {
 353                self.local_selections_changed(cx);
 354            }
 355            EditorEvent::InputIgnored { text } => {
 356                self.input_ignored(text.clone(), cx);
 357                Vim::globals(cx).observe_insertion(text, None)
 358            }
 359            EditorEvent::InputHandled {
 360                text,
 361                utf16_range_to_replace: range_to_replace,
 362            } => Vim::globals(cx).observe_insertion(text, range_to_replace.clone()),
 363            EditorEvent::TransactionBegun { transaction_id } => {
 364                self.transaction_begun(*transaction_id, cx)
 365            }
 366            EditorEvent::TransactionUndone { transaction_id } => {
 367                self.transaction_undone(transaction_id, cx)
 368            }
 369            EditorEvent::Edited { .. } => self.push_to_change_list(cx),
 370            EditorEvent::FocusedIn => self.sync_vim_settings(cx),
 371            _ => {}
 372        }
 373    }
 374
 375    fn push_operator(&mut self, operator: Operator, cx: &mut ViewContext<Self>) {
 376        if matches!(
 377            operator,
 378            Operator::Change
 379                | Operator::Delete
 380                | Operator::Replace
 381                | Operator::Indent
 382                | Operator::Outdent
 383                | Operator::Lowercase
 384                | Operator::Uppercase
 385                | Operator::OppositeCase
 386                | Operator::ToggleComments
 387        ) {
 388            self.start_recording(cx)
 389        };
 390        // Since these operations can only be entered with pre-operators,
 391        // we need to clear the previous operators when pushing,
 392        // so that the current stack is the most correct
 393        if matches!(
 394            operator,
 395            Operator::AddSurrounds { .. }
 396                | Operator::ChangeSurrounds { .. }
 397                | Operator::DeleteSurrounds
 398        ) {
 399            self.operator_stack.clear();
 400            if let Operator::AddSurrounds { target: None } = operator {
 401                self.start_recording(cx);
 402            }
 403        };
 404        self.operator_stack.push(operator);
 405        self.sync_vim_settings(cx);
 406    }
 407
 408    pub fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut ViewContext<Self>) {
 409        let last_mode = self.mode;
 410        let prior_mode = self.last_mode;
 411        let prior_tx = self.current_tx;
 412        self.last_mode = last_mode;
 413        self.mode = mode;
 414        self.operator_stack.clear();
 415        self.selected_register.take();
 416        if mode == Mode::Normal || mode != last_mode {
 417            self.current_tx.take();
 418            self.current_anchor.take();
 419        }
 420        if mode != Mode::Insert && mode != Mode::Replace {
 421            self.take_count(cx);
 422        }
 423
 424        // Sync editor settings like clip mode
 425        self.sync_vim_settings(cx);
 426
 427        if leave_selections {
 428            return;
 429        }
 430
 431        if !mode.is_visual() && last_mode.is_visual() {
 432            self.create_visual_marks(last_mode, cx);
 433        }
 434
 435        // Adjust selections
 436        self.update_editor(cx, |vim, editor, cx| {
 437            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
 438            {
 439                vim.visual_block_motion(true, editor, cx, |_, point, goal| Some((point, goal)))
 440            }
 441            if last_mode == Mode::Insert || last_mode == Mode::Replace {
 442                if let Some(prior_tx) = prior_tx {
 443                    editor.group_until_transaction(prior_tx, cx)
 444                }
 445            }
 446
 447            editor.change_selections(None, cx, |s| {
 448                // we cheat with visual block mode and use multiple cursors.
 449                // the cost of this cheat is we need to convert back to a single
 450                // cursor whenever vim would.
 451                if last_mode == Mode::VisualBlock
 452                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
 453                {
 454                    let tail = s.oldest_anchor().tail();
 455                    let head = s.newest_anchor().head();
 456                    s.select_anchor_ranges(vec![tail..head]);
 457                } else if last_mode == Mode::Insert
 458                    && prior_mode == Mode::VisualBlock
 459                    && mode != Mode::VisualBlock
 460                {
 461                    let pos = s.first_anchor().head();
 462                    s.select_anchor_ranges(vec![pos..pos])
 463                }
 464
 465                let snapshot = s.display_map();
 466                if let Some(pending) = s.pending.as_mut() {
 467                    if pending.selection.reversed && mode.is_visual() && !last_mode.is_visual() {
 468                        let mut end = pending.selection.end.to_point(&snapshot.buffer_snapshot);
 469                        end = snapshot
 470                            .buffer_snapshot
 471                            .clip_point(end + Point::new(0, 1), Bias::Right);
 472                        pending.selection.end = snapshot.buffer_snapshot.anchor_before(end);
 473                    }
 474                }
 475
 476                s.move_with(|map, selection| {
 477                    if last_mode.is_visual() && !mode.is_visual() {
 478                        let mut point = selection.head();
 479                        if !selection.reversed && !selection.is_empty() {
 480                            point = movement::left(map, selection.head());
 481                        }
 482                        selection.collapse_to(point, selection.goal)
 483                    } else if !last_mode.is_visual() && mode.is_visual() {
 484                        if selection.is_empty() {
 485                            selection.end = movement::right(map, selection.start);
 486                        }
 487                    }
 488                });
 489            })
 490        });
 491    }
 492
 493    fn take_count(&mut self, cx: &mut ViewContext<Self>) -> Option<usize> {
 494        let global_state = cx.global_mut::<VimGlobals>();
 495        if global_state.dot_replaying {
 496            return global_state.recorded_count;
 497        }
 498
 499        let count = if self.post_count == None && self.pre_count == None {
 500            return None;
 501        } else {
 502            Some(self.post_count.take().unwrap_or(1) * self.pre_count.take().unwrap_or(1))
 503        };
 504
 505        if global_state.dot_recording {
 506            global_state.recorded_count = count;
 507        }
 508        self.sync_vim_settings(cx);
 509        count
 510    }
 511
 512    pub fn cursor_shape(&self) -> CursorShape {
 513        match self.mode {
 514            Mode::Normal => {
 515                if self.operator_stack.is_empty() {
 516                    CursorShape::Block
 517                } else {
 518                    CursorShape::Underscore
 519                }
 520            }
 521            Mode::Replace => CursorShape::Underscore,
 522            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => CursorShape::Block,
 523            Mode::Insert => CursorShape::Bar,
 524        }
 525    }
 526
 527    pub fn editor_input_enabled(&self) -> bool {
 528        match self.mode {
 529            Mode::Insert => {
 530                if let Some(operator) = self.operator_stack.last() {
 531                    !operator.is_waiting(self.mode)
 532                } else {
 533                    true
 534                }
 535            }
 536            Mode::Normal | Mode::Replace | Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
 537                false
 538            }
 539        }
 540    }
 541
 542    pub fn should_autoindent(&self) -> bool {
 543        !(self.mode == Mode::Insert && self.last_mode == Mode::VisualBlock)
 544    }
 545
 546    pub fn clip_at_line_ends(&self) -> bool {
 547        match self.mode {
 548            Mode::Insert | Mode::Visual | Mode::VisualLine | Mode::VisualBlock | Mode::Replace => {
 549                false
 550            }
 551            Mode::Normal => true,
 552        }
 553    }
 554
 555    pub fn extend_key_context(&self, context: &mut KeyContext) {
 556        let mut mode = match self.mode {
 557            Mode::Normal => "normal",
 558            Mode::Visual | Mode::VisualLine | Mode::VisualBlock => "visual",
 559            Mode::Insert => "insert",
 560            Mode::Replace => "replace",
 561        }
 562        .to_string();
 563
 564        let mut operator_id = "none";
 565
 566        let active_operator = self.active_operator();
 567        if active_operator.is_none() && self.pre_count.is_some()
 568            || active_operator.is_some() && self.post_count.is_some()
 569        {
 570            context.add("VimCount");
 571        }
 572
 573        if let Some(active_operator) = active_operator {
 574            if active_operator.is_waiting(self.mode) {
 575                mode = "waiting".to_string();
 576            } else {
 577                mode = "operator".to_string();
 578                operator_id = active_operator.id();
 579            }
 580        }
 581
 582        if mode != "waiting" && mode != "insert" && mode != "replace" {
 583            context.add("VimControl");
 584        }
 585        context.set("vim_mode", mode);
 586        context.set("vim_operator", operator_id);
 587    }
 588
 589    fn focused(&mut self, preserve_selection: bool, cx: &mut ViewContext<Self>) {
 590        let Some(editor) = self.editor() else {
 591            return;
 592        };
 593        let editor = editor.read(cx);
 594        let editor_mode = editor.mode();
 595        let newest_selection_empty = editor.selections.newest::<usize>(cx).is_empty();
 596
 597        if editor_mode == EditorMode::Full
 598                && !newest_selection_empty
 599                && self.mode == Mode::Normal
 600                // When following someone, don't switch vim mode.
 601                && editor.leader_peer_id().is_none()
 602        {
 603            if preserve_selection {
 604                self.switch_mode(Mode::Visual, true, cx);
 605            } else {
 606                self.update_editor(cx, |_, editor, cx| {
 607                    editor.set_clip_at_line_ends(false, cx);
 608                    editor.change_selections(None, cx, |s| {
 609                        s.move_with(|_, selection| {
 610                            selection.collapse_to(selection.start, selection.goal)
 611                        })
 612                    });
 613                });
 614            }
 615        }
 616
 617        cx.emit(VimEvent::Focused);
 618        self.sync_vim_settings(cx);
 619    }
 620
 621    fn blurred(&mut self, cx: &mut ViewContext<Self>) {
 622        self.stop_recording_immediately(NormalBefore.boxed_clone(), cx);
 623        self.store_visual_marks(cx);
 624        self.clear_operator(cx);
 625        self.update_editor(cx, |_, editor, cx| {
 626            editor.set_cursor_shape(language::CursorShape::Hollow, cx);
 627        });
 628    }
 629
 630    fn update_editor<S>(
 631        &mut self,
 632        cx: &mut ViewContext<Self>,
 633        update: impl FnOnce(&mut Self, &mut Editor, &mut ViewContext<Editor>) -> S,
 634    ) -> Option<S> {
 635        let editor = self.editor.upgrade()?;
 636        Some(editor.update(cx, |editor, cx| update(self, editor, cx)))
 637    }
 638
 639    fn editor_selections(&mut self, cx: &mut ViewContext<Self>) -> Vec<Range<Anchor>> {
 640        self.update_editor(cx, |_, editor, _| {
 641            editor
 642                .selections
 643                .disjoint_anchors()
 644                .iter()
 645                .map(|selection| selection.tail()..selection.head())
 646                .collect()
 647        })
 648        .unwrap_or_default()
 649    }
 650
 651    /// When doing an action that modifies the buffer, we start recording so that `.`
 652    /// will replay the action.
 653    pub fn start_recording(&mut self, cx: &mut ViewContext<Self>) {
 654        Vim::update_globals(cx, |globals, cx| {
 655            if !globals.dot_replaying {
 656                globals.dot_recording = true;
 657                globals.recorded_actions = Default::default();
 658                globals.recorded_count = None;
 659
 660                let selections = self.editor().map(|editor| {
 661                    let editor = editor.read(cx);
 662                    (
 663                        editor.selections.oldest::<Point>(cx),
 664                        editor.selections.newest::<Point>(cx),
 665                    )
 666                });
 667
 668                if let Some((oldest, newest)) = selections {
 669                    globals.recorded_selection = match self.mode {
 670                        Mode::Visual if newest.end.row == newest.start.row => {
 671                            RecordedSelection::SingleLine {
 672                                cols: newest.end.column - newest.start.column,
 673                            }
 674                        }
 675                        Mode::Visual => RecordedSelection::Visual {
 676                            rows: newest.end.row - newest.start.row,
 677                            cols: newest.end.column,
 678                        },
 679                        Mode::VisualLine => RecordedSelection::VisualLine {
 680                            rows: newest.end.row - newest.start.row,
 681                        },
 682                        Mode::VisualBlock => RecordedSelection::VisualBlock {
 683                            rows: newest.end.row.abs_diff(oldest.start.row),
 684                            cols: newest.end.column.abs_diff(oldest.start.column),
 685                        },
 686                        _ => RecordedSelection::None,
 687                    }
 688                } else {
 689                    globals.recorded_selection = RecordedSelection::None;
 690                }
 691            }
 692        })
 693    }
 694
 695    pub fn stop_replaying(&mut self, cx: &mut ViewContext<Self>) {
 696        let globals = Vim::globals(cx);
 697        globals.dot_replaying = false;
 698        if let Some(replayer) = globals.replayer.take() {
 699            replayer.stop();
 700        }
 701    }
 702
 703    /// When finishing an action that modifies the buffer, stop recording.
 704    /// as you usually call this within a keystroke handler we also ensure that
 705    /// the current action is recorded.
 706    pub fn stop_recording(&mut self, cx: &mut ViewContext<Self>) {
 707        let globals = Vim::globals(cx);
 708        if globals.dot_recording {
 709            globals.stop_recording_after_next_action = true;
 710        }
 711    }
 712
 713    /// Stops recording actions immediately rather than waiting until after the
 714    /// next action to stop recording.
 715    ///
 716    /// This doesn't include the current action.
 717    pub fn stop_recording_immediately(
 718        &mut self,
 719        action: Box<dyn Action>,
 720        cx: &mut ViewContext<Self>,
 721    ) {
 722        let globals = Vim::globals(cx);
 723        if globals.dot_recording {
 724            globals
 725                .recorded_actions
 726                .push(ReplayableAction::Action(action.boxed_clone()));
 727            globals.dot_recording = false;
 728            globals.stop_recording_after_next_action = false;
 729        }
 730    }
 731
 732    /// Explicitly record one action (equivalents to start_recording and stop_recording)
 733    pub fn record_current_action(&mut self, cx: &mut ViewContext<Self>) {
 734        self.start_recording(cx);
 735        self.stop_recording(cx);
 736    }
 737
 738    fn push_count_digit(&mut self, number: usize, cx: &mut ViewContext<Self>) {
 739        if self.active_operator().is_some() {
 740            let post_count = self.post_count.unwrap_or(0);
 741
 742            self.post_count = Some(
 743                post_count
 744                    .checked_mul(10)
 745                    .and_then(|post_count| post_count.checked_add(number))
 746                    .unwrap_or(post_count),
 747            )
 748        } else {
 749            let pre_count = self.pre_count.unwrap_or(0);
 750
 751            self.pre_count = Some(
 752                pre_count
 753                    .checked_mul(10)
 754                    .and_then(|pre_count| pre_count.checked_add(number))
 755                    .unwrap_or(pre_count),
 756            )
 757        }
 758        // update the keymap so that 0 works
 759        self.sync_vim_settings(cx)
 760    }
 761
 762    fn select_register(&mut self, register: Arc<str>, cx: &mut ViewContext<Self>) {
 763        if register.chars().count() == 1 {
 764            self.selected_register
 765                .replace(register.chars().next().unwrap());
 766        }
 767        self.operator_stack.clear();
 768        self.sync_vim_settings(cx);
 769    }
 770
 771    fn maybe_pop_operator(&mut self) -> Option<Operator> {
 772        self.operator_stack.pop()
 773    }
 774
 775    fn pop_operator(&mut self, cx: &mut ViewContext<Self>) -> Operator {
 776        let popped_operator = self.operator_stack.pop()
 777            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
 778        self.sync_vim_settings(cx);
 779        popped_operator
 780    }
 781
 782    fn clear_operator(&mut self, cx: &mut ViewContext<Self>) {
 783        self.take_count(cx);
 784        self.selected_register.take();
 785        self.operator_stack.clear();
 786        self.sync_vim_settings(cx);
 787    }
 788
 789    fn active_operator(&self) -> Option<Operator> {
 790        self.operator_stack.last().cloned()
 791    }
 792
 793    fn transaction_begun(&mut self, transaction_id: TransactionId, _: &mut ViewContext<Self>) {
 794        let mode = if (self.mode == Mode::Insert
 795            || self.mode == Mode::Replace
 796            || self.mode == Mode::Normal)
 797            && self.current_tx.is_none()
 798        {
 799            self.current_tx = Some(transaction_id);
 800            self.last_mode
 801        } else {
 802            self.mode
 803        };
 804        if mode == Mode::VisualLine || mode == Mode::VisualBlock {
 805            self.undo_modes.insert(transaction_id, mode);
 806        }
 807    }
 808
 809    fn transaction_undone(&mut self, transaction_id: &TransactionId, cx: &mut ViewContext<Self>) {
 810        match self.mode {
 811            Mode::VisualLine | Mode::VisualBlock | Mode::Visual => {
 812                self.update_editor(cx, |vim, editor, cx| {
 813                    let original_mode = vim.undo_modes.get(transaction_id);
 814                    editor.change_selections(None, cx, |s| match original_mode {
 815                        Some(Mode::VisualLine) => {
 816                            s.move_with(|map, selection| {
 817                                selection.collapse_to(
 818                                    map.prev_line_boundary(selection.start.to_point(map)).1,
 819                                    SelectionGoal::None,
 820                                )
 821                            });
 822                        }
 823                        Some(Mode::VisualBlock) => {
 824                            let mut first = s.first_anchor();
 825                            first.collapse_to(first.start, first.goal);
 826                            s.select_anchors(vec![first]);
 827                        }
 828                        _ => {
 829                            s.move_with(|map, selection| {
 830                                selection.collapse_to(
 831                                    map.clip_at_line_end(selection.start),
 832                                    selection.goal,
 833                                );
 834                            });
 835                        }
 836                    });
 837                });
 838                self.switch_mode(Mode::Normal, true, cx)
 839            }
 840            Mode::Normal => {
 841                self.update_editor(cx, |_, editor, cx| {
 842                    editor.change_selections(None, cx, |s| {
 843                        s.move_with(|map, selection| {
 844                            selection
 845                                .collapse_to(map.clip_at_line_end(selection.end), selection.goal)
 846                        })
 847                    })
 848                });
 849            }
 850            Mode::Insert | Mode::Replace => {}
 851        }
 852    }
 853
 854    fn local_selections_changed(&mut self, cx: &mut ViewContext<Self>) {
 855        let Some(editor) = self.editor() else { return };
 856
 857        if editor.read(cx).leader_peer_id().is_some() {
 858            return;
 859        }
 860
 861        let newest = editor.read(cx).selections.newest_anchor().clone();
 862        let is_multicursor = editor.read(cx).selections.count() > 1;
 863        if self.mode == Mode::Insert && self.current_tx.is_some() {
 864            if self.current_anchor.is_none() {
 865                self.current_anchor = Some(newest);
 866            } else if self.current_anchor.as_ref().unwrap() != &newest {
 867                if let Some(tx_id) = self.current_tx.take() {
 868                    self.update_editor(cx, |_, editor, cx| {
 869                        editor.group_until_transaction(tx_id, cx)
 870                    });
 871                }
 872            }
 873        } else if self.mode == Mode::Normal && newest.start != newest.end {
 874            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
 875                self.switch_mode(Mode::VisualBlock, false, cx);
 876            } else {
 877                self.switch_mode(Mode::Visual, false, cx)
 878            }
 879        } else if newest.start == newest.end
 880            && !is_multicursor
 881            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&self.mode)
 882        {
 883            self.switch_mode(Mode::Normal, true, cx);
 884        }
 885    }
 886
 887    fn input_ignored(&mut self, text: Arc<str>, cx: &mut ViewContext<Self>) {
 888        if text.is_empty() {
 889            return;
 890        }
 891
 892        match self.active_operator() {
 893            Some(Operator::FindForward { before }) => {
 894                let find = Motion::FindForward {
 895                    before,
 896                    char: text.chars().next().unwrap(),
 897                    mode: if VimSettings::get_global(cx).use_multiline_find {
 898                        FindRange::MultiLine
 899                    } else {
 900                        FindRange::SingleLine
 901                    },
 902                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
 903                };
 904                Vim::globals(cx).last_find = Some(find.clone());
 905                self.motion(find, cx)
 906            }
 907            Some(Operator::FindBackward { after }) => {
 908                let find = Motion::FindBackward {
 909                    after,
 910                    char: text.chars().next().unwrap(),
 911                    mode: if VimSettings::get_global(cx).use_multiline_find {
 912                        FindRange::MultiLine
 913                    } else {
 914                        FindRange::SingleLine
 915                    },
 916                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
 917                };
 918                Vim::globals(cx).last_find = Some(find.clone());
 919                self.motion(find, cx)
 920            }
 921            Some(Operator::Replace) => match self.mode {
 922                Mode::Normal => self.normal_replace(text, cx),
 923                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
 924                    self.visual_replace(text, cx)
 925                }
 926                _ => self.clear_operator(cx),
 927            },
 928            Some(Operator::Digraph { first_char }) => {
 929                if let Some(first_char) = first_char {
 930                    if let Some(second_char) = text.chars().next() {
 931                        self.insert_digraph(first_char, second_char, cx);
 932                    }
 933                } else {
 934                    let first_char = text.chars().next();
 935                    self.pop_operator(cx);
 936                    self.push_operator(Operator::Digraph { first_char }, cx);
 937                }
 938            }
 939            Some(Operator::AddSurrounds { target }) => match self.mode {
 940                Mode::Normal => {
 941                    if let Some(target) = target {
 942                        self.add_surrounds(text, target, cx);
 943                        self.clear_operator(cx);
 944                    }
 945                }
 946                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => {
 947                    self.add_surrounds(text, SurroundsType::Selection, cx);
 948                    self.clear_operator(cx);
 949                }
 950                _ => self.clear_operator(cx),
 951            },
 952            Some(Operator::ChangeSurrounds { target }) => match self.mode {
 953                Mode::Normal => {
 954                    if let Some(target) = target {
 955                        self.change_surrounds(text, target, cx);
 956                        self.clear_operator(cx);
 957                    }
 958                }
 959                _ => self.clear_operator(cx),
 960            },
 961            Some(Operator::DeleteSurrounds) => match self.mode {
 962                Mode::Normal => {
 963                    self.delete_surrounds(text, cx);
 964                    self.clear_operator(cx);
 965                }
 966                _ => self.clear_operator(cx),
 967            },
 968            Some(Operator::Mark) => self.create_mark(text, false, cx),
 969            Some(Operator::RecordRegister) => {
 970                self.record_register(text.chars().next().unwrap(), cx)
 971            }
 972            Some(Operator::ReplayRegister) => {
 973                self.replay_register(text.chars().next().unwrap(), cx)
 974            }
 975            Some(Operator::Register) => match self.mode {
 976                Mode::Insert => {
 977                    self.update_editor(cx, |_, editor, cx| {
 978                        if let Some(register) = Vim::update_globals(cx, |globals, cx| {
 979                            globals.read_register(text.chars().next(), Some(editor), cx)
 980                        }) {
 981                            editor.do_paste(
 982                                &register.text.to_string(),
 983                                register.clipboard_selections.clone(),
 984                                false,
 985                                cx,
 986                            )
 987                        }
 988                    });
 989                    self.clear_operator(cx);
 990                }
 991                _ => {
 992                    self.select_register(text, cx);
 993                }
 994            },
 995            Some(Operator::Jump { line }) => self.jump(text, line, cx),
 996            _ => match self.mode {
 997                Mode::Replace => self.multi_replace(text, cx),
 998                _ => {}
 999            },
1000        }
1001    }
1002
1003    fn sync_vim_settings(&mut self, cx: &mut ViewContext<Self>) {
1004        self.update_editor(cx, |vim, editor, cx| {
1005            editor.set_cursor_shape(vim.cursor_shape(), cx);
1006            editor.set_clip_at_line_ends(vim.clip_at_line_ends(), cx);
1007            editor.set_collapse_matches(true);
1008            editor.set_input_enabled(vim.editor_input_enabled());
1009            editor.set_autoindent(vim.should_autoindent());
1010            editor.selections.line_mode = matches!(vim.mode, Mode::VisualLine);
1011        });
1012        cx.notify()
1013    }
1014}
1015
1016impl Settings for VimModeSetting {
1017    const KEY: Option<&'static str> = Some("vim_mode");
1018
1019    type FileContent = Option<bool>;
1020
1021    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
1022        Ok(Self(sources.user.copied().flatten().unwrap_or(
1023            sources.default.ok_or_else(Self::missing_default)?,
1024        )))
1025    }
1026}
1027
1028/// Controls when to use system clipboard.
1029#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
1030#[serde(rename_all = "snake_case")]
1031pub enum UseSystemClipboard {
1032    /// Don't use system clipboard.
1033    Never,
1034    /// Use system clipboard.
1035    Always,
1036    /// Use system clipboard for yank operations.
1037    OnYank,
1038}
1039
1040#[derive(Deserialize)]
1041struct VimSettings {
1042    pub use_system_clipboard: UseSystemClipboard,
1043    pub use_multiline_find: bool,
1044    pub use_smartcase_find: bool,
1045    pub custom_digraphs: HashMap<String, Arc<str>>,
1046}
1047
1048#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
1049struct VimSettingsContent {
1050    pub use_system_clipboard: Option<UseSystemClipboard>,
1051    pub use_multiline_find: Option<bool>,
1052    pub use_smartcase_find: Option<bool>,
1053    pub custom_digraphs: Option<HashMap<String, Arc<str>>>,
1054}
1055
1056impl Settings for VimSettings {
1057    const KEY: Option<&'static str> = Some("vim");
1058
1059    type FileContent = VimSettingsContent;
1060
1061    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
1062        sources.json_merge()
1063    }
1064}