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