Detailed changes
@@ -170,6 +170,10 @@ impl<'a> EditorTestContext<'a> {
keystrokes_under_test_handle
}
+ pub fn run_until_parked(&mut self) {
+ self.cx.background_executor.run_until_parked();
+ }
+
pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text);
@@ -18,10 +18,10 @@ use crate::{
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
- ForegroundExecutor, KeyBinding, Keymap, LayoutId, Menu, PathPromptOptions, Pixels, Platform,
- PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task,
- TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext,
- WindowHandle, WindowId,
+ ForegroundExecutor, KeyBinding, Keymap, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels,
+ Platform, PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription,
+ SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window,
+ WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@@ -170,6 +170,7 @@ impl App {
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
+type KeystrokeObserver = Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) + 'static>;
type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
@@ -211,6 +212,7 @@ pub struct AppContext {
pub(crate) observers: SubscriberSet<EntityId, Handler>,
// TypeId is the type of the event that the listener callback expects
pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
+ pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
@@ -271,6 +273,7 @@ impl AppContext {
observers: SubscriberSet::new(),
event_listeners: SubscriberSet::new(),
release_listeners: SubscriberSet::new(),
+ keystroke_observers: SubscriberSet::new(),
global_observers: SubscriberSet::new(),
quit_observers: SubscriberSet::new(),
layout_id_buffer: Default::default(),
@@ -962,6 +965,15 @@ impl AppContext {
subscription
}
+ pub fn observe_keystrokes(
+ &mut self,
+ f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
+ ) -> Subscription {
+ let (subscription, activate) = self.keystroke_observers.insert((), Box::new(f));
+ activate();
+ subscription
+ }
+
pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
self.text_style_stack.push(text_style);
}
@@ -1288,3 +1300,9 @@ pub(crate) struct AnyTooltip {
pub view: AnyView,
pub cursor_offset: Point<Pixels>,
}
+
+#[derive(Debug)]
+pub struct KeystrokeEvent {
+ pub keystroke: Keystroke,
+ pub action: Option<Box<dyn Action>>,
+}
@@ -208,7 +208,7 @@ impl DispatchTree {
&mut self,
keystroke: &Keystroke,
context: &[KeyContext],
- ) -> Option<Box<dyn Action>> {
+ ) -> Vec<Box<dyn Action>> {
if !self.keystroke_matchers.contains_key(context) {
let keystroke_contexts = context.iter().cloned().collect();
self.keystroke_matchers.insert(
@@ -218,15 +218,15 @@ impl DispatchTree {
}
let keystroke_matcher = self.keystroke_matchers.get_mut(context).unwrap();
- if let KeyMatch::Some(action) = keystroke_matcher.match_keystroke(keystroke, context) {
+ if let KeyMatch::Some(actions) = keystroke_matcher.match_keystroke(keystroke, context) {
// Clear all pending keystrokes when an action has been found.
for keystroke_matcher in self.keystroke_matchers.values_mut() {
keystroke_matcher.clear_pending();
}
- Some(action)
+ actions
} else {
- None
+ vec![]
}
}
@@ -59,7 +59,7 @@ impl KeyBinding {
{
// If the binding is completed, push it onto the matches list
if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
- KeyMatch::Some(self.action.boxed_clone())
+ KeyMatch::Some(vec![self.action.boxed_clone()])
} else {
KeyMatch::Pending
}
@@ -54,14 +54,14 @@ impl KeystrokeMatcher {
}
let mut pending_key = None;
+ let mut found_actions = Vec::new();
for binding in keymap.bindings().iter().rev() {
for candidate in keystroke.match_candidates() {
self.pending_keystrokes.push(candidate.clone());
match binding.match_keystrokes(&self.pending_keystrokes, context_stack) {
- KeyMatch::Some(action) => {
- self.pending_keystrokes.clear();
- return KeyMatch::Some(action);
+ KeyMatch::Some(mut actions) => {
+ found_actions.append(&mut actions);
}
KeyMatch::Pending => {
pending_key.get_or_insert(candidate);
@@ -72,6 +72,11 @@ impl KeystrokeMatcher {
}
}
+ if !found_actions.is_empty() {
+ self.pending_keystrokes.clear();
+ return KeyMatch::Some(found_actions);
+ }
+
if let Some(pending_key) = pending_key {
self.pending_keystrokes.push(pending_key);
}
@@ -101,7 +106,7 @@ impl KeystrokeMatcher {
pub enum KeyMatch {
None,
Pending,
- Some(Box<dyn Action>),
+ Some(Vec<Box<dyn Action>>),
}
impl KeyMatch {
@@ -130,7 +130,7 @@ impl Platform for TestPlatform {
}
fn active_window(&self) -> Option<crate::AnyWindowHandle> {
- unimplemented!()
+ self.active_window.lock().clone()
}
fn open_window(
@@ -3,9 +3,9 @@ use crate::{
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
- ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, LayoutId, Model,
- ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path,
- Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
+ ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
+ Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
+ Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
RenderSvgParams, ScaledPixels, Scene, SceneBuilder, Shadow, SharedString, Size, Style,
SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View,
@@ -495,6 +495,29 @@ impl<'a> WindowContext<'a> {
})
}
+ pub(crate) fn dispatch_keystroke_observers(
+ &mut self,
+ event: &dyn Any,
+ action: Option<Box<dyn Action>>,
+ ) {
+ let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() else {
+ return;
+ };
+
+ self.keystroke_observers
+ .clone()
+ .retain(&(), move |callback| {
+ (callback)(
+ &KeystrokeEvent {
+ keystroke: key_down_event.keystroke.clone(),
+ action: action.as_ref().map(|action| action.boxed_clone()),
+ },
+ self,
+ );
+ true
+ });
+ }
+
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
/// that are currently on the stack to be returned to the app.
pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
@@ -1423,14 +1446,12 @@ impl<'a> WindowContext<'a> {
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
if node.context.is_some() {
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
- if let Some(found) = self
+ let mut new_actions = self
.window
.rendered_frame
.dispatch_tree
- .dispatch_key(&key_down_event.keystroke, &context_stack)
- {
- actions.push(found.boxed_clone())
- }
+ .dispatch_key(&key_down_event.keystroke, &context_stack);
+ actions.append(&mut new_actions);
}
context_stack.pop();
@@ -1438,11 +1459,13 @@ impl<'a> WindowContext<'a> {
}
for action in actions {
- self.dispatch_action_on_node(node_id, action);
+ self.dispatch_action_on_node(node_id, action.boxed_clone());
if !self.propagate_event {
+ self.dispatch_keystroke_observers(event, Some(action));
return;
}
}
+ self.dispatch_keystroke_observers(event, None);
}
fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box<dyn Action>) {
@@ -17,6 +17,7 @@ pub mod project_search;
pub(crate) mod search_bar;
pub fn init(cx: &mut AppContext) {
+ menu::init();
buffer_search::init(cx);
project_search::init(cx);
}
@@ -1,42 +1,40 @@
-use gpui::{div, AnyElement, Element, IntoElement, Render, ViewContext};
-use settings::{Settings, SettingsStore};
+use gpui::{div, AnyElement, Element, IntoElement, Render, Subscription, ViewContext};
+use settings::SettingsStore;
use workspace::{item::ItemHandle, ui::Label, StatusItemView};
-use crate::{state::Mode, Vim, VimModeSetting};
+use crate::{state::Mode, Vim};
pub struct ModeIndicator {
pub mode: Option<Mode>,
- // _subscription: Subscription,
+ _subscriptions: Vec<Subscription>,
}
impl ModeIndicator {
pub fn new(cx: &mut ViewContext<Self>) -> Self {
- cx.observe_global::<Vim>(|this, cx| this.set_mode(Vim::read(cx).state().mode, cx))
- .detach();
+ let _subscriptions = vec![
+ cx.observe_global::<Vim>(|this, cx| this.update_mode(cx)),
+ cx.observe_global::<SettingsStore>(|this, cx| this.update_mode(cx)),
+ ];
- cx.observe_global::<SettingsStore>(move |mode_indicator, cx| {
- if VimModeSetting::get_global(cx).0 {
- mode_indicator.mode = cx
- .has_global::<Vim>()
- .then(|| cx.global::<Vim>().state().mode);
- } else {
- mode_indicator.mode.take();
- }
- })
- .detach();
+ let mut this = Self {
+ mode: None,
+ _subscriptions,
+ };
+ this.update_mode(cx);
+ this
+ }
+ fn update_mode(&mut self, cx: &mut ViewContext<Self>) {
// Vim doesn't exist in some tests
- let mode = cx
- .has_global::<Vim>()
- .then(|| {
- let vim = cx.global::<Vim>();
- vim.enabled.then(|| vim.state().mode)
- })
- .flatten();
+ if !cx.has_global::<Vim>() {
+ return;
+ }
- Self {
- mode,
- // _subscription,
+ let vim = Vim::read(cx);
+ if vim.enabled {
+ self.mode = Some(vim.state().mode);
+ } else {
+ self.mode = None;
}
}
@@ -203,7 +203,6 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
}
fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) {
- dbg!("insert before!");
Vim::update(cx, |vim, cx| {
vim.start_recording(cx);
vim.switch_mode(Mode::Insert, false, cx);
@@ -372,536 +371,540 @@ pub(crate) fn normal_replace(text: Arc<str>, cx: &mut WindowContext) {
});
}
-// #[cfg(test)]
-// mod test {
-// use gpui::TestAppContext;
-// use indoc::indoc;
-
-// use crate::{
-// state::Mode::{self},
-// test::NeovimBackedTestContext,
-// };
-
-// #[gpui::test]
-// async fn test_h(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
-// cx.assert_all(indoc! {"
-// ˇThe qˇuick
-// ˇbrown"
-// })
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_backspace(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx)
-// .await
-// .binding(["backspace"]);
-// cx.assert_all(indoc! {"
-// ˇThe qˇuick
-// ˇbrown"
-// })
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_j(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// aaˇaa
-// 😃😃"
-// })
-// .await;
-// cx.simulate_shared_keystrokes(["j"]).await;
-// cx.assert_shared_state(indoc! {"
-// aaaa
-// 😃ˇ😃"
-// })
-// .await;
-
-// for marked_position in cx.each_marked_position(indoc! {"
-// ˇThe qˇuick broˇwn
-// ˇfox jumps"
-// }) {
-// cx.assert_neovim_compatible(&marked_position, ["j"]).await;
-// }
-// }
-
-// #[gpui::test]
-// async fn test_enter(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["enter"]);
-// cx.assert_all(indoc! {"
-// ˇThe qˇuick broˇwn
-// ˇfox jumps"
-// })
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_k(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);
-// cx.assert_all(indoc! {"
-// ˇThe qˇuick
-// ˇbrown fˇox jumˇps"
-// })
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_l(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["l"]);
-// cx.assert_all(indoc! {"
-// ˇThe qˇuicˇk
-// ˇbrowˇn"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.assert_binding_matches_all(
-// ["$"],
-// indoc! {"
-// ˇThe qˇuicˇk
-// ˇbrowˇn"},
-// )
-// .await;
-// cx.assert_binding_matches_all(
-// ["0"],
-// indoc! {"
-// ˇThe qˇuicˇk
-// ˇbrowˇn"},
-// )
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-g"]);
-
-// cx.assert_all(indoc! {"
-// The ˇquick
-
-// brown fox jumps
-// overˇ the lazy doˇg"})
-// .await;
-// cx.assert(indoc! {"
-// The quiˇck
-
-// brown"})
-// .await;
-// cx.assert(indoc! {"
-// The quiˇck
-
-// "})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_w(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["w"]);
-// cx.assert_all(indoc! {"
-// The ˇquickˇ-ˇbrown
-// ˇ
-// ˇ
-// ˇfox_jumps ˇover
-// ˇthˇe"})
-// .await;
-// let mut cx = cx.binding(["shift-w"]);
-// cx.assert_all(indoc! {"
-// The ˇquickˇ-ˇbrown
-// ˇ
-// ˇ
-// ˇfox_jumps ˇover
-// ˇthˇe"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_end_of_word(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]);
-// cx.assert_all(indoc! {"
-// Thˇe quicˇkˇ-browˇn
-
-// fox_jumpˇs oveˇr
-// thˇe"})
-// .await;
-// let mut cx = cx.binding(["shift-e"]);
-// cx.assert_all(indoc! {"
-// Thˇe quicˇkˇ-browˇn
-
-// fox_jumpˇs oveˇr
-// thˇe"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_b(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["b"]);
-// cx.assert_all(indoc! {"
-// ˇThe ˇquickˇ-ˇbrown
-// ˇ
-// ˇ
-// ˇfox_jumps ˇover
-// ˇthe"})
-// .await;
-// let mut cx = cx.binding(["shift-b"]);
-// cx.assert_all(indoc! {"
-// ˇThe ˇquickˇ-ˇbrown
-// ˇ
-// ˇ
-// ˇfox_jumps ˇover
-// ˇthe"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_gg(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.assert_binding_matches_all(
-// ["g", "g"],
-// indoc! {"
-// The qˇuick
-
-// brown fox jumps
-// over ˇthe laˇzy dog"},
-// )
-// .await;
-// cx.assert_binding_matches(
-// ["g", "g"],
-// indoc! {"
-
-// brown fox jumps
-// over the laˇzy dog"},
-// )
-// .await;
-// cx.assert_binding_matches(
-// ["2", "g", "g"],
-// indoc! {"
-// ˇ
-
-// brown fox jumps
-// over the lazydog"},
-// )
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_end_of_document(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.assert_binding_matches_all(
-// ["shift-g"],
-// indoc! {"
-// The qˇuick
-
-// brown fox jumps
-// over ˇthe laˇzy dog"},
-// )
-// .await;
-// cx.assert_binding_matches(
-// ["shift-g"],
-// indoc! {"
-
-// brown fox jumps
-// over the laˇzy dog"},
-// )
-// .await;
-// cx.assert_binding_matches(
-// ["2", "shift-g"],
-// indoc! {"
-// ˇ
-
-// brown fox jumps
-// over the lazydog"},
-// )
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_a(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["a"]);
-// cx.assert_all("The qˇuicˇk").await;
-// }
-
-// #[gpui::test]
-// async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-a"]);
-// cx.assert_all(indoc! {"
-// ˇ
-// The qˇuick
-// brown ˇfox "})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["^"]);
-// cx.assert("The qˇuick").await;
-// cx.assert(" The qˇuick").await;
-// cx.assert("ˇ").await;
-// cx.assert(indoc! {"
-// The qˇuick
-// brown fox"})
-// .await;
-// cx.assert(indoc! {"
-// ˇ
-// The quick"})
-// .await;
-// // Indoc disallows trailing whitespace.
-// cx.assert(" ˇ \nThe quick").await;
-// }
-
-// #[gpui::test]
-// async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-i"]);
-// cx.assert("The qˇuick").await;
-// cx.assert(" The qˇuick").await;
-// cx.assert("ˇ").await;
-// cx.assert(indoc! {"
-// The qˇuick
-// brown fox"})
-// .await;
-// cx.assert(indoc! {"
-// ˇ
-// The quick"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-d"]);
-// cx.assert(indoc! {"
-// The qˇuick
-// brown fox"})
-// .await;
-// cx.assert(indoc! {"
-// The quick
-// ˇ
-// brown fox"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_x(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["x"]);
-// cx.assert_all("ˇTeˇsˇt").await;
-// cx.assert(indoc! {"
-// Tesˇt
-// test"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_delete_left(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-x"]);
-// cx.assert_all("ˇTˇeˇsˇt").await;
-// cx.assert(indoc! {"
-// Test
-// ˇtest"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_o(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["o"]);
-// cx.assert("ˇ").await;
-// cx.assert("The ˇquick").await;
-// cx.assert_all(indoc! {"
-// The qˇuick
-// brown ˇfox
-// jumps ˇover"})
-// .await;
-// cx.assert(indoc! {"
-// The quick
-// ˇ
-// brown fox"})
-// .await;
-
-// cx.assert_manual(
-// indoc! {"
-// fn test() {
-// println!(ˇ);
-// }"},
-// Mode::Normal,
-// indoc! {"
-// fn test() {
-// println!();
-// ˇ
-// }"},
-// Mode::Insert,
-// );
-
-// cx.assert_manual(
-// indoc! {"
-// fn test(ˇ) {
-// println!();
-// }"},
-// Mode::Normal,
-// indoc! {"
-// fn test() {
-// ˇ
-// println!();
-// }"},
-// Mode::Insert,
-// );
-// }
-
-// #[gpui::test]
-// async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
-// let cx = NeovimBackedTestContext::new(cx).await;
-// let mut cx = cx.binding(["shift-o"]);
-// cx.assert("ˇ").await;
-// cx.assert("The ˇquick").await;
-// cx.assert_all(indoc! {"
-// The qˇuick
-// brown ˇfox
-// jumps ˇover"})
-// .await;
-// cx.assert(indoc! {"
-// The quick
-// ˇ
-// brown fox"})
-// .await;
-
-// // Our indentation is smarter than vims. So we don't match here
-// cx.assert_manual(
-// indoc! {"
-// fn test() {
-// println!(ˇ);
-// }"},
-// Mode::Normal,
-// indoc! {"
-// fn test() {
-// ˇ
-// println!();
-// }"},
-// Mode::Insert,
-// );
-// cx.assert_manual(
-// indoc! {"
-// fn test(ˇ) {
-// println!();
-// }"},
-// Mode::Normal,
-// indoc! {"
-// ˇ
-// fn test() {
-// println!();
-// }"},
-// Mode::Insert,
-// );
-// }
-
-// #[gpui::test]
-// async fn test_dd(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.assert_neovim_compatible("ˇ", ["d", "d"]).await;
-// cx.assert_neovim_compatible("The ˇquick", ["d", "d"]).await;
-// for marked_text in cx.each_marked_position(indoc! {"
-// The qˇuick
-// brown ˇfox
-// jumps ˇover"})
-// {
-// cx.assert_neovim_compatible(&marked_text, ["d", "d"]).await;
-// }
-// cx.assert_neovim_compatible(
-// indoc! {"
-// The quick
-// ˇ
-// brown fox"},
-// ["d", "d"],
-// )
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_cc(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "c"]);
-// cx.assert("ˇ").await;
-// cx.assert("The ˇquick").await;
-// cx.assert_all(indoc! {"
-// The quˇick
-// brown ˇfox
-// jumps ˇover"})
-// .await;
-// cx.assert(indoc! {"
-// The quick
-// ˇ
-// brown fox"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_repeated_word(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// for count in 1..=5 {
-// cx.assert_binding_matches_all(
-// [&count.to_string(), "w"],
-// indoc! {"
-// ˇThe quˇickˇ browˇn
-// ˇ
-// ˇfox ˇjumpsˇ-ˇoˇver
-// ˇthe lazy dog
-// "},
-// )
-// .await;
-// }
-// }
-
-// #[gpui::test]
-// async fn test_h_through_unicode(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
-// cx.assert_all("Testˇ├ˇ──ˇ┐ˇTest").await;
-// }
-
-// #[gpui::test]
-// async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// for count in 1..=3 {
-// let test_case = indoc! {"
-// ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
-// ˇ ˇbˇaaˇa ˇbˇbˇb
-// ˇ
-// ˇb
-// "};
-
-// cx.assert_binding_matches_all([&count.to_string(), "f", "b"], test_case)
-// .await;
-
-// cx.assert_binding_matches_all([&count.to_string(), "t", "b"], test_case)
-// .await;
-// }
-// }
-
-// #[gpui::test]
-// async fn test_capital_f_and_capital_t(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// let test_case = indoc! {"
-// ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
-// ˇ ˇbˇaaˇa ˇbˇbˇb
-// ˇ•••
-// ˇb
-// "
-// };
-
-// for count in 1..=3 {
-// cx.assert_binding_matches_all([&count.to_string(), "shift-f", "b"], test_case)
-// .await;
-
-// cx.assert_binding_matches_all([&count.to_string(), "shift-t", "b"], test_case)
-// .await;
-// }
-// }
-
-// #[gpui::test]
-// async fn test_percent(cx: &mut TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await.binding(["%"]);
-// cx.assert_all("ˇconsole.logˇ(ˇvaˇrˇ)ˇ;").await;
-// cx.assert_all("ˇconsole.logˇ(ˇ'var', ˇ[ˇ1, ˇ2, 3ˇ]ˇ)ˇ;")
-// .await;
-// cx.assert_all("let result = curried_funˇ(ˇ)ˇ(ˇ)ˇ;").await;
-// }
-// }
+#[cfg(test)]
+mod test {
+ use gpui::TestAppContext;
+ use indoc::indoc;
+
+ use crate::{
+ state::Mode::{self},
+ test::NeovimBackedTestContext,
+ };
+
+ #[gpui::test]
+ async fn test_h(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
+ cx.assert_all(indoc! {"
+ ˇThe qˇuick
+ ˇbrown"
+ })
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_backspace(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx)
+ .await
+ .binding(["backspace"]);
+ cx.assert_all(indoc! {"
+ ˇThe qˇuick
+ ˇbrown"
+ })
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_j(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ aaˇaa
+ 😃😃"
+ })
+ .await;
+ cx.simulate_shared_keystrokes(["j"]).await;
+ cx.assert_shared_state(indoc! {"
+ aaaa
+ 😃ˇ😃"
+ })
+ .await;
+
+ for marked_position in cx.each_marked_position(indoc! {"
+ ˇThe qˇuick broˇwn
+ ˇfox jumps"
+ }) {
+ cx.assert_neovim_compatible(&marked_position, ["j"]).await;
+ }
+ }
+
+ #[gpui::test]
+ async fn test_enter(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["enter"]);
+ cx.assert_all(indoc! {"
+ ˇThe qˇuick broˇwn
+ ˇfox jumps"
+ })
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_k(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["k"]);
+ cx.assert_all(indoc! {"
+ ˇThe qˇuick
+ ˇbrown fˇox jumˇps"
+ })
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_l(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["l"]);
+ cx.assert_all(indoc! {"
+ ˇThe qˇuicˇk
+ ˇbrowˇn"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_jump_to_line_boundaries(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.assert_binding_matches_all(
+ ["$"],
+ indoc! {"
+ ˇThe qˇuicˇk
+ ˇbrowˇn"},
+ )
+ .await;
+ cx.assert_binding_matches_all(
+ ["0"],
+ indoc! {"
+ ˇThe qˇuicˇk
+ ˇbrowˇn"},
+ )
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_jump_to_end(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-g"]);
+
+ cx.assert_all(indoc! {"
+ The ˇquick
+
+ brown fox jumps
+ overˇ the lazy doˇg"})
+ .await;
+ cx.assert(indoc! {"
+ The quiˇck
+
+ brown"})
+ .await;
+ cx.assert(indoc! {"
+ The quiˇck
+
+ "})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_w(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["w"]);
+ cx.assert_all(indoc! {"
+ The ˇquickˇ-ˇbrown
+ ˇ
+ ˇ
+ ˇfox_jumps ˇover
+ ˇthˇe"})
+ .await;
+ let mut cx = cx.binding(["shift-w"]);
+ cx.assert_all(indoc! {"
+ The ˇquickˇ-ˇbrown
+ ˇ
+ ˇ
+ ˇfox_jumps ˇover
+ ˇthˇe"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_end_of_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["e"]);
+ cx.assert_all(indoc! {"
+ Thˇe quicˇkˇ-browˇn
+
+
+ fox_jumpˇs oveˇr
+ thˇe"})
+ .await;
+ let mut cx = cx.binding(["shift-e"]);
+ cx.assert_all(indoc! {"
+ Thˇe quicˇkˇ-browˇn
+
+
+ fox_jumpˇs oveˇr
+ thˇe"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_b(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["b"]);
+ cx.assert_all(indoc! {"
+ ˇThe ˇquickˇ-ˇbrown
+ ˇ
+ ˇ
+ ˇfox_jumps ˇover
+ ˇthe"})
+ .await;
+ let mut cx = cx.binding(["shift-b"]);
+ cx.assert_all(indoc! {"
+ ˇThe ˇquickˇ-ˇbrown
+ ˇ
+ ˇ
+ ˇfox_jumps ˇover
+ ˇthe"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_gg(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.assert_binding_matches_all(
+ ["g", "g"],
+ indoc! {"
+ The qˇuick
+
+ brown fox jumps
+ over ˇthe laˇzy dog"},
+ )
+ .await;
+ cx.assert_binding_matches(
+ ["g", "g"],
+ indoc! {"
+
+
+ brown fox jumps
+ over the laˇzy dog"},
+ )
+ .await;
+ cx.assert_binding_matches(
+ ["2", "g", "g"],
+ indoc! {"
+ ˇ
+
+ brown fox jumps
+ over the lazydog"},
+ )
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_end_of_document(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.assert_binding_matches_all(
+ ["shift-g"],
+ indoc! {"
+ The qˇuick
+
+ brown fox jumps
+ over ˇthe laˇzy dog"},
+ )
+ .await;
+ cx.assert_binding_matches(
+ ["shift-g"],
+ indoc! {"
+
+
+ brown fox jumps
+ over the laˇzy dog"},
+ )
+ .await;
+ cx.assert_binding_matches(
+ ["2", "shift-g"],
+ indoc! {"
+ ˇ
+
+ brown fox jumps
+ over the lazydog"},
+ )
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_a(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["a"]);
+ cx.assert_all("The qˇuicˇk").await;
+ }
+
+ #[gpui::test]
+ async fn test_insert_end_of_line(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-a"]);
+ cx.assert_all(indoc! {"
+ ˇ
+ The qˇuick
+ brown ˇfox "})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_jump_to_first_non_whitespace(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["^"]);
+ cx.assert("The qˇuick").await;
+ cx.assert(" The qˇuick").await;
+ cx.assert("ˇ").await;
+ cx.assert(indoc! {"
+ The qˇuick
+ brown fox"})
+ .await;
+ cx.assert(indoc! {"
+ ˇ
+ The quick"})
+ .await;
+ // Indoc disallows trailing whitespace.
+ cx.assert(" ˇ \nThe quick").await;
+ }
+
+ #[gpui::test]
+ async fn test_insert_first_non_whitespace(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-i"]);
+ cx.assert("The qˇuick").await;
+ cx.assert(" The qˇuick").await;
+ cx.assert("ˇ").await;
+ cx.assert(indoc! {"
+ The qˇuick
+ brown fox"})
+ .await;
+ cx.assert(indoc! {"
+ ˇ
+ The quick"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_delete_to_end_of_line(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-d"]);
+ cx.assert(indoc! {"
+ The qˇuick
+ brown fox"})
+ .await;
+ cx.assert(indoc! {"
+ The quick
+ ˇ
+ brown fox"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_x(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["x"]);
+ cx.assert_all("ˇTeˇsˇt").await;
+ cx.assert(indoc! {"
+ Tesˇt
+ test"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_delete_left(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["shift-x"]);
+ cx.assert_all("ˇTˇeˇsˇt").await;
+ cx.assert(indoc! {"
+ Test
+ ˇtest"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_o(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["o"]);
+ cx.assert("ˇ").await;
+ cx.assert("The ˇquick").await;
+ cx.assert_all(indoc! {"
+ The qˇuick
+ brown ˇfox
+ jumps ˇover"})
+ .await;
+ cx.assert(indoc! {"
+ The quick
+ ˇ
+ brown fox"})
+ .await;
+
+ cx.assert_manual(
+ indoc! {"
+ fn test() {
+ println!(ˇ);
+ }"},
+ Mode::Normal,
+ indoc! {"
+ fn test() {
+ println!();
+ ˇ
+ }"},
+ Mode::Insert,
+ );
+
+ cx.assert_manual(
+ indoc! {"
+ fn test(ˇ) {
+ println!();
+ }"},
+ Mode::Normal,
+ indoc! {"
+ fn test() {
+ ˇ
+ println!();
+ }"},
+ Mode::Insert,
+ );
+ }
+
+ #[gpui::test]
+ async fn test_insert_line_above(cx: &mut gpui::TestAppContext) {
+ let cx = NeovimBackedTestContext::new(cx).await;
+ let mut cx = cx.binding(["shift-o"]);
+ cx.assert("ˇ").await;
+ cx.assert("The ˇquick").await;
+ cx.assert_all(indoc! {"
+ The qˇuick
+ brown ˇfox
+ jumps ˇover"})
+ .await;
+ cx.assert(indoc! {"
+ The quick
+ ˇ
+ brown fox"})
+ .await;
+
+ // Our indentation is smarter than vims. So we don't match here
+ cx.assert_manual(
+ indoc! {"
+ fn test() {
+ println!(ˇ);
+ }"},
+ Mode::Normal,
+ indoc! {"
+ fn test() {
+ ˇ
+ println!();
+ }"},
+ Mode::Insert,
+ );
+ cx.assert_manual(
+ indoc! {"
+ fn test(ˇ) {
+ println!();
+ }"},
+ Mode::Normal,
+ indoc! {"
+ ˇ
+ fn test() {
+ println!();
+ }"},
+ Mode::Insert,
+ );
+ }
+
+ #[gpui::test]
+ async fn test_dd(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.assert_neovim_compatible("ˇ", ["d", "d"]).await;
+ cx.assert_neovim_compatible("The ˇquick", ["d", "d"]).await;
+ for marked_text in cx.each_marked_position(indoc! {"
+ The qˇuick
+ brown ˇfox
+ jumps ˇover"})
+ {
+ cx.assert_neovim_compatible(&marked_text, ["d", "d"]).await;
+ }
+ cx.assert_neovim_compatible(
+ indoc! {"
+ The quick
+ ˇ
+ brown fox"},
+ ["d", "d"],
+ )
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_cc(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["c", "c"]);
+ cx.assert("ˇ").await;
+ cx.assert("The ˇquick").await;
+ cx.assert_all(indoc! {"
+ The quˇick
+ brown ˇfox
+ jumps ˇover"})
+ .await;
+ cx.assert(indoc! {"
+ The quick
+ ˇ
+ brown fox"})
+ .await;
+ }
+
+ #[gpui::test]
+ async fn test_repeated_word(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ for count in 1..=5 {
+ cx.assert_binding_matches_all(
+ [&count.to_string(), "w"],
+ indoc! {"
+ ˇThe quˇickˇ browˇn
+ ˇ
+ ˇfox ˇjumpsˇ-ˇoˇver
+ ˇthe lazy dog
+ "},
+ )
+ .await;
+ }
+ }
+
+ #[gpui::test]
+ async fn test_h_through_unicode(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["h"]);
+ cx.assert_all("Testˇ├ˇ──ˇ┐ˇTest").await;
+ }
+
+ #[gpui::test]
+ async fn test_f_and_t(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ for count in 1..=3 {
+ let test_case = indoc! {"
+ ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
+ ˇ ˇbˇaaˇa ˇbˇbˇb
+ ˇ
+ ˇb
+ "};
+
+ cx.assert_binding_matches_all([&count.to_string(), "f", "b"], test_case)
+ .await;
+
+ cx.assert_binding_matches_all([&count.to_string(), "t", "b"], test_case)
+ .await;
+ }
+ }
+
+ #[gpui::test]
+ async fn test_capital_f_and_capital_t(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ let test_case = indoc! {"
+ ˇaaaˇbˇ ˇbˇ ˇbˇbˇ aˇaaˇbaaa
+ ˇ ˇbˇaaˇa ˇbˇbˇb
+ ˇ•••
+ ˇb
+ "
+ };
+
+ for count in 1..=3 {
+ cx.assert_binding_matches_all([&count.to_string(), "shift-f", "b"], test_case)
+ .await;
+
+ cx.assert_binding_matches_all([&count.to_string(), "shift-t", "b"], test_case)
+ .await;
+ }
+ }
+
+ #[gpui::test]
+ async fn test_percent(cx: &mut TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await.binding(["%"]);
+ cx.assert_all("ˇconsole.logˇ(ˇvaˇrˇ)ˇ;").await;
+ cx.assert_all("ˇconsole.logˇ(ˇ'var', ˇ[ˇ1, ˇ2, 3ˇ]ˇ)ˇ;")
+ .await;
+ cx.assert_all("let result = curried_funˇ(ˇ)ˇ(ˇ)ˇ;").await;
+ }
+}
@@ -1,759 +1,752 @@
-// mod neovim_backed_binding_test_context;
-// mod neovim_backed_test_context;
-// mod neovim_connection;
-// mod vim_test_context;
-
-// use std::sync::Arc;
-
-// use command_palette::CommandPalette;
-// use editor::DisplayPoint;
-// pub use neovim_backed_binding_test_context::*;
-// pub use neovim_backed_test_context::*;
-// pub use vim_test_context::*;
-
-// use indoc::indoc;
-// use search::BufferSearchBar;
-
-// use crate::{state::Mode, ModeIndicator};
-
-// #[gpui::test]
-// async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, false).await;
-// cx.simulate_keystrokes(["h", "j", "k", "l"]);
-// cx.assert_editor_state("hjklˇ");
-// }
-
-// #[gpui::test]
-// async fn test_neovim(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.simulate_shared_keystroke("i").await;
-// cx.assert_state_matches().await;
-// cx.simulate_shared_keystrokes([
-// "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
-// ])
-// .await;
-// cx.assert_state_matches().await;
-// cx.assert_editor_state("ˇtest");
-// }
-
-// #[gpui::test]
-// async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.simulate_keystroke("i");
-// assert_eq!(cx.mode(), Mode::Insert);
-
-// // Editor acts as though vim is disabled
-// cx.disable_vim();
-// cx.simulate_keystrokes(["h", "j", "k", "l"]);
-// cx.assert_editor_state("hjklˇ");
-
-// // Selections aren't changed if editor is blurred but vim-mode is still disabled.
-// cx.set_state("«hjklˇ»", Mode::Normal);
-// cx.assert_editor_state("«hjklˇ»");
-// cx.update_editor(|_, cx| cx.blur());
-// cx.assert_editor_state("«hjklˇ»");
-// cx.update_editor(|_, cx| cx.focus_self());
-// cx.assert_editor_state("«hjklˇ»");
-
-// // 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"]);
-// assert_eq!(cx.buffer_text(), "hjkl".to_owned());
-// cx.assert_editor_state("hˇjkl");
-// cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
-// cx.assert_editor_state("hTestˇjkl");
-
-// // Disabling and enabling resets to normal mode
-// assert_eq!(cx.mode(), Mode::Insert);
-// cx.disable_vim();
-// cx.enable_vim();
-// assert_eq!(cx.mode(), Mode::Normal);
-// }
-
-// #[gpui::test]
-// async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state(
-// indoc! {"
-// The quick brown
-// fox juˇmps over
-// the lazy dog"},
-// Mode::Normal,
-// );
-// cx.simulate_keystroke("/");
-
-// let search_bar = cx.workspace(|workspace, cx| {
-// workspace
-// .active_pane()
-// .read(cx)
-// .toolbar()
-// .read(cx)
-// .item_of_type::<BufferSearchBar>()
-// .expect("Buffer search bar should be deployed")
-// });
-
-// cx.update_view(search_bar, |bar, cx| {
-// assert_eq!(bar.query(cx), "");
-// })
-// }
-
-// #[gpui::test]
-// async fn test_count_down(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
-// cx.simulate_keystrokes(["2", "down"]);
-// cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
-// cx.simulate_keystrokes(["9", "down"]);
-// cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
-// }
-
-// #[gpui::test]
-// async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// // goes to end by default
-// cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
-// cx.simulate_keystrokes(["shift-g"]);
-// cx.assert_editor_state("aa\nbb\ncˇc");
-
-// // can go to line 1 (https://github.com/zed-industries/community/issues/710)
-// cx.simulate_keystrokes(["1", "shift-g"]);
-// cx.assert_editor_state("aˇa\nbb\ncc");
-// }
-
-// #[gpui::test]
-// async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// // works in normal mode
-// cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
-// cx.simulate_keystrokes([">", ">"]);
-// cx.assert_editor_state("aa\n bˇb\ncc");
-// cx.simulate_keystrokes(["<", "<"]);
-// cx.assert_editor_state("aa\nbˇb\ncc");
-
-// // works in visuial mode
-// cx.simulate_keystrokes(["shift-v", "down", ">"]);
-// cx.assert_editor_state("aa\n b«b\n ccˇ»");
-// }
-
-// #[gpui::test]
-// async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state("aˇbc\n", Mode::Normal);
-// cx.simulate_keystrokes(["i", "cmd-shift-p"]);
-
-// assert!(cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
-// cx.simulate_keystroke("escape");
-// assert!(!cx.workspace(|workspace, _| workspace.modal::<CommandPalette>().is_some()));
-// cx.assert_state("aˇbc\n", Mode::Insert);
-// }
-
-// #[gpui::test]
-// async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state("aˇbˇc", Mode::Normal);
-// cx.simulate_keystrokes(["escape"]);
-
-// cx.assert_state("aˇbc", Mode::Normal);
-// }
-
-// #[gpui::test]
-// async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
-// cx.simulate_keystrokes(["/", "c", "c"]);
-
-// let search_bar = cx.workspace(|workspace, cx| {
-// workspace
-// .active_pane()
-// .read(cx)
-// .toolbar()
-// .read(cx)
-// .item_of_type::<BufferSearchBar>()
-// .expect("Buffer search bar should be deployed")
-// });
-
-// search_bar.read_with(cx.cx, |bar, cx| {
-// assert_eq!(bar.query(cx), "cc");
-// });
-
-// cx.update_editor(|editor, cx| {
-// let highlights = editor.all_text_background_highlights(cx);
-// assert_eq!(3, highlights.len());
-// assert_eq!(
-// DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
-// highlights[0].0
-// )
-// });
-// cx.simulate_keystrokes(["enter"]);
-
-// cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
-// cx.simulate_keystrokes(["n"]);
-// cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
-// cx.simulate_keystrokes(["shift-n"]);
-// cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
-// }
-
-// #[gpui::test]
-// async fn test_status_indicator(
-// cx: &mut gpui::TestAppContext,
-// deterministic: Arc<gpui::executor::Deterministic>,
-// ) {
-// let mut cx = VimTestContext::new(cx, true).await;
-// deterministic.run_until_parked();
-
-// let mode_indicator = cx.workspace(|workspace, cx| {
-// let status_bar = workspace.status_bar().read(cx);
-// let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
-// assert!(mode_indicator.is_some());
-// mode_indicator.unwrap()
-// });
-
-// assert_eq!(
-// cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-// Some(Mode::Normal)
-// );
-
-// // shows the correct mode
-// cx.simulate_keystrokes(["i"]);
-// deterministic.run_until_parked();
-// assert_eq!(
-// cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-// Some(Mode::Insert)
-// );
-
-// // shows even in search
-// cx.simulate_keystrokes(["escape", "v", "/"]);
-// deterministic.run_until_parked();
-// assert_eq!(
-// cx.workspace(|_, cx| mode_indicator.read(cx).mode),
-// Some(Mode::Visual)
-// );
-
-// // hides if vim mode is disabled
-// cx.disable_vim();
-// deterministic.run_until_parked();
-// cx.workspace(|workspace, cx| {
-// let status_bar = workspace.status_bar().read(cx);
-// let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
-// assert!(mode_indicator.read(cx).mode.is_none());
-// });
-
-// cx.enable_vim();
-// deterministic.run_until_parked();
-// cx.workspace(|workspace, cx| {
-// let status_bar = workspace.status_bar().read(cx);
-// let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
-// assert!(mode_indicator.read(cx).mode.is_some());
-// });
-// }
-
-// #[gpui::test]
-// async fn test_word_characters(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new_typescript(cx).await;
-// cx.set_state(
-// indoc! { "
-// class A {
-// #ˇgoop = 99;
-// $ˇgoop () { return this.#gˇoop };
-// };
-// console.log(new A().$gooˇp())
-// "},
-// Mode::Normal,
-// );
-// cx.simulate_keystrokes(["v", "i", "w"]);
-// cx.assert_state(
-// indoc! {"
-// class A {
-// «#goopˇ» = 99;
-// «$goopˇ» () { return this.«#goopˇ» };
-// };
-// console.log(new A().«$goopˇ»())
-// "},
-// Mode::Visual,
-// )
-// }
-
-// #[gpui::test]
-// async fn test_join_lines(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// ˇone
-// two
-// three
-// four
-// five
-// six
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-j"]).await;
-// cx.assert_shared_state(indoc! {"
-// oneˇ two
-// three
-// four
-// five
-// six
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["3", "shift-j"]).await;
-// cx.assert_shared_state(indoc! {"
-// one two threeˇ four
-// five
-// six
-// "})
-// .await;
-
-// cx.set_shared_state(indoc! {"
-// ˇone
-// two
-// three
-// four
-// five
-// six
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["j", "v", "3", "j", "shift-j"])
-// .await;
-// cx.assert_shared_state(indoc! {"
-// one
-// two three fourˇ five
-// six
-// "})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_wrap(12).await;
-// // tests line wrap as follows:
-// // 1: twelve char
-// // twelve char
-// // 2: twelve char
-// cx.set_shared_state(indoc! { "
-// tˇwelve char twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["j"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char twelve char
-// tˇwelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["k"]).await;
-// cx.assert_shared_state(indoc! { "
-// tˇwelve char twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["g", "j"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char tˇwelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["g", "j"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char twelve char
-// tˇwelve char
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["g", "k"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char tˇwelve char
-// twelve char
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["g", "^"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char ˇtwelve char
-// twelve char
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["^"]).await;
-// cx.assert_shared_state(indoc! { "
-// ˇtwelve char twelve char
-// twelve char
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["g", "$"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve charˇ twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["$"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char twelve chaˇr
-// twelve char
-// "})
-// .await;
-
-// cx.set_shared_state(indoc! { "
-// tˇwelve char twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["enter"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char twelve char
-// ˇtwelve char
-// "})
-// .await;
-
-// cx.set_shared_state(indoc! { "
-// twelve char
-// tˇwelve char twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["o", "o", "escape"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char
-// twelve char twelve char
-// ˇo
-// twelve char
-// "})
-// .await;
-
-// cx.set_shared_state(indoc! { "
-// twelve char
-// tˇwelve char twelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-a", "a", "escape"])
-// .await;
-// cx.assert_shared_state(indoc! { "
-// twelve char
-// twelve char twelve charˇa
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-i", "i", "escape"])
-// .await;
-// cx.assert_shared_state(indoc! { "
-// twelve char
-// ˇitwelve char twelve chara
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-d"]).await;
-// cx.assert_shared_state(indoc! { "
-// twelve char
-// ˇ
-// twelve char
-// "})
-// .await;
-
-// cx.set_shared_state(indoc! { "
-// twelve char
-// twelve char tˇwelve char
-// twelve char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-o", "o", "escape"])
-// .await;
-// cx.assert_shared_state(indoc! { "
-// twelve char
-// ˇo
-// twelve char twelve char
-// twelve char
-// "})
-// .await;
-
-// // line wraps as:
-// // fourteen ch
-// // ar
-// // fourteen ch
-// // ar
-// cx.set_shared_state(indoc! { "
-// fourteen chaˇr
-// fourteen char
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
-// cx.assert_shared_state(indoc! {"
-// fourteenˇ•
-// fourteen char
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
-// .await;
-// cx.assert_shared_state(indoc! {"
-// fourteen•
-// fourteen chaˇr
-// "})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_folds(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.set_neovim_option("foldmethod=manual").await;
-
-// cx.set_shared_state(indoc! { "
-// fn boop() {
-// ˇbarp()
-// bazp()
-// }
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
-// .await;
-
-// // visual display is now:
-// // fn boop () {
-// // [FOLDED]
-// // }
-
-// // TODO: this should not be needed but currently zf does not
-// // return to normal mode.
-// cx.simulate_shared_keystrokes(["escape"]).await;
-
-// // skip over fold downward
-// cx.simulate_shared_keystrokes(["g", "g"]).await;
-// cx.assert_shared_state(indoc! { "
-// ˇfn boop() {
-// barp()
-// bazp()
-// }
-// "})
-// .await;
-
-// cx.simulate_shared_keystrokes(["j", "j"]).await;
-// cx.assert_shared_state(indoc! { "
-// fn boop() {
-// barp()
-// bazp()
-// ˇ}
-// "})
-// .await;
-
-// // skip over fold upward
-// cx.simulate_shared_keystrokes(["2", "k"]).await;
-// cx.assert_shared_state(indoc! { "
-// ˇfn boop() {
-// barp()
-// bazp()
-// }
-// "})
-// .await;
-
-// // yank the fold
-// cx.simulate_shared_keystrokes(["down", "y", "y"]).await;
-// cx.assert_shared_clipboard(" barp()\n bazp()\n").await;
-
-// // re-open
-// cx.simulate_shared_keystrokes(["z", "o"]).await;
-// cx.assert_shared_state(indoc! { "
-// fn boop() {
-// ˇ barp()
-// bazp()
-// }
-// "})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-// cx.set_neovim_option("foldmethod=manual").await;
-
-// cx.set_shared_state(indoc! { "
-// fn boop() {
-// ˇbarp()
-// bazp()
-// }
-// "})
-// .await;
-// cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
-// .await;
-// cx.simulate_shared_keystrokes(["escape"]).await;
-// cx.simulate_shared_keystrokes(["g", "g"]).await;
-// cx.simulate_shared_keystrokes(["5", "d", "j"]).await;
-// cx.assert_shared_state(indoc! { "ˇ"}).await;
-// }
-
-// #[gpui::test]
-// async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// The quick brown
-// fox juˇmps over
-// the lazy dog"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"])
-// .await;
-// cx.assert_shared_state(indoc! {"
-// The quick brown
-// fox juˇ over
-// the lazy dog"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_zero(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// The quˇick brown
-// fox jumps over
-// the lazy dog"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["0"]).await;
-// cx.assert_shared_state(indoc! {"
-// ˇThe quick brown
-// fox jumps over
-// the lazy dog"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["1", "0", "l"]).await;
-// cx.assert_shared_state(indoc! {"
-// The quick ˇbrown
-// fox jumps over
-// the lazy dog"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// ;;ˇ;
-// Lorem Ipsum"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["a", "down", "up", ";", "down", "up"])
-// .await;
-// cx.assert_shared_state(indoc! {"
-// ;;;;ˇ
-// Lorem Ipsum"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_wrap(12).await;
-
-// cx.set_shared_state(indoc! {"
-// aaˇaa
-// 😃😃"
-// })
-// .await;
-// cx.simulate_shared_keystrokes(["j"]).await;
-// cx.assert_shared_state(indoc! {"
-// aaaa
-// 😃ˇ😃"
-// })
-// .await;
-
-// cx.set_shared_state(indoc! {"
-// 123456789012aaˇaa
-// 123456789012😃😃"
-// })
-// .await;
-// cx.simulate_shared_keystrokes(["j"]).await;
-// cx.assert_shared_state(indoc! {"
-// 123456789012aaaa
-// 123456789012😃ˇ😃"
-// })
-// .await;
-
-// cx.set_shared_state(indoc! {"
-// 123456789012aaˇaa
-// 123456789012😃😃"
-// })
-// .await;
-// cx.simulate_shared_keystrokes(["j"]).await;
-// cx.assert_shared_state(indoc! {"
-// 123456789012aaaa
-// 123456789012😃ˇ😃"
-// })
-// .await;
-
-// cx.set_shared_state(indoc! {"
-// 123456789012aaaaˇaaaaaaaa123456789012
-// wow
-// 123456789012😃😃😃😃😃😃123456789012"
-// })
-// .await;
-// cx.simulate_shared_keystrokes(["j", "j"]).await;
-// cx.assert_shared_state(indoc! {"
-// 123456789012aaaaaaaaaaaa123456789012
-// wow
-// 123456789012😃😃ˇ😃😃😃😃123456789012"
-// })
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
-// let mut cx = NeovimBackedTestContext::new(cx).await;
-
-// cx.set_shared_state(indoc! {"
-// one
-// ˇ
-// two"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["}", "}"]).await;
-// cx.assert_shared_state(indoc! {"
-// one
-
-// twˇo"})
-// .await;
-
-// cx.simulate_shared_keystrokes(["{", "{", "{"]).await;
-// cx.assert_shared_state(indoc! {"
-// ˇone
-
-// two"})
-// .await;
-// }
-
-// #[gpui::test]
-// async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
-// let mut cx = VimTestContext::new(cx, true).await;
-
-// cx.set_state(
-// indoc! {"
-// defmodule Test do
-// def test(a, ˇ[_, _] = b), do: IO.puts('hi')
-// end
-// "},
-// Mode::Normal,
-// );
-// cx.simulate_keystrokes(["g", "a"]);
-// cx.assert_state(
-// indoc! {"
-// defmodule Test do
-// def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
-// end
-// "},
-// Mode::Visual,
-// );
-// }
+mod neovim_backed_binding_test_context;
+mod neovim_backed_test_context;
+mod neovim_connection;
+mod vim_test_context;
+
+use command_palette::CommandPalette;
+use editor::DisplayPoint;
+pub use neovim_backed_binding_test_context::*;
+pub use neovim_backed_test_context::*;
+pub use vim_test_context::*;
+
+use indoc::indoc;
+use search::BufferSearchBar;
+
+use crate::{state::Mode, ModeIndicator};
+
+#[gpui::test]
+async fn test_initially_disabled(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, false).await;
+ cx.simulate_keystrokes(["h", "j", "k", "l"]);
+ cx.assert_editor_state("hjklˇ");
+}
+
+#[gpui::test]
+async fn test_neovim(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.simulate_shared_keystroke("i").await;
+ cx.assert_state_matches().await;
+ cx.simulate_shared_keystrokes([
+ "shift-T", "e", "s", "t", " ", "t", "e", "s", "t", "escape", "0", "d", "w",
+ ])
+ .await;
+ cx.assert_state_matches().await;
+ cx.assert_editor_state("ˇtest");
+}
+
+#[gpui::test]
+async fn test_toggle_through_settings(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.simulate_keystroke("i");
+ assert_eq!(cx.mode(), Mode::Insert);
+
+ // Editor acts as though vim is disabled
+ cx.disable_vim();
+ cx.simulate_keystrokes(["h", "j", "k", "l"]);
+ cx.assert_editor_state("hjklˇ");
+
+ // Selections aren't changed if editor is blurred but vim-mode is still disabled.
+ cx.set_state("«hjklˇ»", Mode::Normal);
+ cx.assert_editor_state("«hjklˇ»");
+ cx.update_editor(|_, cx| cx.blur());
+ cx.assert_editor_state("«hjklˇ»");
+ cx.update_editor(|_, cx| cx.focus_self());
+ cx.assert_editor_state("«hjklˇ»");
+
+ // 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"]);
+ assert_eq!(cx.buffer_text(), "hjkl".to_owned());
+ cx.assert_editor_state("hˇjkl");
+ cx.simulate_keystrokes(["i", "T", "e", "s", "t"]);
+ cx.assert_editor_state("hTestˇjkl");
+
+ // Disabling and enabling resets to normal mode
+ assert_eq!(cx.mode(), Mode::Insert);
+ cx.disable_vim();
+ cx.enable_vim();
+ assert_eq!(cx.mode(), Mode::Normal);
+}
+
+#[gpui::test]
+async fn test_buffer_search(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(
+ indoc! {"
+ The quick brown
+ fox juˇmps over
+ the lazy dog"},
+ Mode::Normal,
+ );
+ cx.simulate_keystroke("/");
+
+ let search_bar = cx.workspace(|workspace, cx| {
+ workspace
+ .active_pane()
+ .read(cx)
+ .toolbar()
+ .read(cx)
+ .item_of_type::<BufferSearchBar>()
+ .expect("Buffer search bar should be deployed")
+ });
+
+ cx.update_view(search_bar, |bar, cx| {
+ assert_eq!(bar.query(cx), "");
+ })
+}
+
+#[gpui::test]
+async fn test_count_down(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(indoc! {"aˇa\nbb\ncc\ndd\nee"}, Mode::Normal);
+ cx.simulate_keystrokes(["2", "down"]);
+ cx.assert_editor_state("aa\nbb\ncˇc\ndd\nee");
+ cx.simulate_keystrokes(["9", "down"]);
+ cx.assert_editor_state("aa\nbb\ncc\ndd\neˇe");
+}
+
+#[gpui::test]
+async fn test_end_of_document_710(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ // goes to end by default
+ cx.set_state(indoc! {"aˇa\nbb\ncc"}, Mode::Normal);
+ cx.simulate_keystrokes(["shift-g"]);
+ cx.assert_editor_state("aa\nbb\ncˇc");
+
+ // can go to line 1 (https://github.com/zed-industries/community/issues/710)
+ cx.simulate_keystrokes(["1", "shift-g"]);
+ cx.assert_editor_state("aˇa\nbb\ncc");
+}
+
+#[gpui::test]
+async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ // works in normal mode
+ cx.set_state(indoc! {"aa\nbˇb\ncc"}, Mode::Normal);
+ cx.simulate_keystrokes([">", ">"]);
+ cx.assert_editor_state("aa\n bˇb\ncc");
+ cx.simulate_keystrokes(["<", "<"]);
+ cx.assert_editor_state("aa\nbˇb\ncc");
+
+ // works in visuial mode
+ cx.simulate_keystrokes(["shift-v", "down", ">"]);
+ cx.assert_editor_state("aa\n b«b\n ccˇ»");
+}
+
+#[gpui::test]
+async fn test_escape_command_palette(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state("aˇbc\n", Mode::Normal);
+ cx.simulate_keystrokes(["i", "cmd-shift-p"]);
+
+ assert!(cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
+ cx.simulate_keystroke("escape");
+ cx.run_until_parked();
+ assert!(!cx.workspace(|workspace, cx| workspace.active_modal::<CommandPalette>(cx).is_some()));
+ cx.assert_state("aˇbc\n", Mode::Insert);
+}
+
+#[gpui::test]
+async fn test_escape_cancels(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state("aˇbˇc", Mode::Normal);
+ cx.simulate_keystrokes(["escape"]);
+
+ cx.assert_state("aˇbc", Mode::Normal);
+}
+
+#[gpui::test]
+async fn test_selection_on_search(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(indoc! {"aa\nbˇb\ncc\ncc\ncc\n"}, Mode::Normal);
+ cx.simulate_keystrokes(["/", "c", "c"]);
+
+ let search_bar = cx.workspace(|workspace, cx| {
+ workspace
+ .active_pane()
+ .read(cx)
+ .toolbar()
+ .read(cx)
+ .item_of_type::<BufferSearchBar>()
+ .expect("Buffer search bar should be deployed")
+ });
+
+ cx.update_view(search_bar, |bar, cx| {
+ assert_eq!(bar.query(cx), "cc");
+ });
+
+ cx.update_editor(|editor, cx| {
+ let highlights = editor.all_text_background_highlights(cx);
+ assert_eq!(3, highlights.len());
+ assert_eq!(
+ DisplayPoint::new(2, 0)..DisplayPoint::new(2, 2),
+ highlights[0].0
+ )
+ });
+ cx.simulate_keystrokes(["enter"]);
+
+ cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
+ cx.simulate_keystrokes(["n"]);
+ cx.assert_state(indoc! {"aa\nbb\ncc\nˇcc\ncc\n"}, Mode::Normal);
+ cx.simulate_keystrokes(["shift-n"]);
+ cx.assert_state(indoc! {"aa\nbb\nˇcc\ncc\ncc\n"}, Mode::Normal);
+}
+
+#[gpui::test]
+async fn test_status_indicator(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ let mode_indicator = cx.workspace(|workspace, cx| {
+ let status_bar = workspace.status_bar().read(cx);
+ let mode_indicator = status_bar.item_of_type::<ModeIndicator>();
+ assert!(mode_indicator.is_some());
+ mode_indicator.unwrap()
+ });
+
+ assert_eq!(
+ cx.workspace(|_, cx| mode_indicator.read(cx).mode),
+ Some(Mode::Normal)
+ );
+
+ // shows the correct mode
+ cx.simulate_keystrokes(["i"]);
+ assert_eq!(
+ cx.workspace(|_, cx| mode_indicator.read(cx).mode),
+ Some(Mode::Insert)
+ );
+
+ // shows even in search
+ cx.simulate_keystrokes(["escape", "v", "/"]);
+ assert_eq!(
+ cx.workspace(|_, cx| mode_indicator.read(cx).mode),
+ Some(Mode::Visual)
+ );
+
+ // hides if vim mode is disabled
+ cx.disable_vim();
+ cx.run_until_parked();
+ cx.workspace(|workspace, cx| {
+ let status_bar = workspace.status_bar().read(cx);
+ let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
+ assert!(mode_indicator.read(cx).mode.is_none());
+ });
+
+ cx.enable_vim();
+ cx.run_until_parked();
+ cx.workspace(|workspace, cx| {
+ let status_bar = workspace.status_bar().read(cx);
+ let mode_indicator = status_bar.item_of_type::<ModeIndicator>().unwrap();
+ assert!(mode_indicator.read(cx).mode.is_some());
+ });
+}
+
+#[gpui::test]
+async fn test_word_characters(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new_typescript(cx).await;
+ cx.set_state(
+ indoc! { "
+ class A {
+ #ˇgoop = 99;
+ $ˇgoop () { return this.#gˇoop };
+ };
+ console.log(new A().$gooˇp())
+ "},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes(["v", "i", "w"]);
+ cx.assert_state(
+ indoc! {"
+ class A {
+ «#goopˇ» = 99;
+ «$goopˇ» () { return this.«#goopˇ» };
+ };
+ console.log(new A().«$goopˇ»())
+ "},
+ Mode::Visual,
+ )
+}
+
+#[gpui::test]
+async fn test_join_lines(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ ˇone
+ two
+ three
+ four
+ five
+ six
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-j"]).await;
+ cx.assert_shared_state(indoc! {"
+ oneˇ two
+ three
+ four
+ five
+ six
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["3", "shift-j"]).await;
+ cx.assert_shared_state(indoc! {"
+ one two threeˇ four
+ five
+ six
+ "})
+ .await;
+
+ cx.set_shared_state(indoc! {"
+ ˇone
+ two
+ three
+ four
+ five
+ six
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["j", "v", "3", "j", "shift-j"])
+ .await;
+ cx.assert_shared_state(indoc! {"
+ one
+ two three fourˇ five
+ six
+ "})
+ .await;
+}
+
+#[gpui::test]
+async fn test_wrapped_lines(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_wrap(12).await;
+ // tests line wrap as follows:
+ // 1: twelve char
+ // twelve char
+ // 2: twelve char
+ cx.set_shared_state(indoc! { "
+ tˇwelve char twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["j"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char twelve char
+ tˇwelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["k"]).await;
+ cx.assert_shared_state(indoc! { "
+ tˇwelve char twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["g", "j"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char tˇwelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["g", "j"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char twelve char
+ tˇwelve char
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["g", "k"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char tˇwelve char
+ twelve char
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["g", "^"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char ˇtwelve char
+ twelve char
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["^"]).await;
+ cx.assert_shared_state(indoc! { "
+ ˇtwelve char twelve char
+ twelve char
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["g", "$"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve charˇ twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["$"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char twelve chaˇr
+ twelve char
+ "})
+ .await;
+
+ cx.set_shared_state(indoc! { "
+ tˇwelve char twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["enter"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char twelve char
+ ˇtwelve char
+ "})
+ .await;
+
+ cx.set_shared_state(indoc! { "
+ twelve char
+ tˇwelve char twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["o", "o", "escape"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char
+ twelve char twelve char
+ ˇo
+ twelve char
+ "})
+ .await;
+
+ cx.set_shared_state(indoc! { "
+ twelve char
+ tˇwelve char twelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-a", "a", "escape"])
+ .await;
+ cx.assert_shared_state(indoc! { "
+ twelve char
+ twelve char twelve charˇa
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-i", "i", "escape"])
+ .await;
+ cx.assert_shared_state(indoc! { "
+ twelve char
+ ˇitwelve char twelve chara
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-d"]).await;
+ cx.assert_shared_state(indoc! { "
+ twelve char
+ ˇ
+ twelve char
+ "})
+ .await;
+
+ cx.set_shared_state(indoc! { "
+ twelve char
+ twelve char tˇwelve char
+ twelve char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-o", "o", "escape"])
+ .await;
+ cx.assert_shared_state(indoc! { "
+ twelve char
+ ˇo
+ twelve char twelve char
+ twelve char
+ "})
+ .await;
+
+ // line wraps as:
+ // fourteen ch
+ // ar
+ // fourteen ch
+ // ar
+ cx.set_shared_state(indoc! { "
+ fourteen chaˇr
+ fourteen char
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["d", "i", "w"]).await;
+ cx.assert_shared_state(indoc! {"
+ fourteenˇ•
+ fourteen char
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["j", "shift-f", "e", "f", "r"])
+ .await;
+ cx.assert_shared_state(indoc! {"
+ fourteen•
+ fourteen chaˇr
+ "})
+ .await;
+}
+
+#[gpui::test]
+async fn test_folds(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.set_neovim_option("foldmethod=manual").await;
+
+ cx.set_shared_state(indoc! { "
+ fn boop() {
+ ˇbarp()
+ bazp()
+ }
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
+ .await;
+
+ // visual display is now:
+ // fn boop () {
+ // [FOLDED]
+ // }
+
+ // TODO: this should not be needed but currently zf does not
+ // return to normal mode.
+ cx.simulate_shared_keystrokes(["escape"]).await;
+
+ // skip over fold downward
+ cx.simulate_shared_keystrokes(["g", "g"]).await;
+ cx.assert_shared_state(indoc! { "
+ ˇfn boop() {
+ barp()
+ bazp()
+ }
+ "})
+ .await;
+
+ cx.simulate_shared_keystrokes(["j", "j"]).await;
+ cx.assert_shared_state(indoc! { "
+ fn boop() {
+ barp()
+ bazp()
+ ˇ}
+ "})
+ .await;
+
+ // skip over fold upward
+ cx.simulate_shared_keystrokes(["2", "k"]).await;
+ cx.assert_shared_state(indoc! { "
+ ˇfn boop() {
+ barp()
+ bazp()
+ }
+ "})
+ .await;
+
+ // yank the fold
+ cx.simulate_shared_keystrokes(["down", "y", "y"]).await;
+ cx.assert_shared_clipboard(" barp()\n bazp()\n").await;
+
+ // re-open
+ cx.simulate_shared_keystrokes(["z", "o"]).await;
+ cx.assert_shared_state(indoc! { "
+ fn boop() {
+ ˇ barp()
+ bazp()
+ }
+ "})
+ .await;
+}
+
+#[gpui::test]
+async fn test_folds_panic(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+ cx.set_neovim_option("foldmethod=manual").await;
+
+ cx.set_shared_state(indoc! { "
+ fn boop() {
+ ˇbarp()
+ bazp()
+ }
+ "})
+ .await;
+ cx.simulate_shared_keystrokes(["shift-v", "j", "z", "f"])
+ .await;
+ cx.simulate_shared_keystrokes(["escape"]).await;
+ cx.simulate_shared_keystrokes(["g", "g"]).await;
+ cx.simulate_shared_keystrokes(["5", "d", "j"]).await;
+ cx.assert_shared_state(indoc! { "ˇ"}).await;
+}
+
+#[gpui::test]
+async fn test_clear_counts(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ The quick brown
+ fox juˇmps over
+ the lazy dog"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["4", "escape", "3", "d", "l"])
+ .await;
+ cx.assert_shared_state(indoc! {"
+ The quick brown
+ fox juˇ over
+ the lazy dog"})
+ .await;
+}
+
+#[gpui::test]
+async fn test_zero(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ The quˇick brown
+ fox jumps over
+ the lazy dog"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["0"]).await;
+ cx.assert_shared_state(indoc! {"
+ ˇThe quick brown
+ fox jumps over
+ the lazy dog"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["1", "0", "l"]).await;
+ cx.assert_shared_state(indoc! {"
+ The quick ˇbrown
+ fox jumps over
+ the lazy dog"})
+ .await;
+}
+
+#[gpui::test]
+async fn test_selection_goal(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ ;;ˇ;
+ Lorem Ipsum"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["a", "down", "up", ";", "down", "up"])
+ .await;
+ cx.assert_shared_state(indoc! {"
+ ;;;;ˇ
+ Lorem Ipsum"})
+ .await;
+}
+
+#[gpui::test]
+async fn test_wrapped_motions(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_wrap(12).await;
+
+ cx.set_shared_state(indoc! {"
+ aaˇaa
+ 😃😃"
+ })
+ .await;
+ cx.simulate_shared_keystrokes(["j"]).await;
+ cx.assert_shared_state(indoc! {"
+ aaaa
+ 😃ˇ😃"
+ })
+ .await;
+
+ cx.set_shared_state(indoc! {"
+ 123456789012aaˇaa
+ 123456789012😃😃"
+ })
+ .await;
+ cx.simulate_shared_keystrokes(["j"]).await;
+ cx.assert_shared_state(indoc! {"
+ 123456789012aaaa
+ 123456789012😃ˇ😃"
+ })
+ .await;
+
+ cx.set_shared_state(indoc! {"
+ 123456789012aaˇaa
+ 123456789012😃😃"
+ })
+ .await;
+ cx.simulate_shared_keystrokes(["j"]).await;
+ cx.assert_shared_state(indoc! {"
+ 123456789012aaaa
+ 123456789012😃ˇ😃"
+ })
+ .await;
+
+ cx.set_shared_state(indoc! {"
+ 123456789012aaaaˇaaaaaaaa123456789012
+ wow
+ 123456789012😃😃😃😃😃😃123456789012"
+ })
+ .await;
+ cx.simulate_shared_keystrokes(["j", "j"]).await;
+ cx.assert_shared_state(indoc! {"
+ 123456789012aaaaaaaaaaaa123456789012
+ wow
+ 123456789012😃😃ˇ😃😃😃😃123456789012"
+ })
+ .await;
+}
+
+#[gpui::test]
+async fn test_paragraphs_dont_wrap(cx: &mut gpui::TestAppContext) {
+ let mut cx = NeovimBackedTestContext::new(cx).await;
+
+ cx.set_shared_state(indoc! {"
+ one
+ ˇ
+ two"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["}", "}"]).await;
+ cx.assert_shared_state(indoc! {"
+ one
+
+ twˇo"})
+ .await;
+
+ cx.simulate_shared_keystrokes(["{", "{", "{"]).await;
+ cx.assert_shared_state(indoc! {"
+ ˇone
+
+ two"})
+ .await;
+}
+
+#[gpui::test]
+async fn test_select_all_issue_2170(cx: &mut gpui::TestAppContext) {
+ let mut cx = VimTestContext::new(cx, true).await;
+
+ cx.set_state(
+ indoc! {"
+ defmodule Test do
+ def test(a, ˇ[_, _] = b), do: IO.puts('hi')
+ end
+ "},
+ Mode::Normal,
+ );
+ cx.simulate_keystrokes(["g", "a"]);
+ cx.assert_state(
+ indoc! {"
+ defmodule Test do
+ def test(a, «[ˇ»_, _] = b), do: IO.puts('hi')
+ end
+ "},
+ Mode::Visual,
+ );
+}
@@ -1,3 +1,6 @@
+#![allow(unused)]
+// todo!()
+
use std::ops::{Deref, DerefMut};
use crate::state::Mode;
@@ -1,4 +1,7 @@
-use editor::scroll::VERTICAL_SCROLL_MARGIN;
+#![allow(unused)]
+// todo!()
+
+use editor::{scroll::VERTICAL_SCROLL_MARGIN, test::editor_test_context::ContextHandle};
use indoc::indoc;
use settings::SettingsStore;
use std::{
@@ -7,7 +10,6 @@ use std::{
};
use collections::{HashMap, HashSet};
-use gpui::{geometry::vector::vec2f, ContextHandle};
use language::language_settings::{AllLanguageSettings, SoftWrap};
use util::test::marked_text_offsets;
@@ -151,19 +153,20 @@ impl<'a> NeovimBackedTestContext<'a> {
})
}
- pub async fn set_scroll_height(&mut self, rows: u32) {
- // match Zed's scrolling behavior
- self.neovim
- .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN))
- .await;
- // +2 to account for the vim command UI at the bottom.
- self.neovim.set_option(&format!("lines={}", rows + 2)).await;
- let window = self.window;
- let line_height =
- self.editor(|editor, cx| editor.style().text.line_height(cx.font_cache()));
-
- window.simulate_resize(vec2f(1000., (rows as f32) * line_height), &mut self.cx);
- }
+ // todo!()
+ // pub async fn set_scroll_height(&mut self, rows: u32) {
+ // // match Zed's scrolling behavior
+ // self.neovim
+ // .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN))
+ // .await;
+ // // +2 to account for the vim command UI at the bottom.
+ // self.neovim.set_option(&format!("lines={}", rows + 2)).await;
+ // let window = self.window;
+ // let line_height =
+ // self.editor(|editor, cx| editor.style().text.line_height(cx.font_cache()));
+
+ // window.simulate_resize(vec2f(1000., (rows as f32) * line_height), &mut self.cx);
+ // }
pub async fn set_neovim_option(&mut self, option: &str) {
self.neovim.set_option(option).await;
@@ -211,12 +214,7 @@ impl<'a> NeovimBackedTestContext<'a> {
pub async fn assert_shared_clipboard(&mut self, text: &str) {
let neovim = self.neovim.read_register('"').await;
- let editor = self
- .platform()
- .read_from_clipboard()
- .unwrap()
- .text()
- .clone();
+ let editor = self.read_from_clipboard().unwrap().text().clone();
if text == neovim && text == editor {
return;
@@ -10,7 +10,7 @@ use async_compat::Compat;
#[cfg(feature = "neovim")]
use async_trait::async_trait;
#[cfg(feature = "neovim")]
-use gpui::keymap_matcher::Keystroke;
+use gpui::Keystroke;
#[cfg(feature = "neovim")]
use language::Point;
@@ -116,16 +116,24 @@ impl NeovimConnection {
keystroke.key = "lt".to_string()
}
- let special = keystroke.shift
- || keystroke.ctrl
- || keystroke.alt
- || keystroke.cmd
+ let special = keystroke.modifiers.shift
+ || keystroke.modifiers.control
+ || keystroke.modifiers.alt
+ || keystroke.modifiers.command
|| keystroke.key.len() > 1;
let start = if special { "<" } else { "" };
- let shift = if keystroke.shift { "S-" } else { "" };
- let ctrl = if keystroke.ctrl { "C-" } else { "" };
- let alt = if keystroke.alt { "M-" } else { "" };
- let cmd = if keystroke.cmd { "D-" } else { "" };
+ let shift = if keystroke.modifiers.shift { "S-" } else { "" };
+ let ctrl = if keystroke.modifiers.control {
+ "C-"
+ } else {
+ ""
+ };
+ let alt = if keystroke.modifiers.alt { "M-" } else { "" };
+ let cmd = if keystroke.modifiers.command {
+ "D-"
+ } else {
+ ""
+ };
let end = if special { ">" } else { "" };
let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key);
@@ -1,3 +1,6 @@
+#![allow(unused)]
+// todo!()
+
use std::ops::{Deref, DerefMut};
use editor::test::{
@@ -16,11 +19,25 @@ pub struct VimTestContext<'a> {
impl<'a> VimTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
+ cx.update(|cx| {
+ search::init(cx);
+ let settings = SettingsStore::test(cx);
+ cx.set_global(settings);
+ command_palette::init(cx);
+ crate::init(cx);
+ });
let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
Self::new_with_lsp(lsp, enabled)
}
pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> {
+ cx.update(|cx| {
+ search::init(cx);
+ let settings = SettingsStore::test(cx);
+ cx.set_global(settings);
+ command_palette::init(cx);
+ crate::init(cx);
+ });
Self::new_with_lsp(
EditorLspTestContext::new_typescript(Default::default(), cx).await,
true,
@@ -28,12 +45,6 @@ impl<'a> VimTestContext<'a> {
}
pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> VimTestContext<'a> {
- cx.update(|cx| {
- search::init(cx);
- crate::init(cx);
- command_palette::init(cx);
- });
-
cx.update(|cx| {
cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
@@ -65,9 +76,11 @@ impl<'a> VimTestContext<'a> {
pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
where
- F: FnOnce(&mut T, &mut ViewContext<T>) -> R,
+ T: 'static,
+ F: FnOnce(&mut T, &mut ViewContext<T>) -> R + 'static,
{
- self.update_window(self.window, |_, cx| view.update(cx, update))
+ let window = self.window.clone();
+ self.update_window(window, move |_, cx| view.update(cx, update))
.unwrap()
}
@@ -75,8 +88,7 @@ impl<'a> VimTestContext<'a> {
where
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
{
- self.update_window(self.window, |_, cx| self.cx.workspace.update(cx, update))
- .unwrap()
+ self.cx.update_workspace(update)
}
pub fn enable_vim(&mut self) {
@@ -111,7 +123,8 @@ impl<'a> VimTestContext<'a> {
Vim::update(cx, |vim, cx| {
vim.switch_mode(mode, true, cx);
})
- });
+ })
+ .unwrap();
self.cx.cx.cx.run_until_parked();
}
@@ -116,45 +116,43 @@ fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
visual::register(workspace, cx);
}
-pub fn observe_keystrokes(_: &mut WindowContext) {
- // todo!()
-
- // cx.observe_keystrokes(|_keystroke, result, handled_by, cx| {
- // if result == &MatchResult::Pending {
- // return true;
- // }
- // if let Some(handled_by) = handled_by {
- // Vim::update(cx, |vim, _| {
- // if vim.workspace_state.recording {
- // vim.workspace_state
- // .recorded_actions
- // .push(ReplayableAction::Action(handled_by.boxed_clone()));
-
- // if vim.workspace_state.stop_recording_after_next_action {
- // vim.workspace_state.recording = false;
- // vim.workspace_state.stop_recording_after_next_action = false;
- // }
- // }
- // });
-
- // // Keystroke is handled by the vim system, so continue forward
- // if handled_by.namespace() == "vim" {
- // return true;
- // }
- // }
-
- // Vim::update(cx, |vim, cx| match vim.active_operator() {
- // Some(
- // Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace,
- // ) => {}
- // Some(_) => {
- // vim.clear_operator(cx);
- // }
- // _ => {}
- // });
- // true
- // })
- // .detach()
+pub fn observe_keystrokes(cx: &mut WindowContext) {
+ cx.observe_keystrokes(|keystroke_event, cx| {
+ if let Some(action) = keystroke_event
+ .action
+ .as_ref()
+ .map(|action| action.boxed_clone())
+ {
+ Vim::update(cx, |vim, _| {
+ if vim.workspace_state.recording {
+ vim.workspace_state
+ .recorded_actions
+ .push(ReplayableAction::Action(action.boxed_clone()));
+
+ if vim.workspace_state.stop_recording_after_next_action {
+ vim.workspace_state.recording = false;
+ vim.workspace_state.stop_recording_after_next_action = false;
+ }
+ }
+ });
+
+ // Keystroke is handled by the vim system, so continue forward
+ if action.name().starts_with("vim::") {
+ return;
+ }
+ }
+
+ Vim::update(cx, |vim, cx| match vim.active_operator() {
+ Some(
+ Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace,
+ ) => {}
+ Some(_) => {
+ vim.clear_operator(cx);
+ }
+ _ => {}
+ });
+ })
+ .detach()
}
#[derive(Default)]