Fix keystroke observer leak in vim crate (#17913)
Release Notes: - Fixed a performance problem that happened when using vim mode after opening and closing many editors Co-authored-by: Antonio <antonio@zed.dev> Co-authored-by: Nathan <nathan@zed.dev>
This commit is contained in:
parent
67f149a4bc
commit
243629cce8
3 changed files with 79 additions and 43 deletions
|
@ -204,7 +204,8 @@ impl App {
|
||||||
|
|
||||||
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>;
|
pub(crate) type KeystrokeObserver =
|
||||||
|
Box<dyn FnMut(&KeystrokeEvent, &mut WindowContext) -> bool + '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>;
|
||||||
|
@ -1050,7 +1051,7 @@ impl AppContext {
|
||||||
/// and that this API will not be invoked if the event's propagation is stopped.
|
/// and that this API will not be invoked if the event's propagation is stopped.
|
||||||
pub fn observe_keystrokes(
|
pub fn observe_keystrokes(
|
||||||
&mut self,
|
&mut self,
|
||||||
f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
|
mut f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
|
||||||
) -> Subscription {
|
) -> Subscription {
|
||||||
fn inner(
|
fn inner(
|
||||||
keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
|
keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
|
||||||
|
@ -1060,7 +1061,14 @@ impl AppContext {
|
||||||
activate();
|
activate();
|
||||||
subscription
|
subscription
|
||||||
}
|
}
|
||||||
inner(&mut self.keystroke_observers, Box::new(f))
|
|
||||||
|
inner(
|
||||||
|
&mut self.keystroke_observers,
|
||||||
|
Box::new(move |event, cx| {
|
||||||
|
f(event, cx);
|
||||||
|
true
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register key bindings.
|
/// Register key bindings.
|
||||||
|
|
|
@ -4,16 +4,17 @@ use crate::{
|
||||||
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
|
Context, Corners, CursorStyle, Decorations, DevicePixels, DispatchActionListener,
|
||||||
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter,
|
||||||
FileDropEvent, Flatten, FontId, GPUSpecs, Global, GlobalElementId, GlyphId, Hsla, InputHandler,
|
FileDropEvent, Flatten, FontId, GPUSpecs, Global, GlobalElementId, GlyphId, Hsla, InputHandler,
|
||||||
IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent, LayoutId,
|
IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, Keystroke, KeystrokeEvent,
|
||||||
LineLayoutIndex, Model, ModelContext, Modifiers, ModifiersChangedEvent, MonochromeSprite,
|
KeystrokeObserver, LayoutId, LineLayoutIndex, Model, ModelContext, Modifiers,
|
||||||
MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
|
ModifiersChangedEvent, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent, MouseUpEvent,
|
||||||
PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
|
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
|
||||||
PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams,
|
PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams,
|
||||||
Replay, ResizeEdge, ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style,
|
RenderImage, RenderImageParams, RenderSvgParams, Replay, ResizeEdge, ScaledPixels, Scene,
|
||||||
SubscriberSet, Subscription, TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement,
|
Shadow, SharedString, Size, StrikethroughStyle, Style, SubscriberSet, Subscription,
|
||||||
TransformationMatrix, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
TaffyLayoutEngine, Task, TextStyle, TextStyleRefinement, TransformationMatrix, Underline,
|
||||||
WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControls, WindowDecorations,
|
UnderlineStyle, View, VisualContext, WeakView, WindowAppearance, WindowBackgroundAppearance,
|
||||||
WindowOptions, WindowParams, WindowTextSystem, SUBPIXEL_VARIANTS,
|
WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem,
|
||||||
|
SUBPIXEL_VARIANTS,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use collections::{FxHashMap, FxHashSet};
|
use collections::{FxHashMap, FxHashSet};
|
||||||
|
@ -1043,8 +1044,7 @@ impl<'a> WindowContext<'a> {
|
||||||
action: action.as_ref().map(|action| action.boxed_clone()),
|
action: action.as_ref().map(|action| action.boxed_clone()),
|
||||||
},
|
},
|
||||||
self,
|
self,
|
||||||
);
|
)
|
||||||
true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4250,6 +4250,36 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||||
subscription
|
subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Register a callback to be invoked when a keystroke is received by the application
|
||||||
|
/// in any window. Note that this fires after all other action and event mechanisms have resolved
|
||||||
|
/// and that this API will not be invoked if the event's propagation is stopped.
|
||||||
|
pub fn observe_keystrokes(
|
||||||
|
&mut self,
|
||||||
|
mut f: impl FnMut(&mut V, &KeystrokeEvent, &mut ViewContext<V>) + 'static,
|
||||||
|
) -> Subscription {
|
||||||
|
fn inner(
|
||||||
|
keystroke_observers: &mut SubscriberSet<(), KeystrokeObserver>,
|
||||||
|
handler: KeystrokeObserver,
|
||||||
|
) -> Subscription {
|
||||||
|
let (subscription, activate) = keystroke_observers.insert((), handler);
|
||||||
|
activate();
|
||||||
|
subscription
|
||||||
|
}
|
||||||
|
|
||||||
|
let view = self.view.downgrade();
|
||||||
|
inner(
|
||||||
|
&mut self.keystroke_observers,
|
||||||
|
Box::new(move |event, cx| {
|
||||||
|
if let Some(view) = view.upgrade() {
|
||||||
|
view.update(cx, |view, cx| f(view, event, cx));
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a callback to be invoked when the window's pending input changes.
|
/// Register a callback to be invoked when the window's pending input changes.
|
||||||
pub fn observe_pending_input(
|
pub fn observe_pending_input(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -24,7 +24,7 @@ use editor::{
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, impl_actions, Action, AppContext, Entity, EventEmitter, KeyContext, KeystrokeEvent,
|
actions, impl_actions, Action, AppContext, Entity, EventEmitter, KeyContext, KeystrokeEvent,
|
||||||
Render, View, ViewContext, WeakView,
|
Render, Subscription, View, ViewContext, WeakView,
|
||||||
};
|
};
|
||||||
use insert::NormalBefore;
|
use insert::NormalBefore;
|
||||||
use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
|
use language::{CursorShape, Point, Selection, SelectionGoal, TransactionId};
|
||||||
|
@ -166,6 +166,8 @@ pub(crate) struct Vim {
|
||||||
pub search: SearchState,
|
pub search: SearchState,
|
||||||
|
|
||||||
editor: WeakView<Editor>,
|
editor: WeakView<Editor>,
|
||||||
|
|
||||||
|
_subscriptions: Vec<Subscription>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack: Vim intercepts events dispatched to a window and updates the view in response.
|
// Hack: Vim intercepts events dispatched to a window and updates the view in response.
|
||||||
|
@ -189,36 +191,32 @@ impl Vim {
|
||||||
pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
|
pub fn new(cx: &mut ViewContext<Editor>) -> View<Self> {
|
||||||
let editor = cx.view().clone();
|
let editor = cx.view().clone();
|
||||||
|
|
||||||
cx.new_view(|cx: &mut ViewContext<Vim>| {
|
cx.new_view(|cx| Vim {
|
||||||
cx.subscribe(&editor, |vim, _, event, cx| {
|
mode: Mode::Normal,
|
||||||
vim.handle_editor_event(event, cx)
|
last_mode: Mode::Normal,
|
||||||
})
|
pre_count: None,
|
||||||
.detach();
|
post_count: None,
|
||||||
|
operator_stack: Vec::new(),
|
||||||
|
replacements: Vec::new(),
|
||||||
|
|
||||||
let listener = cx.listener(Vim::observe_keystrokes);
|
marks: HashMap::default(),
|
||||||
cx.observe_keystrokes(listener).detach();
|
stored_visual_mode: None,
|
||||||
|
change_list: Vec::new(),
|
||||||
|
change_list_position: None,
|
||||||
|
current_tx: None,
|
||||||
|
current_anchor: None,
|
||||||
|
undo_modes: HashMap::default(),
|
||||||
|
|
||||||
Vim {
|
selected_register: None,
|
||||||
mode: Mode::Normal,
|
search: SearchState::default(),
|
||||||
last_mode: Mode::Normal,
|
|
||||||
pre_count: None,
|
|
||||||
post_count: None,
|
|
||||||
operator_stack: Vec::new(),
|
|
||||||
replacements: Vec::new(),
|
|
||||||
|
|
||||||
marks: HashMap::default(),
|
editor: editor.downgrade(),
|
||||||
stored_visual_mode: None,
|
_subscriptions: vec![
|
||||||
change_list: Vec::new(),
|
cx.observe_keystrokes(Self::observe_keystrokes),
|
||||||
change_list_position: None,
|
cx.subscribe(&editor, |this, _, event, cx| {
|
||||||
current_tx: None,
|
this.handle_editor_event(event, cx)
|
||||||
current_anchor: None,
|
}),
|
||||||
undo_modes: HashMap::default(),
|
],
|
||||||
|
|
||||||
selected_register: None,
|
|
||||||
search: SearchState::default(),
|
|
||||||
|
|
||||||
editor: editor.downgrade(),
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue