First round of vim tests

This commit is contained in:
Conrad Irwin 2023-12-11 13:57:46 -07:00
parent f35453caad
commit d1805d8ada
16 changed files with 938 additions and 884 deletions

View file

@ -170,6 +170,10 @@ impl<'a> EditorTestContext<'a> {
keystrokes_under_test_handle 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>> { pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false); let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
assert_eq!(self.buffer_text(), unmarked_text); assert_eq!(self.buffer_text(), unmarked_text);

View file

@ -19,10 +19,10 @@ use crate::{
current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any, current_platform, image_cache::ImageCache, init_app_menus, Action, ActionRegistry, Any,
AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, AnyView, AnyWindowHandle, AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context,
DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId,
ForegroundExecutor, KeyBinding, Keymap, LayoutId, Menu, PathPromptOptions, Pixels, Platform, ForegroundExecutor, KeyBinding, Keymap, Keystroke, LayoutId, Menu, PathPromptOptions, Pixels,
PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, Platform, PlatformDisplay, Point, Render, SharedString, SubscriberSet, Subscription,
TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window, WindowContext, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, Window,
WindowHandle, WindowId, WindowContext, WindowHandle, WindowId,
}; };
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
@ -171,6 +171,7 @@ impl App {
pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>; pub(crate) type FrameCallback = Box<dyn FnOnce(&mut AppContext)>;
type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>; type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &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 QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>; type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>; type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
@ -212,6 +213,7 @@ pub struct AppContext {
pub(crate) observers: SubscriberSet<EntityId, Handler>, pub(crate) observers: SubscriberSet<EntityId, Handler>,
// TypeId is the type of the event that the listener callback expects // TypeId is the type of the event that the listener callback expects
pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>, pub(crate) event_listeners: SubscriberSet<EntityId, (TypeId, Listener)>,
pub(crate) keystroke_observers: SubscriberSet<(), KeystrokeObserver>,
pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>, pub(crate) release_listeners: SubscriberSet<EntityId, ReleaseListener>,
pub(crate) global_observers: SubscriberSet<TypeId, Handler>, pub(crate) global_observers: SubscriberSet<TypeId, Handler>,
pub(crate) quit_observers: SubscriberSet<(), QuitHandler>, pub(crate) quit_observers: SubscriberSet<(), QuitHandler>,
@ -272,6 +274,7 @@ impl AppContext {
observers: SubscriberSet::new(), observers: SubscriberSet::new(),
event_listeners: SubscriberSet::new(), event_listeners: SubscriberSet::new(),
release_listeners: SubscriberSet::new(), release_listeners: SubscriberSet::new(),
keystroke_observers: SubscriberSet::new(),
global_observers: SubscriberSet::new(), global_observers: SubscriberSet::new(),
quit_observers: SubscriberSet::new(), quit_observers: SubscriberSet::new(),
layout_id_buffer: Default::default(), layout_id_buffer: Default::default(),
@ -955,6 +958,15 @@ impl AppContext {
subscription 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) { pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
self.text_style_stack.push(text_style); self.text_style_stack.push(text_style);
} }
@ -1281,3 +1293,9 @@ pub(crate) struct AnyTooltip {
pub view: AnyView, pub view: AnyView,
pub cursor_offset: Point<Pixels>, pub cursor_offset: Point<Pixels>,
} }
#[derive(Debug)]
pub struct KeystrokeEvent {
pub keystroke: Keystroke,
pub action: Option<Box<dyn Action>>,
}

View file

@ -208,7 +208,7 @@ impl DispatchTree {
&mut self, &mut self,
keystroke: &Keystroke, keystroke: &Keystroke,
context: &[KeyContext], context: &[KeyContext],
) -> Option<Box<dyn Action>> { ) -> Vec<Box<dyn Action>> {
if !self.keystroke_matchers.contains_key(context) { if !self.keystroke_matchers.contains_key(context) {
let keystroke_contexts = context.iter().cloned().collect(); let keystroke_contexts = context.iter().cloned().collect();
self.keystroke_matchers.insert( self.keystroke_matchers.insert(
@ -218,15 +218,15 @@ impl DispatchTree {
} }
let keystroke_matcher = self.keystroke_matchers.get_mut(context).unwrap(); 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. // Clear all pending keystrokes when an action has been found.
for keystroke_matcher in self.keystroke_matchers.values_mut() { for keystroke_matcher in self.keystroke_matchers.values_mut() {
keystroke_matcher.clear_pending(); keystroke_matcher.clear_pending();
} }
Some(action) actions
} else { } else {
None vec![]
} }
} }

View file

@ -59,7 +59,7 @@ impl KeyBinding {
{ {
// If the binding is completed, push it onto the matches list // If the binding is completed, push it onto the matches list
if self.keystrokes.as_ref().len() == pending_keystrokes.len() { if self.keystrokes.as_ref().len() == pending_keystrokes.len() {
KeyMatch::Some(self.action.boxed_clone()) KeyMatch::Some(vec![self.action.boxed_clone()])
} else { } else {
KeyMatch::Pending KeyMatch::Pending
} }

View file

@ -54,14 +54,14 @@ impl KeystrokeMatcher {
} }
let mut pending_key = None; let mut pending_key = None;
let mut found_actions = Vec::new();
for binding in keymap.bindings().iter().rev() { for binding in keymap.bindings().iter().rev() {
for candidate in keystroke.match_candidates() { for candidate in keystroke.match_candidates() {
self.pending_keystrokes.push(candidate.clone()); self.pending_keystrokes.push(candidate.clone());
match binding.match_keystrokes(&self.pending_keystrokes, context_stack) { match binding.match_keystrokes(&self.pending_keystrokes, context_stack) {
KeyMatch::Some(action) => { KeyMatch::Some(mut actions) => {
self.pending_keystrokes.clear(); found_actions.append(&mut actions);
return KeyMatch::Some(action);
} }
KeyMatch::Pending => { KeyMatch::Pending => {
pending_key.get_or_insert(candidate); 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 { if let Some(pending_key) = pending_key {
self.pending_keystrokes.push(pending_key); self.pending_keystrokes.push(pending_key);
} }
@ -101,7 +106,7 @@ impl KeystrokeMatcher {
pub enum KeyMatch { pub enum KeyMatch {
None, None,
Pending, Pending,
Some(Box<dyn Action>), Some(Vec<Box<dyn Action>>),
} }
impl KeyMatch { impl KeyMatch {

View file

@ -128,7 +128,7 @@ impl Platform for TestPlatform {
} }
fn active_window(&self) -> Option<crate::AnyWindowHandle> { fn active_window(&self) -> Option<crate::AnyWindowHandle> {
unimplemented!() self.active_window.lock().clone()
} }
fn open_window( fn open_window(

View file

@ -3,9 +3,9 @@ use crate::{
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle, AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, EventEmitter, FileDropEvent, Flatten, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, LayoutId, Model, ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent, Path, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
@ -478,6 +478,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 /// 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. /// that are currently on the stack to be returned to the app.
pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) { pub fn defer(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
@ -1406,14 +1429,12 @@ impl<'a> WindowContext<'a> {
let node = self.window.rendered_frame.dispatch_tree.node(*node_id); let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
if node.context.is_some() { if node.context.is_some() {
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() { if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
if let Some(found) = self let mut new_actions = self
.window .window
.rendered_frame .rendered_frame
.dispatch_tree .dispatch_tree
.dispatch_key(&key_down_event.keystroke, &context_stack) .dispatch_key(&key_down_event.keystroke, &context_stack);
{ actions.append(&mut new_actions);
actions.push(found.boxed_clone())
}
} }
context_stack.pop(); context_stack.pop();
@ -1421,11 +1442,13 @@ impl<'a> WindowContext<'a> {
} }
for action in actions { 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 { if !self.propagate_event {
self.dispatch_keystroke_observers(event, Some(action));
return; return;
} }
} }
self.dispatch_keystroke_observers(event, None);
} }
fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box<dyn Action>) { fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box<dyn Action>) {

View file

@ -114,6 +114,7 @@ impl<D: PickerDelegate> Picker<D> {
} }
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) { fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
dbg!("picker cancel");
self.delegate.dismissed(cx); self.delegate.dismissed(cx);
} }

View file

@ -17,6 +17,7 @@ pub mod project_search;
pub(crate) mod search_bar; pub(crate) mod search_bar;
pub fn init(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) {
menu::init();
buffer_search::init(cx); buffer_search::init(cx);
project_search::init(cx); project_search::init(cx);
} }

View file

@ -1,42 +1,40 @@
use gpui::{div, AnyElement, Element, IntoElement, Render, ViewContext}; use gpui::{div, AnyElement, Element, IntoElement, Render, Subscription, ViewContext};
use settings::{Settings, SettingsStore}; use settings::SettingsStore;
use workspace::{item::ItemHandle, ui::Label, StatusItemView}; use workspace::{item::ItemHandle, ui::Label, StatusItemView};
use crate::{state::Mode, Vim, VimModeSetting}; use crate::{state::Mode, Vim};
pub struct ModeIndicator { pub struct ModeIndicator {
pub mode: Option<Mode>, pub mode: Option<Mode>,
// _subscription: Subscription, _subscriptions: Vec<Subscription>,
} }
impl ModeIndicator { impl ModeIndicator {
pub fn new(cx: &mut ViewContext<Self>) -> Self { pub fn new(cx: &mut ViewContext<Self>) -> Self {
cx.observe_global::<Vim>(|this, cx| this.set_mode(Vim::read(cx).state().mode, cx)) let _subscriptions = vec![
.detach(); 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| { let mut this = Self {
if VimModeSetting::get_global(cx).0 { mode: None,
mode_indicator.mode = cx _subscriptions,
.has_global::<Vim>() };
.then(|| cx.global::<Vim>().state().mode); this.update_mode(cx);
} else { this
mode_indicator.mode.take();
} }
})
.detach();
fn update_mode(&mut self, cx: &mut ViewContext<Self>) {
// Vim doesn't exist in some tests // Vim doesn't exist in some tests
let mode = cx if !cx.has_global::<Vim>() {
.has_global::<Vim>() return;
.then(|| { }
let vim = cx.global::<Vim>();
vim.enabled.then(|| vim.state().mode)
})
.flatten();
Self { let vim = Vim::read(cx);
mode, if vim.enabled {
// _subscription, self.mode = Some(vim.state().mode);
} else {
self.mode = None;
} }
} }

View file

@ -203,7 +203,6 @@ fn insert_after(_: &mut Workspace, _: &InsertAfter, cx: &mut ViewContext<Workspa
} }
fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) { fn insert_before(_: &mut Workspace, _: &InsertBefore, cx: &mut ViewContext<Workspace>) {
dbg!("insert before!");
Vim::update(cx, |vim, cx| { Vim::update(cx, |vim, cx| {
vim.start_recording(cx); vim.start_recording(cx);
vim.switch_mode(Mode::Insert, false, cx); vim.switch_mode(Mode::Insert, false, cx);

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
use editor::scroll::VERTICAL_SCROLL_MARGIN; use editor::{scroll::VERTICAL_SCROLL_MARGIN, test::editor_test_context::ContextHandle};
use indoc::indoc; use indoc::indoc;
use settings::SettingsStore; use settings::SettingsStore;
use std::{ use std::{
@ -7,7 +7,6 @@ use std::{
}; };
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use gpui::{geometry::vector::vec2f, ContextHandle};
use language::language_settings::{AllLanguageSettings, SoftWrap}; use language::language_settings::{AllLanguageSettings, SoftWrap};
use util::test::marked_text_offsets; use util::test::marked_text_offsets;
@ -151,19 +150,20 @@ impl<'a> NeovimBackedTestContext<'a> {
}) })
} }
pub async fn set_scroll_height(&mut self, rows: u32) { // todo!()
// match Zed's scrolling behavior // pub async fn set_scroll_height(&mut self, rows: u32) {
self.neovim // // match Zed's scrolling behavior
.set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN)) // self.neovim
.await; // .set_option(&format!("scrolloff={}", VERTICAL_SCROLL_MARGIN))
// +2 to account for the vim command UI at the bottom. // .await;
self.neovim.set_option(&format!("lines={}", rows + 2)).await; // // +2 to account for the vim command UI at the bottom.
let window = self.window; // self.neovim.set_option(&format!("lines={}", rows + 2)).await;
let line_height = // let window = self.window;
self.editor(|editor, cx| editor.style().text.line_height(cx.font_cache())); // 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); // window.simulate_resize(vec2f(1000., (rows as f32) * line_height), &mut self.cx);
} // }
pub async fn set_neovim_option(&mut self, option: &str) { pub async fn set_neovim_option(&mut self, option: &str) {
self.neovim.set_option(option).await; self.neovim.set_option(option).await;
@ -211,12 +211,7 @@ impl<'a> NeovimBackedTestContext<'a> {
pub async fn assert_shared_clipboard(&mut self, text: &str) { pub async fn assert_shared_clipboard(&mut self, text: &str) {
let neovim = self.neovim.read_register('"').await; let neovim = self.neovim.read_register('"').await;
let editor = self let editor = self.read_from_clipboard().unwrap().text().clone();
.platform()
.read_from_clipboard()
.unwrap()
.text()
.clone();
if text == neovim && text == editor { if text == neovim && text == editor {
return; return;

View file

@ -10,7 +10,7 @@ use async_compat::Compat;
#[cfg(feature = "neovim")] #[cfg(feature = "neovim")]
use async_trait::async_trait; use async_trait::async_trait;
#[cfg(feature = "neovim")] #[cfg(feature = "neovim")]
use gpui::keymap_matcher::Keystroke; use gpui::Keystroke;
#[cfg(feature = "neovim")] #[cfg(feature = "neovim")]
use language::Point; use language::Point;
@ -116,16 +116,24 @@ impl NeovimConnection {
keystroke.key = "lt".to_string() keystroke.key = "lt".to_string()
} }
let special = keystroke.shift let special = keystroke.modifiers.shift
|| keystroke.ctrl || keystroke.modifiers.control
|| keystroke.alt || keystroke.modifiers.alt
|| keystroke.cmd || keystroke.modifiers.command
|| keystroke.key.len() > 1; || keystroke.key.len() > 1;
let start = if special { "<" } else { "" }; let start = if special { "<" } else { "" };
let shift = if keystroke.shift { "S-" } else { "" }; let shift = if keystroke.modifiers.shift { "S-" } else { "" };
let ctrl = if keystroke.ctrl { "C-" } else { "" }; let ctrl = if keystroke.modifiers.control {
let alt = if keystroke.alt { "M-" } else { "" }; "C-"
let cmd = if keystroke.cmd { "D-" } else { "" }; } else {
""
};
let alt = if keystroke.modifiers.alt { "M-" } else { "" };
let cmd = if keystroke.modifiers.command {
"D-"
} else {
""
};
let end = if special { ">" } else { "" }; let end = if special { ">" } else { "" };
let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key); let key = format!("{start}{shift}{ctrl}{alt}{cmd}{}{end}", keystroke.key);

View file

@ -16,11 +16,25 @@ pub struct VimTestContext<'a> {
impl<'a> VimTestContext<'a> { impl<'a> VimTestContext<'a> {
pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> 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; let lsp = EditorLspTestContext::new_rust(Default::default(), cx).await;
Self::new_with_lsp(lsp, enabled) Self::new_with_lsp(lsp, enabled)
} }
pub async fn new_typescript(cx: &'a mut gpui::TestAppContext) -> VimTestContext<'a> { 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( Self::new_with_lsp(
EditorLspTestContext::new_typescript(Default::default(), cx).await, EditorLspTestContext::new_typescript(Default::default(), cx).await,
true, true,
@ -28,12 +42,6 @@ impl<'a> VimTestContext<'a> {
} }
pub fn new_with_lsp(mut cx: EditorLspTestContext<'a>, enabled: bool) -> 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(|cx| {
cx.update_global(|store: &mut SettingsStore, cx| { cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled)); store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
@ -65,9 +73,11 @@ impl<'a> VimTestContext<'a> {
pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R pub fn update_view<F, T, R>(&mut self, view: View<T>, update: F) -> R
where 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() .unwrap()
} }
@ -75,8 +85,7 @@ impl<'a> VimTestContext<'a> {
where where
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T, F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
{ {
self.update_window(self.window, |_, cx| self.cx.workspace.update(cx, update)) self.cx.update_workspace(update)
.unwrap()
} }
pub fn enable_vim(&mut self) { pub fn enable_vim(&mut self) {

View file

@ -116,45 +116,43 @@ fn register(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
visual::register(workspace, cx); visual::register(workspace, cx);
} }
pub fn observe_keystrokes(_: &mut WindowContext) { pub fn observe_keystrokes(cx: &mut WindowContext) {
// todo!() 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()));
// cx.observe_keystrokes(|_keystroke, result, handled_by, cx| { if vim.workspace_state.stop_recording_after_next_action {
// if result == &MatchResult::Pending { vim.workspace_state.recording = false;
// return true; vim.workspace_state.stop_recording_after_next_action = false;
// } }
// 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 { // Keystroke is handled by the vim system, so continue forward
// vim.workspace_state.recording = false; if action.name().starts_with("vim::") {
// vim.workspace_state.stop_recording_after_next_action = false; return;
// } }
// } }
// });
// // Keystroke is handled by the vim system, so continue forward Vim::update(cx, |vim, cx| match vim.active_operator() {
// if handled_by.namespace() == "vim" { Some(
// return true; Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace,
// } ) => {}
// } Some(_) => {
vim.clear_operator(cx);
// Vim::update(cx, |vim, cx| match vim.active_operator() { }
// Some( _ => {}
// Operator::FindForward { .. } | Operator::FindBackward { .. } | Operator::Replace, });
// ) => {} })
// Some(_) => { .detach()
// vim.clear_operator(cx);
// }
// _ => {}
// });
// true
// })
// .detach()
} }
#[derive(Default)] #[derive(Default)]