1mod editor_events;
2mod editor_utils;
3mod insert;
4mod mode;
5mod normal;
6#[cfg(test)]
7mod vim_tests;
8
9use collections::HashMap;
10use editor::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.adjust_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 });
93 }
94 }
95 });
96 });
97 }
98}