vim.rs

 1mod editor_events;
 2mod editor_utils;
 3mod insert;
 4mod mode;
 5mod normal;
 6#[cfg(test)]
 7mod vim_tests;
 8
 9use collections::HashMap;
10use editor::{CursorShape, Editor};
11use editor_utils::VimEditorExt;
12use gpui::{action, MutableAppContext, ViewContext, WeakViewHandle};
13
14use mode::Mode;
15use workspace::{self, Settings, Workspace};
16
17action!(SwitchMode, Mode);
18
19pub fn init(cx: &mut MutableAppContext) {
20    editor_events::init(cx);
21    insert::init(cx);
22    normal::init(cx);
23
24    cx.add_action(|_: &mut Workspace, action: &SwitchMode, cx| VimState::switch_mode(action, cx));
25
26    cx.observe_global::<Settings, _>(VimState::settings_changed)
27        .detach();
28}
29
30#[derive(Default)]
31pub struct VimState {
32    editors: HashMap<usize, WeakViewHandle<Editor>>,
33    active_editor: Option<WeakViewHandle<Editor>>,
34
35    enabled: bool,
36    mode: Mode,
37}
38
39impl VimState {
40    fn update_active_editor<S>(
41        cx: &mut MutableAppContext,
42        update: impl FnOnce(&mut Editor, &mut ViewContext<Editor>) -> S,
43    ) -> Option<S> {
44        cx.global::<Self>()
45            .active_editor
46            .clone()
47            .and_then(|ae| ae.upgrade(cx))
48            .map(|ae| ae.update(cx, update))
49    }
50
51    fn switch_mode(SwitchMode(mode): &SwitchMode, cx: &mut MutableAppContext) {
52        let active_editor = cx.update_default_global(|this: &mut Self, _| {
53            this.mode = *mode;
54            this.active_editor.clone()
55        });
56
57        if let Some(active_editor) = active_editor.and_then(|e| e.upgrade(cx)) {
58            active_editor.update(cx, |active_editor, cx| {
59                active_editor.set_keymap_context_layer::<Self>(mode.keymap_context_layer());
60                active_editor.set_input_enabled(*mode == Mode::Insert);
61                if *mode != Mode::Insert {
62                    active_editor.clip_selections(cx);
63                }
64            });
65        }
66        VimState::update_cursor_shapes(cx);
67    }
68
69    fn settings_changed(cx: &mut MutableAppContext) {
70        cx.update_default_global(|this: &mut Self, cx| {
71            let settings = cx.global::<Settings>();
72            if this.enabled != settings.vim_mode {
73                this.enabled = settings.vim_mode;
74                this.mode = if settings.vim_mode {
75                    Mode::Normal
76                } else {
77                    Mode::Insert
78                };
79                Self::update_cursor_shapes(cx);
80            }
81        });
82    }
83
84    fn update_cursor_shapes(cx: &mut MutableAppContext) {
85        cx.defer(move |cx| {
86            cx.update_default_global(|this: &mut VimState, cx| {
87                let cursor_shape = this.mode.cursor_shape();
88                for editor in this.editors.values() {
89                    if let Some(editor) = editor.upgrade(cx) {
90                        editor.update(cx, |editor, cx| {
91                            editor.set_cursor_shape(cursor_shape, cx);
92                            editor.set_clip_at_line_ends(cursor_shape == CursorShape::Block, cx);
93                        });
94                    }
95                }
96            });
97        });
98    }
99}