mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 03:25:59 +00:00
Merge pull request #686 from zed-industries/disable-vim-on-start
Fully disable vim mode on start unless it's enabled
This commit is contained in:
commit
3ae5fc74c9
6 changed files with 117 additions and 88 deletions
|
@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue