Skip to content

Commit

Permalink
Added facility to register commands that do not need editor update
Browse files Browse the repository at this point in the history
Summary: An editor update is a costly operation, possibly taking up to 10ms. There are situations where your Lexical command's handler does not need to be inside an update block. A big example is logging. This diff allows command handlers to opt out of the auto-update-wrapper functionality. When applied to logging plugins, this can produce a drastic speedup.

Reviewed By: Fetz

Differential Revision: D53517219

fbshipit-source-id: ff525cf6010e37d3ed9fdcdc5f1e758a06d0243e
  • Loading branch information
Amy Worrall authored and facebook-github-bot committed Feb 8, 2024
1 parent 1a2d0a5 commit de10ddf
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 5 deletions.
6 changes: 5 additions & 1 deletion Lexical/Core/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,11 @@ struct Listeners {
var errors: [UUID: ErrorListener] = [:]
}

public typealias Commands = [CommandType: [CommandPriority: [UUID: CommandListener]]]
public struct CommandListenerWithMetadata {
let listener: CommandListener
let shouldWrapInUpdateBlock: Bool
}
public typealias Commands = [CommandType: [CommandPriority: [UUID: CommandListenerWithMetadata]]]

// enum is used in setting theme
public enum TextTransform: String {
Expand Down
6 changes: 4 additions & 2 deletions Lexical/Core/Editor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ public class Editor: NSObject {
/// - listener: The code to run when the command is dispatched.
/// - priority: The priority for your handler. Higher priority handlers run before lower priority handlers.
/// - Returns: A closure to remove the command handler.
public func registerCommand(type: CommandType, listener: @escaping CommandListener, priority: CommandPriority = CommandPriority.Editor) -> RemovalHandler {
public func registerCommand(type: CommandType, listener: @escaping CommandListener, priority: CommandPriority = CommandPriority.Editor, shouldWrapInUpdateBlock: Bool = true) -> RemovalHandler {
let uuid = UUID()

if self.commands[type] == nil {
Expand All @@ -282,7 +282,9 @@ public class Editor: NSObject {
)
}

self.commands[type]?[priority]?[uuid] = listener
let wrapper = CommandListenerWithMetadata(listener: listener, shouldWrapInUpdateBlock: shouldWrapInUpdateBlock)

self.commands[type]?[priority]?[uuid] = wrapper

return { [weak self] in
guard let self else { return }
Expand Down
14 changes: 12 additions & 2 deletions Lexical/Core/Updates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public func triggerCommandListeners(activeEditor: Editor, type: CommandType, pay
}

// TODO: handle throws
for listener in listeners {
for wrapper in listeners {
let listener = wrapper.listener
if listener(payload) {
handled = true
return
Expand All @@ -87,8 +88,17 @@ public func triggerCommandListeners(activeEditor: Editor, type: CommandType, pay
}
}

var shouldWrapInUpdateBlock = false
for p in (listenersInPriorityOrder ?? [:]).values {
for metadata in p.values {
if metadata.shouldWrapInUpdateBlock {
shouldWrapInUpdateBlock = true
}
}
}

do {
if !activeEditor.isUpdating {
if shouldWrapInUpdateBlock && !activeEditor.isUpdating {
try activeEditor.update(closure)
} else {
try closure()
Expand Down

0 comments on commit de10ddf

Please sign in to comment.