Detailed changes
@@ -782,7 +782,7 @@ type GlobalActionCallback = dyn FnMut(&dyn AnyAction, &mut MutableAppContext);
type SubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext) -> bool>;
type GlobalSubscriptionCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ObservationCallback = Box<dyn FnMut(&mut MutableAppContext) -> bool>;
-type GlobalObservationCallback = Box<dyn FnMut(&mut MutableAppContext)>;
+type GlobalObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
type ReleaseObservationCallback = Box<dyn FnMut(&dyn Any, &mut MutableAppContext)>;
pub struct MutableAppContext {
@@ -1222,10 +1222,10 @@ impl MutableAppContext {
}
}
- pub fn observe_global<G, F>(&mut self, observe: F) -> Subscription
+ pub fn observe_global<G, F>(&mut self, mut observe: F) -> Subscription
where
G: Any,
- F: 'static + FnMut(&mut MutableAppContext),
+ F: 'static + FnMut(&G, &mut MutableAppContext),
{
let type_id = TypeId::of::<G>();
let id = post_inc(&mut self.next_subscription_id);
@@ -1234,7 +1234,14 @@ impl MutableAppContext {
.lock()
.entry(type_id)
.or_default()
- .insert(id, Some(Box::new(observe)));
+ .insert(
+ id,
+ Some(
+ Box::new(move |global: &dyn Any, cx: &mut MutableAppContext| {
+ observe(global.downcast_ref().unwrap(), cx)
+ }) as GlobalObservationCallback,
+ ),
+ );
Subscription::GlobalObservation {
id,
@@ -2075,10 +2082,10 @@ impl MutableAppContext {
fn notify_global_observers(&mut self, observed_type_id: TypeId) {
let callbacks = self.global_observations.lock().remove(&observed_type_id);
if let Some(callbacks) = callbacks {
- if self.cx.globals.contains_key(&observed_type_id) {
+ if let Some(global) = self.cx.globals.remove(&observed_type_id) {
for (id, callback) in callbacks {
if let Some(mut callback) = callback {
- callback(self);
+ callback(global.as_ref(), self);
match self
.global_observations
.lock()
@@ -2095,6 +2102,7 @@ impl MutableAppContext {
}
}
}
+ self.cx.globals.insert(observed_type_id, global);
}
}
}
@@ -5232,7 +5240,7 @@ mod tests {
let observation_count = Rc::new(RefCell::new(0));
let subscription = cx.observe_global::<Global, _>({
let observation_count = observation_count.clone();
- move |_| {
+ move |_, _| {
*observation_count.borrow_mut() += 1;
}
});
@@ -5262,7 +5270,7 @@ mod tests {
let observation_count = Rc::new(RefCell::new(0));
cx.observe_global::<OtherGlobal, _>({
let observation_count = observation_count.clone();
- move |_| {
+ move |_, _| {
*observation_count.borrow_mut() += 1;
}
})
@@ -5636,7 +5644,7 @@ mod tests {
*subscription.borrow_mut() = Some(cx.observe_global::<(), _>({
let observation_count = observation_count.clone();
let subscription = subscription.clone();
- move |_| {
+ move |_, _| {
subscription.borrow_mut().take();
*observation_count.borrow_mut() += 1;
}
@@ -13,7 +13,7 @@ pub fn init(cx: &mut MutableAppContext) {
fn editor_created(EditorCreated(editor): &EditorCreated, cx: &mut MutableAppContext) {
cx.update_default_global(|vim_state: &mut VimState, cx| {
vim_state.editors.insert(editor.id(), editor.downgrade());
- VimState::sync_editor_options(cx);
+ vim_state.sync_editor_options(cx);
})
}
@@ -24,21 +24,21 @@ fn editor_focused(EditorFocused(editor): &EditorFocused, cx: &mut MutableAppCont
Mode::Normal
};
- cx.update_default_global(|vim_state: &mut VimState, _| {
- vim_state.active_editor = Some(editor.downgrade());
+ VimState::update_global(cx, |state, cx| {
+ state.active_editor = Some(editor.downgrade());
+ state.switch_mode(&SwitchMode(mode), cx);
});
- VimState::switch_mode(&SwitchMode(mode), cx);
}
fn editor_blurred(EditorBlurred(editor): &EditorBlurred, cx: &mut MutableAppContext) {
- cx.update_default_global(|vim_state: &mut VimState, _| {
- if let Some(previous_editor) = vim_state.active_editor.clone() {
+ VimState::update_global(cx, |state, cx| {
+ if let Some(previous_editor) = state.active_editor.clone() {
if previous_editor == editor.clone() {
- vim_state.active_editor = None;
+ state.active_editor = None;
}
}
- });
- VimState::sync_editor_options(cx);
+ state.sync_editor_options(cx);
+ })
}
fn editor_released(EditorReleased(editor): &EditorReleased, cx: &mut MutableAppContext) {
@@ -18,11 +18,13 @@ pub fn init(cx: &mut MutableAppContext) {
}
fn normal_before(_: &mut Workspace, _: &NormalBefore, cx: &mut ViewContext<Workspace>) {
- VimState::update_active_editor(cx, |editor, cx| {
- editor.move_cursors(cx, |map, mut cursor, _| {
- *cursor.column_mut() = cursor.column().saturating_sub(1);
- (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
+ VimState::update_global(cx, |state, cx| {
+ state.update_active_editor(cx, |editor, cx| {
+ editor.move_cursors(cx, |map, mut cursor, _| {
+ *cursor.column_mut() = cursor.column().saturating_sub(1);
+ (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
+ });
});
- });
- VimState::switch_mode(&SwitchMode(Mode::Normal), cx);
+ state.switch_mode(&SwitchMode(Mode::Normal), cx);
+ })
}
@@ -28,31 +28,39 @@ pub fn init(cx: &mut MutableAppContext) {
}
fn move_left(_: &mut Workspace, _: &MoveLeft, cx: &mut ViewContext<Workspace>) {
- VimState::update_active_editor(cx, |editor, cx| {
- editor.move_cursors(cx, |map, mut cursor, _| {
- *cursor.column_mut() = cursor.column().saturating_sub(1);
- (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
+ VimState::update_global(cx, |state, cx| {
+ state.update_active_editor(cx, |editor, cx| {
+ editor.move_cursors(cx, |map, mut cursor, _| {
+ *cursor.column_mut() = cursor.column().saturating_sub(1);
+ (map.clip_point(cursor, Bias::Left), SelectionGoal::None)
+ });
});
- });
+ })
}
fn move_down(_: &mut Workspace, _: &MoveDown, cx: &mut ViewContext<Workspace>) {
- VimState::update_active_editor(cx, |editor, cx| {
- editor.move_cursors(cx, movement::down);
+ VimState::update_global(cx, |state, cx| {
+ state.update_active_editor(cx, |editor, cx| {
+ editor.move_cursors(cx, movement::down);
+ });
});
}
fn move_up(_: &mut Workspace, _: &MoveUp, cx: &mut ViewContext<Workspace>) {
- VimState::update_active_editor(cx, |editor, cx| {
- editor.move_cursors(cx, movement::up);
+ VimState::update_global(cx, |state, cx| {
+ state.update_active_editor(cx, |editor, cx| {
+ editor.move_cursors(cx, movement::up);
+ });
});
}
fn move_right(_: &mut Workspace, _: &MoveRight, cx: &mut ViewContext<Workspace>) {
- VimState::update_active_editor(cx, |editor, cx| {
- editor.move_cursors(cx, |map, mut cursor, _| {
- *cursor.column_mut() += 1;
- (map.clip_point(cursor, Bias::Right), SelectionGoal::None)
+ VimState::update_global(cx, |state, cx| {
+ state.update_active_editor(cx, |editor, cx| {
+ editor.move_cursors(cx, |map, mut cursor, _| {
+ *cursor.column_mut() += 1;
+ (map.clip_point(cursor, Bias::Right), SelectionGoal::None)
+ });
});
});
}
@@ -19,10 +19,14 @@ pub fn init(cx: &mut MutableAppContext) {
insert::init(cx);
normal::init(cx);
- cx.add_action(|_: &mut Workspace, action: &SwitchMode, cx| VimState::switch_mode(action, cx));
+ cx.add_action(|_: &mut Workspace, action: &SwitchMode, cx| {
+ VimState::update_global(cx, |state, cx| state.switch_mode(action, cx))
+ });
- cx.observe_global::<Settings, _>(VimState::settings_changed)
- .detach();
+ cx.observe_global::<Settings, _>(|settings, cx| {
+ VimState::update_global(cx, |state, cx| state.set_enabled(settings.vim_mode, cx))
+ })
+ .detach();
}
#[derive(Default)]
@@ -35,62 +39,59 @@ pub struct VimState {
}
impl VimState {
+ fn update_global<F, S>(cx: &mut MutableAppContext, update: F) -> S
+ where
+ F: FnOnce(&mut Self, &mut MutableAppContext) -> S,
+ {
+ cx.update_default_global(update)
+ }
+
fn update_active_editor<S>(
+ &self,
cx: &mut MutableAppContext,
update: impl FnOnce(&mut Editor, &mut ViewContext<Editor>) -> S,
) -> Option<S> {
- cx.global::<Self>()
- .active_editor
+ self.active_editor
.clone()
.and_then(|ae| ae.upgrade(cx))
.map(|ae| ae.update(cx, update))
}
- fn switch_mode(SwitchMode(mode): &SwitchMode, cx: &mut MutableAppContext) {
- cx.update_default_global(|this: &mut Self, _| {
- this.mode = *mode;
- });
-
- VimState::sync_editor_options(cx);
+ fn switch_mode(&mut self, SwitchMode(mode): &SwitchMode, cx: &mut MutableAppContext) {
+ self.mode = *mode;
+ self.sync_editor_options(cx);
}
- fn settings_changed(cx: &mut MutableAppContext) {
- cx.update_default_global(|this: &mut Self, cx| {
- let settings = cx.global::<Settings>();
- if this.enabled != settings.vim_mode {
- this.enabled = settings.vim_mode;
- this.mode = if settings.vim_mode {
- Mode::Normal
- } else {
- Mode::Insert
- };
- Self::sync_editor_options(cx);
+ fn set_enabled(&mut self, enabled: bool, cx: &mut MutableAppContext) {
+ if self.enabled != enabled {
+ self.enabled = enabled;
+ if enabled {
+ self.mode = Mode::Normal;
}
- });
+ self.sync_editor_options(cx);
+ }
}
- fn sync_editor_options(cx: &mut MutableAppContext) {
- cx.defer(move |cx| {
- cx.update_default_global(|this: &mut VimState, cx| {
- let mode = this.mode;
- let cursor_shape = mode.cursor_shape();
- let keymap_layer_active = this.enabled;
- for editor in this.editors.values() {
- if let Some(editor) = editor.upgrade(cx) {
- editor.update(cx, |editor, cx| {
- editor.set_cursor_shape(cursor_shape, cx);
- editor.set_clip_at_line_ends(cursor_shape == CursorShape::Block, cx);
- editor.set_input_enabled(mode == Mode::Insert);
- if keymap_layer_active {
- let context_layer = mode.keymap_context_layer();
- editor.set_keymap_context_layer::<Self>(context_layer);
- } else {
- editor.remove_keymap_context_layer::<Self>();
- }
- });
+ fn sync_editor_options(&self, cx: &mut MutableAppContext) {
+ let mode = self.mode;
+ let cursor_shape = mode.cursor_shape();
+ for editor in self.editors.values() {
+ if let Some(editor) = editor.upgrade(cx) {
+ editor.update(cx, |editor, cx| {
+ if self.enabled {
+ editor.set_cursor_shape(cursor_shape, cx);
+ editor.set_clip_at_line_ends(cursor_shape == CursorShape::Block, cx);
+ editor.set_input_enabled(mode == Mode::Insert);
+ let context_layer = mode.keymap_context_layer();
+ editor.set_keymap_context_layer::<Self>(context_layer);
+ } else {
+ editor.set_cursor_shape(CursorShape::Bar, cx);
+ editor.set_clip_at_line_ends(false, cx);
+ editor.set_input_enabled(true);
+ editor.remove_keymap_context_layer::<Self>();
}
- }
- });
- });
+ });
+ }
+ }
}
}
@@ -11,7 +11,7 @@ use crate::*;
#[gpui::test]
async fn test_insert_mode(cx: &mut gpui::TestAppContext) {
- let mut cx = VimTestAppContext::new(cx, "").await;
+ let mut cx = VimTestAppContext::new(cx, true, "").await;
cx.simulate_keystroke("i");
assert_eq!(cx.mode(), Mode::Insert);
cx.simulate_keystrokes(&["T", "e", "s", "t"]);
@@ -23,7 +23,7 @@ async fn test_insert_mode(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_normal_hjkl(cx: &mut gpui::TestAppContext) {
- let mut cx = VimTestAppContext::new(cx, "Test\nTestTest\nTest").await;
+ let mut cx = VimTestAppContext::new(cx, true, "Test\nTestTest\nTest").await;
cx.simulate_keystroke("l");
cx.assert_newest_selection_head(indoc! {"
T|est
@@ -81,15 +81,17 @@ async fn test_normal_hjkl(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
- let mut cx = VimTestAppContext::new(cx, "").await;
+ let mut cx = VimTestAppContext::new(cx, true, "").await;
+
+ cx.simulate_keystroke("i");
+ assert_eq!(cx.mode(), Mode::Insert);
// Editor acts as though vim is disabled
cx.disable_vim();
- assert_eq!(cx.mode(), Mode::Insert);
cx.simulate_keystrokes(&["h", "j", "k", "l"]);
cx.assert_newest_selection_head("hjkl|");
- // Enabling dynamically sets vim mode again
+ // Enabling dynamically sets vim mode again and restores normal mode
cx.enable_vim();
assert_eq!(cx.mode(), Mode::Normal);
cx.simulate_keystrokes(&["h", "h", "h", "l"]);
@@ -106,6 +108,13 @@ async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
assert_eq!(cx.mode(), Mode::Normal);
}
+#[gpui::test]
+async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestAppContext::new(cx, false, "").await;
+ cx.simulate_keystrokes(&["h", "j", "k", "l"]);
+ cx.assert_newest_selection_head("hjkl|");
+}
+
struct VimTestAppContext<'a> {
cx: &'a mut gpui::TestAppContext,
window_id: usize,
@@ -115,6 +124,7 @@ struct VimTestAppContext<'a> {
impl<'a> VimTestAppContext<'a> {
async fn new(
cx: &'a mut gpui::TestAppContext,
+ enabled: bool,
initial_editor_text: &str,
) -> VimTestAppContext<'a> {
cx.update(|cx| {
@@ -125,7 +135,7 @@ impl<'a> VimTestAppContext<'a> {
cx.update(|cx| {
cx.update_global(|settings: &mut Settings, _| {
- settings.vim_mode = true;
+ settings.vim_mode = enabled;
});
});