mirror of
https://github.com/facebook/lexical.git
synced 2025-08-06 16:39:33 +08:00
Use for...of for performance optimizations (#1509)
* Use for...of for performance optimizations * Fix ignores * Remove console times
This commit is contained in:

committed by
acywatson

parent
7dbba00306
commit
db26e25d7a
@ -58,7 +58,6 @@ module.exports = {
|
|||||||
// import helps to configure simple-import-sort
|
// import helps to configure simple-import-sort
|
||||||
'import',
|
'import',
|
||||||
'jest',
|
'jest',
|
||||||
'no-for-of-loops',
|
|
||||||
'no-function-declare-after-return',
|
'no-function-declare-after-return',
|
||||||
'react',
|
'react',
|
||||||
'no-only-tests',
|
'no-only-tests',
|
||||||
@ -109,10 +108,6 @@ module.exports = {
|
|||||||
|
|
||||||
'no-debugger': ERROR,
|
'no-debugger': ERROR,
|
||||||
|
|
||||||
// Prevent for...of loops because they require a Symbol polyfill.
|
|
||||||
// You can disable this rule for code that isn't shipped (e.g. build scripts and tests).
|
|
||||||
'no-for-of-loops/no-for-of-loops': ERROR,
|
|
||||||
|
|
||||||
// Prevent function declarations after return statements
|
// Prevent function declarations after return statements
|
||||||
'no-function-declare-after-return/no-function-declare-after-return': ERROR,
|
'no-function-declare-after-return/no-function-declare-after-return': ERROR,
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable no-for-of-loops/no-for-of-loops */
|
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const {markdown} = require('danger');
|
const {markdown} = require('danger');
|
||||||
|
@ -108,7 +108,6 @@
|
|||||||
"eslint-plugin-flowtype": "^8.0.3",
|
"eslint-plugin-flowtype": "^8.0.3",
|
||||||
"eslint-plugin-jest": "^24.4.0",
|
"eslint-plugin-jest": "^24.4.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-no-for-of-loops": "^1.0.1",
|
|
||||||
"eslint-plugin-no-function-declare-after-return": "^1.1.0",
|
"eslint-plugin-no-function-declare-after-return": "^1.1.0",
|
||||||
"eslint-plugin-no-only-tests": "^2.6.0",
|
"eslint-plugin-no-only-tests": "^2.6.0",
|
||||||
"eslint-plugin-react": "^7.24.0",
|
"eslint-plugin-react": "^7.24.0",
|
||||||
|
@ -174,9 +174,8 @@ function $createNodesFromDOM(
|
|||||||
currentLexicalNode = transformOutput.node;
|
currentLexicalNode = transformOutput.node;
|
||||||
if (currentLexicalNode !== null) {
|
if (currentLexicalNode !== null) {
|
||||||
lexicalNodes.push(currentLexicalNode);
|
lexicalNodes.push(currentLexicalNode);
|
||||||
const forChildFunctions = Array.from(forChildMap.values());
|
for (const [, forChildFunction] of forChildMap) {
|
||||||
for (let i = 0; i < forChildFunctions.length; i++) {
|
forChildFunction(currentLexicalNode);
|
||||||
forChildFunctions[i](currentLexicalNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,6 @@ function isIndentPermitted(maxDepth: number): boolean {
|
|||||||
|
|
||||||
let totalDepth = 0;
|
let totalDepth = 0;
|
||||||
|
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const elementNode of elementNodesInSelection) {
|
for (const elementNode of elementNodesInSelection) {
|
||||||
if ($isListNode(elementNode)) {
|
if ($isListNode(elementNode)) {
|
||||||
totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth);
|
totalDepth = Math.max($getListDepth(elementNode) + 1, totalDepth);
|
||||||
|
@ -11,7 +11,6 @@ import {DEFAULT_SETTINGS} from './appSettings';
|
|||||||
|
|
||||||
// override default options with query parameters if any
|
// override default options with query parameters if any
|
||||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const param of Object.keys(DEFAULT_SETTINGS)) {
|
for (const param of Object.keys(DEFAULT_SETTINGS)) {
|
||||||
if (urlSearchParams.has(param)) {
|
if (urlSearchParams.has(param)) {
|
||||||
try {
|
try {
|
||||||
|
@ -88,7 +88,6 @@ export default function TablePlugin(): React$Node {
|
|||||||
const tableSelections = new Map<NodeKey, TableSelection>();
|
const tableSelections = new Map<NodeKey, TableSelection>();
|
||||||
|
|
||||||
return editor.addListener('mutation', TableNode, (nodeMutations) => {
|
return editor.addListener('mutation', TableNode, (nodeMutations) => {
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const [nodeKey, mutation] of nodeMutations) {
|
for (const [nodeKey, mutation] of nodeMutations) {
|
||||||
if (mutation === 'created') {
|
if (mutation === 'created') {
|
||||||
editor.update(() => {
|
editor.update(() => {
|
||||||
|
@ -37,7 +37,6 @@ describe('LexicalNodeHelpers tests', () => {
|
|||||||
jest.restoreAllMocks();
|
jest.restoreAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const plugin of ['PlainTextPlugin', 'RichTextPlugin']) {
|
for (const plugin of ['PlainTextPlugin', 'RichTextPlugin']) {
|
||||||
it(`${plugin} custom initialEditorState`, async () => {
|
it(`${plugin} custom initialEditorState`, async () => {
|
||||||
let editor;
|
let editor;
|
||||||
|
@ -100,7 +100,6 @@ function findOffset(
|
|||||||
if (typeof Segmenter === 'function') {
|
if (typeof Segmenter === 'function') {
|
||||||
const segmenter = new Segmenter();
|
const segmenter = new Segmenter();
|
||||||
const graphemes = segmenter.segment(text);
|
const graphemes = segmenter.segment(text);
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const {segment: grapheme} of graphemes) {
|
for (const {segment: grapheme} of graphemes) {
|
||||||
const nextOffset = offset + strlen(grapheme);
|
const nextOffset = offset + strlen(grapheme);
|
||||||
if (nextOffset > maxCharacters) {
|
if (nextOffset > maxCharacters) {
|
||||||
|
@ -56,28 +56,23 @@ export type HistoryState = {
|
|||||||
|
|
||||||
function getDirtyNodes(
|
function getDirtyNodes(
|
||||||
editorState: EditorState,
|
editorState: EditorState,
|
||||||
dirtyLeavesSet: Set<NodeKey>,
|
dirtyLeaves: Set<NodeKey>,
|
||||||
dirtyElementsSet: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
|
dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
|
||||||
): Array<LexicalNode> {
|
): Array<LexicalNode> {
|
||||||
const dirtyLeaves = Array.from(dirtyLeavesSet);
|
|
||||||
const dirtyElements = Array.from(dirtyElementsSet);
|
|
||||||
const nodeMap = editorState._nodeMap;
|
const nodeMap = editorState._nodeMap;
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
|
|
||||||
for (let i = 0; i < dirtyLeaves.length; i++) {
|
for (const dirtyLeafKey of dirtyLeaves) {
|
||||||
const dirtyLeafKey = dirtyLeaves[i];
|
|
||||||
const dirtyLeaf = nodeMap.get(dirtyLeafKey);
|
const dirtyLeaf = nodeMap.get(dirtyLeafKey);
|
||||||
if (dirtyLeaf !== undefined) {
|
if (dirtyLeaf !== undefined) {
|
||||||
nodes.push(dirtyLeaf);
|
nodes.push(dirtyLeaf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < dirtyElements.length; i++) {
|
for (const [dirtyElementKey, intentionallyMarkedAsDirty] of dirtyElements) {
|
||||||
const intentionallyMarkedAsDirty = dirtyElements[i][1];
|
|
||||||
if (!intentionallyMarkedAsDirty) {
|
if (!intentionallyMarkedAsDirty) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const dirtyElementKey = dirtyElements[i][0];
|
|
||||||
const dirtyElement = nodeMap.get(dirtyElementKey);
|
const dirtyElement = nodeMap.get(dirtyElementKey);
|
||||||
if (dirtyElement !== undefined && !$isRootNode(dirtyElement)) {
|
if (dirtyElement !== undefined && !$isRootNode(dirtyElement)) {
|
||||||
nodes.push(dirtyElement);
|
nodes.push(dirtyElement);
|
||||||
|
@ -241,10 +241,7 @@ export function syncLexicalDecoratorMapToYjs(
|
|||||||
yjsMap: YMap,
|
yjsMap: YMap,
|
||||||
): void {
|
): void {
|
||||||
const internalMap = decoratorMap._map;
|
const internalMap = decoratorMap._map;
|
||||||
const keys = Array.from(internalMap.keys());
|
for (const [key] of internalMap) {
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
|
||||||
const key = keys[i];
|
|
||||||
syncLexicalDecoratorMapKeyToYjs(
|
syncLexicalDecoratorMapKeyToYjs(
|
||||||
binding,
|
binding,
|
||||||
collabNode,
|
collabNode,
|
||||||
|
@ -75,15 +75,10 @@ export function $garbageCollectDetachedNodes(
|
|||||||
dirtyLeaves: Set<NodeKey>,
|
dirtyLeaves: Set<NodeKey>,
|
||||||
dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
|
dirtyElements: Map<NodeKey, IntentionallyMarkedAsDirtyElement>,
|
||||||
): void {
|
): void {
|
||||||
const dirtyLeavesArr = Array.from(dirtyLeaves);
|
|
||||||
const dirtyLeavesLength = dirtyLeavesArr.length;
|
|
||||||
const dirtyElementsArr = Array.from(dirtyElements);
|
|
||||||
const dirtyElementsLength = dirtyElementsArr.length;
|
|
||||||
const prevNodeMap = prevEditorState._nodeMap;
|
const prevNodeMap = prevEditorState._nodeMap;
|
||||||
const nodeMap = editorState._nodeMap;
|
const nodeMap = editorState._nodeMap;
|
||||||
|
|
||||||
for (let i = 0; i < dirtyLeavesLength; i++) {
|
for (const nodeKey of dirtyLeaves) {
|
||||||
const nodeKey = dirtyLeavesArr[i];
|
|
||||||
const node = nodeMap.get(nodeKey);
|
const node = nodeMap.get(nodeKey);
|
||||||
|
|
||||||
if (node !== undefined && !node.isAttached()) {
|
if (node !== undefined && !node.isAttached()) {
|
||||||
@ -94,10 +89,8 @@ export function $garbageCollectDetachedNodes(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < dirtyElementsLength; i++) {
|
for (const [nodeKey] of dirtyElements) {
|
||||||
const nodeKey = dirtyElementsArr[i][0];
|
|
||||||
const node = nodeMap.get(nodeKey);
|
const node = nodeMap.get(nodeKey);
|
||||||
|
|
||||||
if (node !== undefined) {
|
if (node !== undefined) {
|
||||||
// Garbage collect node and its children if they exist
|
// Garbage collect node and its children if they exist
|
||||||
if (!node.isAttached()) {
|
if (!node.isAttached()) {
|
||||||
|
@ -196,10 +196,7 @@ export function $flushMutations(
|
|||||||
// is Lexical's "current" editor state. This is basically like
|
// is Lexical's "current" editor state. This is basically like
|
||||||
// an internal revert on the DOM.
|
// an internal revert on the DOM.
|
||||||
if (badDOMTargets.size > 0) {
|
if (badDOMTargets.size > 0) {
|
||||||
const entries = Array.from(badDOMTargets.entries());
|
for (const [targetDOM, targetNode] of badDOMTargets) {
|
||||||
for (let i = 0; i < entries.length; i++) {
|
|
||||||
const [targetDOM, targetNode] = entries[i];
|
|
||||||
|
|
||||||
if ($isElementNode(targetNode)) {
|
if ($isElementNode(targetNode)) {
|
||||||
const childKeys = targetNode.__children;
|
const childKeys = targetNode.__children;
|
||||||
let currentDOM = targetDOM.firstChild;
|
let currentDOM = targetDOM.firstChild;
|
||||||
|
@ -285,10 +285,10 @@ export class NodeSelection implements BaseSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNodes(): Array<LexicalNode> {
|
getNodes(): Array<LexicalNode> {
|
||||||
const objects = Array.from(this._nodes);
|
const objects = this._nodes;
|
||||||
const nodes = [];
|
const nodes = [];
|
||||||
for (let i = 0; i < objects.length; i++) {
|
for (const object of objects) {
|
||||||
const node = $getNodeByKey(objects[i]);
|
const node = $getNodeByKey(object);
|
||||||
if (node !== null) {
|
if (node !== null) {
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
}
|
}
|
||||||
|
@ -141,10 +141,7 @@ function $normalizeAllDirtyTextNodes(
|
|||||||
): void {
|
): void {
|
||||||
const dirtyLeaves = editor._dirtyLeaves;
|
const dirtyLeaves = editor._dirtyLeaves;
|
||||||
const nodeMap = editorState._nodeMap;
|
const nodeMap = editorState._nodeMap;
|
||||||
const dirtyLeavesLength = dirtyLeaves.size;
|
for (const nodeKey of dirtyLeaves) {
|
||||||
const dDirtyLeavesArr = Array.from(dirtyLeaves);
|
|
||||||
for (let i = 0; i < dirtyLeavesLength; i++) {
|
|
||||||
const nodeKey = dDirtyLeavesArr[i];
|
|
||||||
const node = nodeMap.get(nodeKey);
|
const node = nodeMap.get(nodeKey);
|
||||||
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
||||||
$normalizeTextNode(node);
|
$normalizeTextNode(node);
|
||||||
@ -183,9 +180,7 @@ function $applyAllTransforms(
|
|||||||
if (untransformedDirtyLeavesLength > 0) {
|
if (untransformedDirtyLeavesLength > 0) {
|
||||||
// We leverage editor._dirtyLeaves to track the new dirty leaves after the transforms
|
// We leverage editor._dirtyLeaves to track the new dirty leaves after the transforms
|
||||||
editor._dirtyLeaves = new Set();
|
editor._dirtyLeaves = new Set();
|
||||||
const untransformedDirtyLeavesArr = Array.from(untransformedDirtyLeaves);
|
for (const nodeKey of untransformedDirtyLeaves) {
|
||||||
for (let i = 0; i < untransformedDirtyLeavesLength; i++) {
|
|
||||||
const nodeKey = untransformedDirtyLeavesArr[i];
|
|
||||||
const node = nodeMap.get(nodeKey);
|
const node = nodeMap.get(nodeKey);
|
||||||
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
if ($isTextNode(node) && node.isSimpleText() && !node.isUnmergeable()) {
|
||||||
$normalizeTextNode(node);
|
$normalizeTextNode(node);
|
||||||
@ -211,18 +206,12 @@ function $applyAllTransforms(
|
|||||||
// new ones caused by element transforms
|
// new ones caused by element transforms
|
||||||
editor._dirtyLeaves = new Set();
|
editor._dirtyLeaves = new Set();
|
||||||
editor._dirtyElements = new Map();
|
editor._dirtyElements = new Map();
|
||||||
const untransformedDirtyElementsArr = Array.from(
|
for (const currentUntransformedDirtyElement of untransformedDirtyElements) {
|
||||||
untransformedDirtyElements,
|
|
||||||
);
|
|
||||||
for (let i = 0; i < untransformedDirtyElementsLength; i++) {
|
|
||||||
const currentUntransformedDirtyElement = untransformedDirtyElementsArr[i];
|
|
||||||
const nodeKey = currentUntransformedDirtyElement[0];
|
const nodeKey = currentUntransformedDirtyElement[0];
|
||||||
const intentionallyMarkedAsDirty = currentUntransformedDirtyElement[1];
|
const intentionallyMarkedAsDirty = currentUntransformedDirtyElement[1];
|
||||||
if (nodeKey === 'root' || !intentionallyMarkedAsDirty) {
|
if (nodeKey === 'root' || !intentionallyMarkedAsDirty) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const nodeIntentionallyMarkedAsDirty =
|
|
||||||
untransformedDirtyElementsArr[i][1];
|
|
||||||
const node = nodeMap.get(nodeKey);
|
const node = nodeMap.get(nodeKey);
|
||||||
if (
|
if (
|
||||||
node !== undefined &&
|
node !== undefined &&
|
||||||
@ -230,7 +219,7 @@ function $applyAllTransforms(
|
|||||||
) {
|
) {
|
||||||
$applyTransforms(editor, node, transformsCache);
|
$applyTransforms(editor, node, transformsCache);
|
||||||
}
|
}
|
||||||
dirtyElements.set(nodeKey, nodeIntentionallyMarkedAsDirty);
|
dirtyElements.set(nodeKey, intentionallyMarkedAsDirty);
|
||||||
}
|
}
|
||||||
untransformedDirtyLeaves = editor._dirtyLeaves;
|
untransformedDirtyLeaves = editor._dirtyLeaves;
|
||||||
untransformedDirtyLeavesLength = untransformedDirtyLeaves.size;
|
untransformedDirtyLeavesLength = untransformedDirtyLeaves.size;
|
||||||
@ -460,9 +449,9 @@ export function triggerListeners(
|
|||||||
const previouslyUpdating = editor._updating;
|
const previouslyUpdating = editor._updating;
|
||||||
editor._updating = isCurrentlyEnqueuingUpdates;
|
editor._updating = isCurrentlyEnqueuingUpdates;
|
||||||
try {
|
try {
|
||||||
const listeners = Array.from(editor._listeners[type]);
|
const listeners = editor._listeners[type];
|
||||||
for (let i = 0; i < listeners.length; i++) {
|
for (const listener of listeners) {
|
||||||
listeners[i](...payload);
|
listener(...payload);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
editor._updating = previouslyUpdating;
|
editor._updating = previouslyUpdating;
|
||||||
@ -486,9 +475,9 @@ export function triggerCommandListeners(
|
|||||||
for (let e = 0; e < editors.length; e++) {
|
for (let e = 0; e < editors.length; e++) {
|
||||||
const currentEditor = editors[e];
|
const currentEditor = editors[e];
|
||||||
const commandListeners = currentEditor._listeners.command;
|
const commandListeners = currentEditor._listeners.command;
|
||||||
const listeners = Array.from(commandListeners[i]);
|
const listeners = commandListeners[i];
|
||||||
for (let s = 0; s < listeners.length; s++) {
|
for (const listener of listeners) {
|
||||||
if (listeners[s](type, payload, editor) === true) {
|
if (listener(type, payload, editor) === true) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,11 +359,7 @@ export function markAllNodesAsDirty(editor: LexicalEditor, type: string): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nodeMap = editorState._nodeMap;
|
const nodeMap = editorState._nodeMap;
|
||||||
const nodeMapEntries = Array.from(nodeMap);
|
for (const [, node] of nodeMap) {
|
||||||
// For...of would be faster here, but this will get
|
|
||||||
// compiled away to a slow-path with Babel.
|
|
||||||
for (let i = 0; i < nodeMapEntries.length; i++) {
|
|
||||||
const node = nodeMapEntries[i][1];
|
|
||||||
node.markDirty();
|
node.markDirty();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -884,11 +880,9 @@ export function $nodesOfType<T: LexicalNode>(klass: Class<T>): Array<T> {
|
|||||||
const editorState = getActiveEditorState();
|
const editorState = getActiveEditorState();
|
||||||
const readOnly = editorState._readOnly;
|
const readOnly = editorState._readOnly;
|
||||||
const klassType = klass.getType();
|
const klassType = klass.getType();
|
||||||
const nodes = Array.from(editorState._nodeMap.values());
|
const nodes = editorState._nodeMap;
|
||||||
const nodesLength = nodes.length;
|
|
||||||
const nodesOfType = [];
|
const nodesOfType = [];
|
||||||
for (let i = 0; i < nodesLength; i++) {
|
for (const [, node] of nodes) {
|
||||||
const node = nodes[i];
|
|
||||||
if (
|
if (
|
||||||
node instanceof klass &&
|
node instanceof klass &&
|
||||||
node.__type === klassType &&
|
node.__type === klassType &&
|
||||||
|
@ -126,9 +126,9 @@ export class DecoratorMap {
|
|||||||
|
|
||||||
set(key: string, value: DecoratorStateValue): void {
|
set(key: string, value: DecoratorStateValue): void {
|
||||||
this._map.set(key, value);
|
this._map.set(key, value);
|
||||||
const observers = Array.from(this._observers);
|
const observers = this._observers;
|
||||||
for (let i = 0; i < observers.length; i++) {
|
for (const observer of observers) {
|
||||||
observers[i](key, value);
|
observer(key, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ function invertObject(targetObj /* : ErrorMap */) /* : ErrorMap */ {
|
|||||||
const result = {};
|
const result = {};
|
||||||
const mapKeys = Object.keys(targetObj);
|
const mapKeys = Object.keys(targetObj);
|
||||||
|
|
||||||
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
|
|
||||||
for (const originalKey of mapKeys) {
|
for (const originalKey of mapKeys) {
|
||||||
const originalVal = targetObj[originalKey];
|
const originalVal = targetObj[originalKey];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user