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