Skip to content

Commit 444c515

Browse files
author
ilimei
committed
✨ feat: Optimize plugin command priority
1 parent 3762e1c commit 444c515

6 files changed

Lines changed: 242 additions & 107 deletions

File tree

src/editor-kernel/kernel.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import EventEmitter from 'eventemitter3';
22
import {
3+
COMMAND_PRIORITY_CRITICAL,
4+
CommandListener,
5+
CommandListenerPriority,
36
CommandPayloadType,
47
DecoratorNode,
58
LexicalCommand,
@@ -14,6 +17,7 @@ import defaultLocale from '@/locale';
1417
import DataSource from './data-source';
1518
import { registerEvent } from './event';
1619
import {
20+
Commands,
1721
IEditor,
1822
IEditorKernel,
1923
IEditorPlugin,
@@ -228,4 +232,62 @@ export class Kernel extends EventEmitter implements IEditorKernel {
228232
}
229233
return translation;
230234
}
235+
236+
private _commands: Commands = new Map();
237+
private _commandsClean: Map<LexicalCommand<unknown>, () => void> = new Map();
238+
239+
registerHighCommand<P>(
240+
command: LexicalCommand<P>,
241+
listener: CommandListener<P>,
242+
priority: CommandListenerPriority,
243+
): () => void {
244+
const lexicalEditor = this.editor;
245+
if (!lexicalEditor) {
246+
throw new Error('Editor is not initialized.');
247+
}
248+
const commandsMap = this._commands;
249+
250+
if (!commandsMap.has(command)) {
251+
commandsMap.set(command, [new Set(), new Set(), new Set(), new Set(), new Set()]);
252+
this._commandsClean.set(
253+
command,
254+
lexicalEditor.registerCommand(
255+
command,
256+
(payload: any) => {
257+
for (let i = 4; i >= 0; i--) {
258+
const listenerInPriorityOrder = this._commands.get(command);
259+
260+
if (listenerInPriorityOrder !== undefined) {
261+
const listenersSet = listenerInPriorityOrder[i];
262+
for (const listener of listenersSet) {
263+
if (listener(payload, lexicalEditor)) {
264+
return true;
265+
}
266+
}
267+
}
268+
}
269+
return false;
270+
},
271+
COMMAND_PRIORITY_CRITICAL,
272+
),
273+
);
274+
}
275+
276+
const listenersInPriorityOrder = commandsMap.get(command);
277+
278+
if (listenersInPriorityOrder === undefined) {
279+
return () => {};
280+
}
281+
282+
const listeners = listenersInPriorityOrder[priority];
283+
listeners.add(listener as CommandListener<unknown>);
284+
return () => {
285+
listeners.delete(listener as CommandListener<unknown>);
286+
287+
if (listenersInPriorityOrder.every((listenersSet) => listenersSet.size === 0)) {
288+
commandsMap.delete(command);
289+
this._commandsClean.get(command)?.();
290+
}
291+
};
292+
}
231293
}

src/editor-kernel/types.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type {
2+
CommandListener,
23
CommandPayloadType,
34
DecoratorNode,
45
LexicalCommand,
@@ -8,6 +9,17 @@ import type {
89

910
import type DataSource from './data-source';
1011

12+
export type CommandListenerPriority = 0 | 1 | 2 | 3 | 4;
13+
14+
export const COMMAND_PRIORITY_EDITOR = 0;
15+
export const COMMAND_PRIORITY_LOW = 1;
16+
export const COMMAND_PRIORITY_NORMAL = 2;
17+
export const COMMAND_PRIORITY_HIGH = 3;
18+
export const COMMAND_PRIORITY_CRITICAL = 4;
19+
20+
export type Commands = Map<LexicalCommand<unknown>, Array<Set<CommandListener<unknown>>>>;
21+
export type CommandsClean = Map<LexicalCommand<unknown>, () => void>;
22+
1123
/**
1224
* Internationalization key type declaration, plugins can extend through declaration merging
1325
*/
@@ -114,6 +126,35 @@ export interface IEditor {
114126
* @param listener
115127
*/
116128
once<T extends keyof IKernelEventMap>(event: T, listener: IKernelEventMap[T]): this;
129+
130+
/**
131+
* Extends the priority level of Lexical commands.
132+
* Registers a listener that triggers when the provided command is dispatched
133+
* via {@link LexicalEditor.dispatch}. The listener is triggered based on its priority.
134+
* Listeners with higher priority can "intercept" commands and prevent them
135+
* from propagating to other handlers by returning true.
136+
*
137+
* Listeners are always invoked within {@link LexicalEditor.update} and can call dollar functions.
138+
*
139+
* Listeners registered at the same priority level will deterministically run
140+
* in the order of registration.
141+
*
142+
* @param command - The command that triggers the callback.
143+
* @param listener - The function executed when the command is dispatched.
144+
* @param priority - The relative priority of the listener. 0 | 1 | 2 | 3 | 4
145+
* (or {@link COMMAND_PRIORITY_EDITOR} |
146+
* {@link COMMAND_PRIORITY_LOW} |
147+
* {@link COMMAND_PRIORITY_NORMAL} |
148+
* {@link COMMAND_PRIORITY_HIGH} |
149+
* {@link COMMAND_PRIORITY_CRITICAL})
150+
* @returns A teardown function to clean up the listener.
151+
*/
152+
registerHighCommand<P>(
153+
command: LexicalCommand<P>,
154+
listener: CommandListener<P>,
155+
priority: CommandListenerPriority,
156+
): () => void;
157+
117158
/**
118159
* Register internationalization text
119160
* @param locale Internationalization text object
@@ -127,6 +168,7 @@ export interface IEditor {
127168
* Register multiple editor plugins
128169
*/
129170
registerPlugins(plugins: Array<IPlugin>): IEditor;
171+
130172
/**
131173
* Get editor Service, usually provided by plugins to extend certain functionalities
132174
* @param serviceId
@@ -177,6 +219,7 @@ export interface IEditorKernel extends IEditor {
177219
* @param dataSource
178220
*/
179221
registerDataSource(dataSource: DataSource): void;
222+
180223
/**
181224
* Register editor node decorator
182225
* @param name
@@ -186,6 +229,7 @@ export interface IEditorKernel extends IEditor {
186229
name: string,
187230
decorator: (_node: DecoratorNode<any>, _editor: LexicalEditor) => any,
188231
): void;
232+
189233
/**
190234
* Register Lexical Node
191235
* @param nodes
@@ -197,6 +241,7 @@ export interface IEditorKernel extends IEditor {
197241
* @param service
198242
*/
199243
registerService<T>(serviceId: IServiceID<T>, service: T): void;
244+
200245
/**
201246
* Register theme
202247
* @param themes

src/plugins/common/plugin/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export const CommonPlugin: IEditorPluginConstructor<CommonPluginOptions> = class
249249
registerDragonSupport(editor),
250250
registerHistory(editor, createEmptyHistoryState(), 300),
251251
registerHeaderBackspace(editor),
252-
registerRichKeydown(editor),
252+
registerRichKeydown(editor, this.kernel),
253253
registerCommands(editor),
254254
);
255255
}

src/plugins/common/plugin/register.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
$isRootOrShadowRoot,
1515
$isTextNode,
1616
$setSelection,
17-
COMMAND_PRIORITY_CRITICAL,
1817
COMMAND_PRIORITY_EDITOR,
1918
COMMAND_PRIORITY_NORMAL,
2019
ElementNode,
@@ -32,6 +31,7 @@ import {
3231
} from 'lexical';
3332

3433
import { CONTROL_OR_META, CONTROL_OR_META_AND_SHIFT } from '@/common/sys';
34+
import { IEditor } from '@/types';
3535

3636
function resolveElement(
3737
element: ElementNode,
@@ -160,9 +160,9 @@ export function registerHeaderBackspace(editor: LexicalEditor) {
160160
);
161161
}
162162

163-
export function registerRichKeydown(editor: LexicalEditor) {
163+
export function registerRichKeydown(editor: LexicalEditor, kernel: IEditor) {
164164
return mergeRegister(
165-
editor.registerCommand(
165+
kernel.registerHighCommand(
166166
KEY_DOWN_COMMAND,
167167
(payload) => {
168168
// ctrl + shift + x
@@ -184,7 +184,7 @@ export function registerRichKeydown(editor: LexicalEditor) {
184184
},
185185
COMMAND_PRIORITY_EDITOR,
186186
),
187-
editor.registerCommand(
187+
kernel.registerHighCommand(
188188
KEY_ARROW_UP_COMMAND,
189189
(event) => {
190190
const selection = $getSelection();
@@ -215,9 +215,9 @@ export function registerRichKeydown(editor: LexicalEditor) {
215215
}
216216
return false;
217217
},
218-
COMMAND_PRIORITY_NORMAL,
218+
COMMAND_PRIORITY_EDITOR,
219219
),
220-
editor.registerCommand<KeyboardEvent>(
220+
kernel.registerHighCommand<KeyboardEvent>(
221221
KEY_ARROW_DOWN_COMMAND,
222222
(event) => {
223223
const selection = $getSelection();
@@ -263,9 +263,9 @@ export function registerRichKeydown(editor: LexicalEditor) {
263263
}
264264
return false;
265265
},
266-
COMMAND_PRIORITY_CRITICAL,
266+
COMMAND_PRIORITY_EDITOR,
267267
),
268-
editor.registerCommand(
268+
kernel.registerHighCommand(
269269
KEY_ARROW_RIGHT_COMMAND,
270270
(event) => {
271271
const selection = $getSelection();
@@ -296,7 +296,7 @@ export function registerRichKeydown(editor: LexicalEditor) {
296296
}
297297
return false;
298298
},
299-
COMMAND_PRIORITY_CRITICAL,
299+
COMMAND_PRIORITY_EDITOR,
300300
),
301301
);
302302
}

0 commit comments

Comments
 (0)