vim.rs

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