editor_events.rs

  1use crate::Vim;
  2use editor::{Editor, EditorEvent};
  3use gpui::{AppContext, Entity, EntityId, View, ViewContext, WindowContext};
  4
  5pub fn init(cx: &mut AppContext) {
  6    cx.observe_new_views(|_, cx: &mut ViewContext<Editor>| {
  7        let editor = cx.view().clone();
  8        cx.subscribe(&editor, |_, editor, event: &EditorEvent, cx| match event {
  9            EditorEvent::Focused => cx.window_context().defer(|cx| focused(editor, cx)),
 10            EditorEvent::Blurred => cx.window_context().defer(|cx| blurred(editor, cx)),
 11            _ => {}
 12        })
 13        .detach();
 14
 15        let id = cx.view().entity_id();
 16        cx.on_release(move |_, _, cx| released(id, cx)).detach();
 17    })
 18    .detach();
 19}
 20
 21fn focused(editor: View<Editor>, cx: &mut WindowContext) {
 22    if Vim::read(cx).active_editor.clone().is_some() {
 23        Vim::update(cx, |vim, cx| {
 24            vim.update_active_editor(cx, |previously_active_editor, cx| {
 25                vim.unhook_vim_settings(previously_active_editor, cx)
 26            });
 27        });
 28    }
 29
 30    Vim::update(cx, |vim, cx| {
 31        vim.set_active_editor(editor.clone(), cx);
 32    });
 33}
 34
 35fn blurred(editor: View<Editor>, cx: &mut WindowContext) {
 36    Vim::update(cx, |vim, cx| {
 37        vim.workspace_state.recording = false;
 38        vim.workspace_state.recorded_actions.clear();
 39        if let Some(previous_editor) = vim.active_editor.clone() {
 40            if previous_editor
 41                .upgrade()
 42                .is_some_and(|previous| previous == editor.clone())
 43            {
 44                vim.clear_operator(cx);
 45                vim.active_editor = None;
 46                vim.editor_subscription = None;
 47            }
 48        }
 49
 50        editor.update(cx, |editor, cx| vim.unhook_vim_settings(editor, cx))
 51    });
 52}
 53
 54fn released(entity_id: EntityId, cx: &mut AppContext) {
 55    cx.update_global(|vim: &mut Vim, _| {
 56        if vim
 57            .active_editor
 58            .as_ref()
 59            .is_some_and(|previous| previous.entity_id() == entity_id)
 60        {
 61            vim.active_editor = None;
 62            vim.editor_subscription = None;
 63        }
 64        vim.editor_states.remove(&entity_id)
 65    });
 66}
 67
 68#[cfg(test)]
 69mod test {
 70    use crate::{test::VimTestContext, Vim};
 71    use editor::Editor;
 72    use gpui::{Context, Entity, VisualTestContext};
 73    use language::Buffer;
 74
 75    // regression test for blur called with a different active editor
 76    #[gpui::test]
 77    async fn test_blur_focus(cx: &mut gpui::TestAppContext) {
 78        let mut cx = VimTestContext::new(cx, true).await;
 79
 80        let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
 81        let window2 = cx.add_window(|cx| Editor::for_buffer(buffer, None, cx));
 82        let editor2 = cx
 83            .update(|cx| {
 84                window2.update(cx, |_, cx| {
 85                    cx.activate_window();
 86                    cx.focus_self();
 87                    cx.view().clone()
 88                })
 89            })
 90            .unwrap();
 91        cx.run_until_parked();
 92
 93        cx.update(|cx| {
 94            let vim = Vim::read(cx);
 95            assert_eq!(
 96                vim.active_editor.as_ref().unwrap().entity_id(),
 97                editor2.entity_id(),
 98            )
 99        });
100
101        // no panic when blurring an editor in a different window.
102        cx.update_editor(|editor1, cx| {
103            editor1.handle_blur(cx);
104        });
105    }
106
107    // regression test for focus_in/focus_out being called on window activation
108    #[gpui::test]
109    async fn test_focus_across_windows(cx: &mut gpui::TestAppContext) {
110        let mut cx = VimTestContext::new(cx, true).await;
111
112        let mut cx1 = VisualTestContext::from_window(cx.window, &cx);
113        let editor1 = cx.editor.clone();
114
115        let buffer = cx.new_model(|_| Buffer::new(0, 0, "a = 1\nb = 2\n"));
116        let (editor2, cx2) = cx.add_window_view(|cx| Editor::for_buffer(buffer, None, cx));
117
118        editor2.update(cx2, |_, cx| {
119            cx.focus_self();
120            cx.activate_window();
121        });
122        cx.run_until_parked();
123
124        cx1.update(|cx| {
125            assert_eq!(
126                Vim::read(cx).active_editor.as_ref().unwrap().entity_id(),
127                editor2.entity_id(),
128            )
129        });
130
131        cx1.update(|cx| {
132            cx.activate_window();
133        });
134        cx.run_until_parked();
135
136        cx.update(|cx| {
137            assert_eq!(
138                Vim::read(cx).active_editor.as_ref().unwrap().entity_id(),
139                editor1.entity_id(),
140            )
141        });
142    }
143}