vim.rs

  1//! Vim support for Zed.
  2
  3#[cfg(test)]
  4mod test;
  5
  6mod command;
  7mod editor_events;
  8mod insert;
  9mod mode_indicator;
 10mod motion;
 11mod normal;
 12mod object;
 13mod replace;
 14mod state;
 15mod surrounds;
 16mod utils;
 17mod visual;
 18
 19use anyhow::Result;
 20use collections::HashMap;
 21use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
 22use editor::{
 23    movement::{self, FindRange},
 24    Anchor, Editor, EditorEvent, EditorMode,
 25};
 26use gpui::{
 27    actions, impl_actions, Action, AppContext, EntityId, FocusableView, Global, KeystrokeEvent,
 28    Subscription, View, ViewContext, WeakView, WindowContext,
 29};
 30use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
 31pub use mode_indicator::ModeIndicator;
 32use motion::Motion;
 33use normal::normal_replace;
 34use replace::multi_replace;
 35use schemars::JsonSchema;
 36use serde::Deserialize;
 37use serde_derive::Serialize;
 38use settings::{update_settings_file, Settings, SettingsSources, SettingsStore};
 39use state::{EditorState, Mode, Operator, RecordedSelection, WorkspaceState};
 40use std::{ops::Range, sync::Arc};
 41use surrounds::{add_surrounds, change_surrounds, delete_surrounds};
 42use ui::BorrowAppContext;
 43use visual::{visual_block_motion, visual_replace};
 44use workspace::{self, Workspace};
 45
 46use crate::state::ReplayableAction;
 47
 48/// Whether or not to enable Vim mode (work in progress).
 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
 68actions!(
 69    vim,
 70    [
 71        Tab,
 72        Enter,
 73        Object,
 74        InnerObject,
 75        FindForward,
 76        FindBackward,
 77        OpenDefaultKeymap
 78    ]
 79);
 80
 81// in the workspace namespace so it's not filtered out when vim is disabled.
 82actions!(workspace, [ToggleVimMode]);
 83
 84impl_actions!(vim, [SwitchMode, PushOperator, Number]);
 85
 86/// Initializes the `vim` crate.
 87pub fn init(cx: &mut AppContext) {
 88    cx.set_global(Vim::default());
 89    VimModeSetting::register(cx);
 90    VimSettings::register(cx);
 91
 92    cx.observe_keystrokes(observe_keystrokes).detach();
 93    editor_events::init(cx);
 94
 95    cx.observe_new_views(|workspace: &mut Workspace, cx| register(workspace, cx))
 96        .detach();
 97
 98    // Any time settings change, update vim mode to match. The Vim struct
 99    // will be initialized as disabled by default, so we filter its commands
100    // out when starting up.
101    CommandPaletteFilter::update_global(cx, |filter, _| {
102        filter.hide_namespace(Vim::NAMESPACE);
103    });
104    cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
105        vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
106    });
107    cx.observe_global::<SettingsStore>(|cx| {
108        cx.update_global(|vim: &mut Vim, cx: &mut AppContext| {
109            vim.set_enabled(VimModeSetting::get_global(cx).0, cx)
110        });
111    })
112    .detach();
113}
114
115fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
116    workspace.register_action(|_: &mut Workspace, &SwitchMode(mode): &SwitchMode, cx| {
117        Vim::update(cx, |vim, cx| vim.switch_mode(mode, false, cx))
118    });
119    workspace.register_action(
120        |_: &mut Workspace, PushOperator(operator): &PushOperator, cx| {
121            Vim::update(cx, |vim, cx| vim.push_operator(operator.clone(), cx))
122        },
123    );
124    workspace.register_action(|_: &mut Workspace, n: &Number, cx: _| {
125        Vim::update(cx, |vim, cx| vim.push_count_digit(n.0, cx));
126    });
127
128    workspace.register_action(|_: &mut Workspace, _: &Tab, cx| {
129        Vim::active_editor_input_ignored(" ".into(), cx)
130    });
131
132    workspace.register_action(|_: &mut Workspace, _: &Enter, cx| {
133        Vim::active_editor_input_ignored("\n".into(), cx)
134    });
135
136    workspace.register_action(|workspace: &mut Workspace, _: &ToggleVimMode, cx| {
137        let fs = workspace.app_state().fs.clone();
138        let currently_enabled = VimModeSetting::get_global(cx).0;
139        update_settings_file::<VimModeSetting>(fs, cx, move |setting| {
140            *setting = Some(!currently_enabled)
141        })
142    });
143
144    workspace.register_action(|_: &mut Workspace, _: &OpenDefaultKeymap, cx| {
145        cx.emit(workspace::Event::OpenBundledFile {
146            text: settings::vim_keymap(),
147            title: "Default Vim Bindings",
148            language: "JSON",
149        });
150    });
151
152    normal::register(workspace, cx);
153    insert::register(workspace, cx);
154    motion::register(workspace, cx);
155    command::register(workspace, cx);
156    replace::register(workspace, cx);
157    object::register(workspace, cx);
158    visual::register(workspace, cx);
159}
160
161/// Called whenever an keystroke is typed so vim can observe all actions
162/// and keystrokes accordingly.
163fn observe_keystrokes(keystroke_event: &KeystrokeEvent, cx: &mut WindowContext) {
164    if let Some(action) = keystroke_event
165        .action
166        .as_ref()
167        .map(|action| action.boxed_clone())
168    {
169        Vim::update(cx, |vim, _| {
170            if vim.workspace_state.recording {
171                vim.workspace_state
172                    .recorded_actions
173                    .push(ReplayableAction::Action(action.boxed_clone()));
174
175                if vim.workspace_state.stop_recording_after_next_action {
176                    vim.workspace_state.recording = false;
177                    vim.workspace_state.stop_recording_after_next_action = false;
178                }
179            }
180        });
181
182        // Keystroke is handled by the vim system, so continue forward
183        if action.name().starts_with("vim::") {
184            return;
185        }
186    } else if cx.has_pending_keystrokes() {
187        return;
188    }
189
190    Vim::update(cx, |vim, cx| match vim.active_operator() {
191        Some(
192            Operator::FindForward { .. }
193            | Operator::FindBackward { .. }
194            | Operator::Replace
195            | Operator::AddSurrounds { .. }
196            | Operator::ChangeSurrounds { .. }
197            | Operator::DeleteSurrounds,
198        ) => {}
199        Some(_) => {
200            vim.clear_operator(cx);
201        }
202        _ => {}
203    });
204}
205
206/// The state pertaining to Vim mode.
207#[derive(Default)]
208struct Vim {
209    active_editor: Option<WeakView<Editor>>,
210    editor_subscription: Option<Subscription>,
211    enabled: bool,
212    editor_states: HashMap<EntityId, EditorState>,
213    workspace_state: WorkspaceState,
214    default_state: EditorState,
215}
216
217impl Global for Vim {}
218
219impl Vim {
220    /// The namespace for Vim actions.
221    const NAMESPACE: &'static str = "vim";
222
223    fn read(cx: &mut AppContext) -> &Self {
224        cx.global::<Self>()
225    }
226
227    fn update<F, S>(cx: &mut WindowContext, update: F) -> S
228    where
229        F: FnOnce(&mut Self, &mut WindowContext) -> S,
230    {
231        cx.update_global(update)
232    }
233
234    fn activate_editor(&mut self, editor: View<Editor>, cx: &mut WindowContext) {
235        if !editor.read(cx).use_modal_editing() {
236            return;
237        }
238
239        self.active_editor = Some(editor.clone().downgrade());
240        self.editor_subscription = Some(cx.subscribe(&editor, |editor, event, cx| match event {
241            EditorEvent::SelectionsChanged { local: true } => {
242                let editor = editor.read(cx);
243                if editor.leader_peer_id().is_none() {
244                    let newest = editor.selections.newest_anchor().clone();
245                    let is_multicursor = editor.selections.count() > 1;
246                    Vim::update(cx, |vim, cx| {
247                        vim.local_selections_changed(newest, is_multicursor, cx);
248                    })
249                }
250            }
251            EditorEvent::InputIgnored { text } => {
252                Vim::active_editor_input_ignored(text.clone(), cx);
253                Vim::record_insertion(text, None, cx)
254            }
255            EditorEvent::InputHandled {
256                text,
257                utf16_range_to_replace: range_to_replace,
258            } => Vim::record_insertion(text, range_to_replace.clone(), cx),
259            EditorEvent::TransactionBegun { transaction_id } => Vim::update(cx, |vim, cx| {
260                vim.transaction_begun(*transaction_id, cx);
261            }),
262            EditorEvent::TransactionUndone { transaction_id } => Vim::update(cx, |vim, cx| {
263                vim.transaction_undone(transaction_id, cx);
264            }),
265            _ => {}
266        }));
267
268        let editor = editor.read(cx);
269        let editor_mode = editor.mode();
270        let newest_selection_empty = editor.selections.newest::<usize>(cx).is_empty();
271
272        if editor_mode == EditorMode::Full
273                && !newest_selection_empty
274                && self.state().mode == Mode::Normal
275                // When following someone, don't switch vim mode.
276                && editor.leader_peer_id().is_none()
277        {
278            self.switch_mode(Mode::Visual, true, cx);
279        }
280
281        self.sync_vim_settings(cx);
282    }
283
284    fn record_insertion(
285        text: &Arc<str>,
286        range_to_replace: Option<Range<isize>>,
287        cx: &mut WindowContext,
288    ) {
289        Vim::update(cx, |vim, _| {
290            if vim.workspace_state.recording {
291                vim.workspace_state
292                    .recorded_actions
293                    .push(ReplayableAction::Insertion {
294                        text: text.clone(),
295                        utf16_range_to_replace: range_to_replace,
296                    });
297                if vim.workspace_state.stop_recording_after_next_action {
298                    vim.workspace_state.recording = false;
299                    vim.workspace_state.stop_recording_after_next_action = false;
300                }
301            }
302        });
303    }
304
305    fn update_active_editor<S>(
306        &mut self,
307        cx: &mut WindowContext,
308        update: impl FnOnce(&mut Vim, &mut Editor, &mut ViewContext<Editor>) -> S,
309    ) -> Option<S> {
310        let editor = self.active_editor.clone()?.upgrade()?;
311        Some(editor.update(cx, |editor, cx| update(self, editor, cx)))
312    }
313
314    fn editor_selections(&mut self, cx: &mut WindowContext) -> Vec<Range<Anchor>> {
315        self.update_active_editor(cx, |_, editor, _| {
316            editor
317                .selections
318                .disjoint_anchors()
319                .iter()
320                .map(|selection| selection.tail()..selection.head())
321                .collect()
322        })
323        .unwrap_or_default()
324    }
325
326    /// When doing an action that modifies the buffer, we start recording so that `.`
327    /// will replay the action.
328    pub fn start_recording(&mut self, cx: &mut WindowContext) {
329        if !self.workspace_state.replaying {
330            self.workspace_state.recording = true;
331            self.workspace_state.recorded_actions = Default::default();
332            self.workspace_state.recorded_count = None;
333
334            let selections = self
335                .active_editor
336                .as_ref()
337                .and_then(|editor| editor.upgrade())
338                .map(|editor| {
339                    let editor = editor.read(cx);
340                    (
341                        editor.selections.oldest::<Point>(cx),
342                        editor.selections.newest::<Point>(cx),
343                    )
344                });
345
346            if let Some((oldest, newest)) = selections {
347                self.workspace_state.recorded_selection = match self.state().mode {
348                    Mode::Visual if newest.end.row == newest.start.row => {
349                        RecordedSelection::SingleLine {
350                            cols: newest.end.column - newest.start.column,
351                        }
352                    }
353                    Mode::Visual => RecordedSelection::Visual {
354                        rows: newest.end.row - newest.start.row,
355                        cols: newest.end.column,
356                    },
357                    Mode::VisualLine => RecordedSelection::VisualLine {
358                        rows: newest.end.row - newest.start.row,
359                    },
360                    Mode::VisualBlock => RecordedSelection::VisualBlock {
361                        rows: newest.end.row.abs_diff(oldest.start.row),
362                        cols: newest.end.column.abs_diff(oldest.start.column),
363                    },
364                    _ => RecordedSelection::None,
365                }
366            } else {
367                self.workspace_state.recorded_selection = RecordedSelection::None;
368            }
369        }
370    }
371
372    pub fn stop_replaying(&mut self) {
373        self.workspace_state.replaying = false;
374    }
375
376    /// When finishing an action that modifies the buffer, stop recording.
377    /// as you usually call this within a keystroke handler we also ensure that
378    /// the current action is recorded.
379    pub fn stop_recording(&mut self) {
380        if self.workspace_state.recording {
381            self.workspace_state.stop_recording_after_next_action = true;
382        }
383    }
384
385    /// Stops recording actions immediately rather than waiting until after the
386    /// next action to stop recording.
387    ///
388    /// This doesn't include the current action.
389    pub fn stop_recording_immediately(&mut self, action: Box<dyn Action>) {
390        if self.workspace_state.recording {
391            self.workspace_state
392                .recorded_actions
393                .push(ReplayableAction::Action(action.boxed_clone()));
394            self.workspace_state.recording = false;
395            self.workspace_state.stop_recording_after_next_action = false;
396        }
397    }
398
399    /// Explicitly record one action (equivalents to start_recording and stop_recording)
400    pub fn record_current_action(&mut self, cx: &mut WindowContext) {
401        self.start_recording(cx);
402        self.stop_recording();
403    }
404
405    fn switch_mode(&mut self, mode: Mode, leave_selections: bool, cx: &mut WindowContext) {
406        let state = self.state();
407        let last_mode = state.mode;
408        let prior_mode = state.last_mode;
409        let prior_tx = state.current_tx;
410        self.update_state(|state| {
411            state.last_mode = last_mode;
412            state.mode = mode;
413            state.operator_stack.clear();
414            state.current_tx.take();
415            state.current_anchor.take();
416        });
417        if mode != Mode::Insert {
418            self.take_count(cx);
419        }
420
421        // Sync editor settings like clip mode
422        self.sync_vim_settings(cx);
423
424        if leave_selections {
425            return;
426        }
427
428        // Adjust selections
429        self.update_active_editor(cx, |_, editor, cx| {
430            if last_mode != Mode::VisualBlock && last_mode.is_visual() && mode == Mode::VisualBlock
431            {
432                visual_block_motion(true, editor, cx, |_, point, goal| Some((point, goal)))
433            }
434            if last_mode == Mode::Insert || last_mode == Mode::Replace {
435                if let Some(prior_tx) = prior_tx {
436                    editor.group_until_transaction(prior_tx, cx)
437                }
438            }
439
440            editor.change_selections(None, cx, |s| {
441                // we cheat with visual block mode and use multiple cursors.
442                // the cost of this cheat is we need to convert back to a single
443                // cursor whenever vim would.
444                if last_mode == Mode::VisualBlock
445                    && (mode != Mode::VisualBlock && mode != Mode::Insert)
446                {
447                    let tail = s.oldest_anchor().tail();
448                    let head = s.newest_anchor().head();
449                    s.select_anchor_ranges(vec![tail..head]);
450                } else if last_mode == Mode::Insert
451                    && prior_mode == Mode::VisualBlock
452                    && mode != Mode::VisualBlock
453                {
454                    let pos = s.first_anchor().head();
455                    s.select_anchor_ranges(vec![pos..pos])
456                }
457
458                s.move_with(|map, selection| {
459                    if last_mode.is_visual() && !mode.is_visual() {
460                        let mut point = selection.head();
461                        if !selection.reversed && !selection.is_empty() {
462                            point = movement::left(map, selection.head());
463                        }
464                        selection.collapse_to(point, selection.goal)
465                    } else if !last_mode.is_visual() && mode.is_visual() {
466                        if selection.is_empty() {
467                            selection.end = movement::right(map, selection.start);
468                        }
469                    } else if last_mode == Mode::Replace {
470                        if selection.head().column() != 0 {
471                            let point = movement::left(map, selection.head());
472                            selection.collapse_to(point, selection.goal)
473                        }
474                    }
475                });
476            })
477        });
478    }
479
480    fn push_count_digit(&mut self, number: usize, cx: &mut WindowContext) {
481        if self.active_operator().is_some() {
482            self.update_state(|state| {
483                state.post_count = Some(state.post_count.unwrap_or(0) * 10 + number)
484            })
485        } else {
486            self.update_state(|state| {
487                state.pre_count = Some(state.pre_count.unwrap_or(0) * 10 + number)
488            })
489        }
490        // update the keymap so that 0 works
491        self.sync_vim_settings(cx)
492    }
493
494    fn take_count(&mut self, cx: &mut WindowContext) -> Option<usize> {
495        if self.workspace_state.replaying {
496            return self.workspace_state.recorded_count;
497        }
498
499        let count = if self.state().post_count == None && self.state().pre_count == None {
500            return None;
501        } else {
502            Some(self.update_state(|state| {
503                state.post_count.take().unwrap_or(1) * state.pre_count.take().unwrap_or(1)
504            }))
505        };
506        if self.workspace_state.recording {
507            self.workspace_state.recorded_count = count;
508        }
509        self.sync_vim_settings(cx);
510        count
511    }
512
513    fn push_operator(&mut self, operator: Operator, cx: &mut WindowContext) {
514        if matches!(
515            operator,
516            Operator::Change | Operator::Delete | Operator::Replace
517        ) {
518            self.start_recording(cx)
519        };
520        // Since these operations can only be entered with pre-operators,
521        // we need to clear the previous operators when pushing,
522        // so that the current stack is the most correct
523        if matches!(
524            operator,
525            Operator::AddSurrounds { .. }
526                | Operator::ChangeSurrounds { .. }
527                | Operator::DeleteSurrounds
528        ) {
529            self.clear_operator(cx);
530        };
531        self.update_state(|state| state.operator_stack.push(operator));
532        self.sync_vim_settings(cx);
533    }
534
535    fn maybe_pop_operator(&mut self) -> Option<Operator> {
536        self.update_state(|state| state.operator_stack.pop())
537    }
538
539    fn pop_operator(&mut self, cx: &mut WindowContext) -> Operator {
540        let popped_operator = self.update_state(|state| state.operator_stack.pop())
541            .expect("Operator popped when no operator was on the stack. This likely means there is an invalid keymap config");
542        self.sync_vim_settings(cx);
543        popped_operator
544    }
545
546    fn clear_operator(&mut self, cx: &mut WindowContext) {
547        self.take_count(cx);
548        self.update_state(|state| state.operator_stack.clear());
549        self.sync_vim_settings(cx);
550    }
551
552    fn active_operator(&self) -> Option<Operator> {
553        self.state().operator_stack.last().cloned()
554    }
555
556    fn transaction_begun(&mut self, transaction_id: TransactionId, _: &mut WindowContext) {
557        self.update_state(|state| {
558            let mode = if (state.mode == Mode::Insert
559                || state.mode == Mode::Replace
560                || state.mode == Mode::Normal)
561                && state.current_tx.is_none()
562            {
563                state.current_tx = Some(transaction_id);
564                state.last_mode
565            } else {
566                state.mode
567            };
568            if mode == Mode::VisualLine || mode == Mode::VisualBlock {
569                state.undo_modes.insert(transaction_id, mode);
570            }
571        });
572    }
573
574    fn transaction_undone(&mut self, transaction_id: &TransactionId, cx: &mut WindowContext) {
575        if !self.state().mode.is_visual() {
576            return;
577        };
578        self.update_active_editor(cx, |vim, editor, cx| {
579            let original_mode = vim.state().undo_modes.get(transaction_id);
580            editor.change_selections(None, cx, |s| match original_mode {
581                Some(Mode::VisualLine) => {
582                    s.move_with(|map, selection| {
583                        selection.collapse_to(
584                            map.prev_line_boundary(selection.start.to_point(map)).1,
585                            SelectionGoal::None,
586                        )
587                    });
588                }
589                Some(Mode::VisualBlock) => {
590                    let mut first = s.first_anchor();
591                    first.collapse_to(first.start, first.goal);
592                    s.select_anchors(vec![first]);
593                }
594                _ => {
595                    s.move_with(|_, selection| {
596                        selection.collapse_to(selection.start, selection.goal);
597                    });
598                }
599            });
600        });
601        self.switch_mode(Mode::Normal, true, cx)
602    }
603
604    fn local_selections_changed(
605        &mut self,
606        newest: Selection<editor::Anchor>,
607        is_multicursor: bool,
608        cx: &mut WindowContext,
609    ) {
610        let state = self.state();
611        if state.mode == Mode::Insert && state.current_tx.is_some() {
612            if state.current_anchor.is_none() {
613                self.update_state(|state| state.current_anchor = Some(newest));
614            } else if state.current_anchor.as_ref().unwrap() != &newest {
615                if let Some(tx_id) = self.update_state(|state| state.current_tx.take()) {
616                    self.update_active_editor(cx, |_, editor, cx| {
617                        editor.group_until_transaction(tx_id, cx)
618                    });
619                }
620            }
621        } else if state.mode == Mode::Normal && newest.start != newest.end {
622            if matches!(newest.goal, SelectionGoal::HorizontalRange { .. }) {
623                self.switch_mode(Mode::VisualBlock, false, cx);
624            } else {
625                self.switch_mode(Mode::Visual, false, cx)
626            }
627        } else if newest.start == newest.end
628            && !is_multicursor
629            && [Mode::Visual, Mode::VisualLine, Mode::VisualBlock].contains(&state.mode)
630        {
631            self.switch_mode(Mode::Normal, true, cx)
632        }
633    }
634
635    fn active_editor_input_ignored(text: Arc<str>, cx: &mut WindowContext) {
636        if text.is_empty() {
637            return;
638        }
639
640        match Vim::read(cx).active_operator() {
641            Some(Operator::FindForward { before }) => {
642                let find = Motion::FindForward {
643                    before,
644                    char: text.chars().next().unwrap(),
645                    mode: if VimSettings::get_global(cx).use_multiline_find {
646                        FindRange::MultiLine
647                    } else {
648                        FindRange::SingleLine
649                    },
650                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
651                };
652                Vim::update(cx, |vim, _| {
653                    vim.workspace_state.last_find = Some(find.clone())
654                });
655                motion::motion(find, cx)
656            }
657            Some(Operator::FindBackward { after }) => {
658                let find = Motion::FindBackward {
659                    after,
660                    char: text.chars().next().unwrap(),
661                    mode: if VimSettings::get_global(cx).use_multiline_find {
662                        FindRange::MultiLine
663                    } else {
664                        FindRange::SingleLine
665                    },
666                    smartcase: VimSettings::get_global(cx).use_smartcase_find,
667                };
668                Vim::update(cx, |vim, _| {
669                    vim.workspace_state.last_find = Some(find.clone())
670                });
671                motion::motion(find, cx)
672            }
673            Some(Operator::Replace) => match Vim::read(cx).state().mode {
674                Mode::Normal => normal_replace(text, cx),
675                Mode::Visual | Mode::VisualLine | Mode::VisualBlock => visual_replace(text, cx),
676                _ => Vim::update(cx, |vim, cx| vim.clear_operator(cx)),
677            },
678            Some(Operator::AddSurrounds { target }) => match Vim::read(cx).state().mode {
679                Mode::Normal => {
680                    if let Some(target) = target {
681                        add_surrounds(text, target, cx);
682                        Vim::update(cx, |vim, cx| vim.clear_operator(cx));
683                    }
684                }
685                _ => Vim::update(cx, |vim, cx| vim.clear_operator(cx)),
686            },
687            Some(Operator::ChangeSurrounds { target }) => match Vim::read(cx).state().mode {
688                Mode::Normal => {
689                    if let Some(target) = target {
690                        change_surrounds(text, target, cx);
691                        Vim::update(cx, |vim, cx| vim.clear_operator(cx));
692                    }
693                }
694                _ => Vim::update(cx, |vim, cx| vim.clear_operator(cx)),
695            },
696            Some(Operator::DeleteSurrounds) => match Vim::read(cx).state().mode {
697                Mode::Normal => {
698                    delete_surrounds(text, cx);
699                    Vim::update(cx, |vim, cx| vim.clear_operator(cx));
700                }
701                _ => Vim::update(cx, |vim, cx| vim.clear_operator(cx)),
702            },
703            _ => match Vim::read(cx).state().mode {
704                Mode::Replace => multi_replace(text, cx),
705                _ => {}
706            },
707        }
708    }
709
710    fn set_enabled(&mut self, enabled: bool, cx: &mut AppContext) {
711        if self.enabled == enabled {
712            return;
713        }
714        if !enabled {
715            CommandPaletteInterceptor::update_global(cx, |interceptor, _| {
716                interceptor.clear();
717            });
718            CommandPaletteFilter::update_global(cx, |filter, _| {
719                filter.hide_namespace(Self::NAMESPACE);
720            });
721            *self = Default::default();
722            return;
723        }
724
725        self.enabled = true;
726        CommandPaletteFilter::update_global(cx, |filter, _| {
727            filter.show_namespace(Self::NAMESPACE);
728        });
729        CommandPaletteInterceptor::update_global(cx, |interceptor, _| {
730            interceptor.set(Box::new(command::command_interceptor));
731        });
732
733        if let Some(active_window) = cx
734            .active_window()
735            .and_then(|window| window.downcast::<Workspace>())
736        {
737            active_window
738                .update(cx, |workspace, cx| {
739                    let active_editor = workspace.active_item_as::<Editor>(cx);
740                    if let Some(active_editor) = active_editor {
741                        self.activate_editor(active_editor, cx);
742                        self.switch_mode(Mode::Normal, false, cx);
743                    }
744                })
745                .ok();
746        }
747    }
748
749    /// Returns the state of the active editor.
750    pub fn state(&self) -> &EditorState {
751        if let Some(active_editor) = self.active_editor.as_ref() {
752            if let Some(state) = self.editor_states.get(&active_editor.entity_id()) {
753                return state;
754            }
755        }
756
757        &self.default_state
758    }
759
760    /// Updates the state of the active editor.
761    pub fn update_state<T>(&mut self, func: impl FnOnce(&mut EditorState) -> T) -> T {
762        let mut state = self.state().clone();
763        let ret = func(&mut state);
764
765        if let Some(active_editor) = self.active_editor.as_ref() {
766            self.editor_states.insert(active_editor.entity_id(), state);
767        }
768
769        ret
770    }
771
772    fn sync_vim_settings(&mut self, cx: &mut WindowContext) {
773        self.update_active_editor(cx, |vim, editor, cx| {
774            let state = vim.state();
775            editor.set_cursor_shape(state.cursor_shape(), cx);
776            editor.set_clip_at_line_ends(state.clip_at_line_ends(), cx);
777            editor.set_collapse_matches(true);
778            editor.set_input_enabled(!state.vim_controlled());
779            editor.set_autoindent(state.should_autoindent());
780            editor.selections.line_mode = matches!(state.mode, Mode::VisualLine);
781            if editor.is_focused(cx) {
782                editor.set_keymap_context_layer::<Self>(state.keymap_context_layer(), cx);
783            // disables vim if the rename editor is focused,
784            // but not if the command palette is open.
785            } else if editor.focus_handle(cx).contains_focused(cx) {
786                editor.remove_keymap_context_layer::<Self>(cx)
787            }
788        });
789    }
790
791    fn unhook_vim_settings(editor: &mut Editor, cx: &mut ViewContext<Editor>) {
792        if editor.mode() == EditorMode::Full {
793            editor.set_cursor_shape(CursorShape::Bar, cx);
794            editor.set_clip_at_line_ends(false, cx);
795            editor.set_collapse_matches(false);
796            editor.set_input_enabled(true);
797            editor.set_autoindent(true);
798            editor.selections.line_mode = false;
799        }
800        editor.remove_keymap_context_layer::<Self>(cx)
801    }
802}
803
804impl Settings for VimModeSetting {
805    const KEY: Option<&'static str> = Some("vim_mode");
806
807    type FileContent = Option<bool>;
808
809    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
810        Ok(Self(sources.user.copied().flatten().unwrap_or(
811            sources.default.ok_or_else(Self::missing_default)?,
812        )))
813    }
814}
815
816/// Controls when to use system clipboard.
817#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
818#[serde(rename_all = "snake_case")]
819pub enum UseSystemClipboard {
820    /// Don't use system clipboard.
821    Never,
822    /// Use system clipboard.
823    Always,
824    /// Use system clipboard for yank operations.
825    OnYank,
826}
827
828#[derive(Deserialize)]
829struct VimSettings {
830    // all vim uses vim clipboard
831    // vim always uses system cliupbaord
832    // some magic where yy is system and dd is not.
833    pub use_system_clipboard: UseSystemClipboard,
834    pub use_multiline_find: bool,
835    pub use_smartcase_find: bool,
836}
837
838#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
839struct VimSettingsContent {
840    pub use_system_clipboard: Option<UseSystemClipboard>,
841    pub use_multiline_find: Option<bool>,
842    pub use_smartcase_find: Option<bool>,
843}
844
845impl Settings for VimSettings {
846    const KEY: Option<&'static str> = Some("vim");
847
848    type FileContent = VimSettingsContent;
849
850    fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
851        sources.json_merge()
852    }
853}