Use for...of for performance optimizations (#1509)

* Use for...of for performance optimizations

* Fix ignores

* Remove console times
This commit is contained in:
Dominic Gannaway
2022-03-23 03:17:48 +00:00
committed by acywatson
parent 7dbba00306
commit db26e25d7a
18 changed files with 29 additions and 79 deletions

View File

@ -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,

View File

@ -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');

View File

@ -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",

View File

@ -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);
} }
} }

View File

@ -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);

View File

@ -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 {

View File

@ -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(() => {

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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,

View File

@ -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()) {

View File

@ -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;

View File

@ -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);
} }

View File

@ -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;
} }
} }

View File

@ -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 &&

View File

@ -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);
} }
} }

View File

@ -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];