From 3a3f9d4e694242ed4538b1bd541c375eb8ef8c58 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:32:20 +0200 Subject: [PATCH 1/6] Replace Array.sort and Array.reverse with Array.toSorted and Array.toReversed --- oxlint.config.mts | 8 -------- packages/app-vscode/src/ScopeTreeProvider.ts | 2 +- .../src/keyboard/KeyboardCommandsModal.ts | 2 +- .../app-vscode/src/keyboard/KeyboardHandler.ts | 4 +++- .../src/keyboard/buildSuffixTrie.test.ts | 2 +- .../grammar/getAcceptableTokenTypes.test.ts | 1 + .../src/keyboard/grammar/grammar.test.ts | 1 + .../src/docs/components/ScopeVisualizer.tsx | 2 +- .../src/docs/components/flattenHighlights.ts | 2 +- .../docs/contributing/MissingLanguageScopes.tsx | 6 +++--- .../src/ide/inMemoryTextEditor/performEdits.ts | 2 +- packages/lib-engine/src/actions/Sort.ts | 10 +++++----- .../lib-engine/src/core/getPreferredSnippet.ts | 2 +- .../src/core/handleHoistedModifiers.ts | 2 +- .../TreeSitterQuery/checkCaptureStartEnd.ts | 4 ++-- .../src/processTargets/TargetPipelineRunner.ts | 2 +- .../scopeHandlers/BaseScopeHandler.test.ts | 4 ++-- .../NotebookCellApiScopeHandler.ts | 4 ++-- .../SentenceScopeHandler.ts | 2 +- .../SurroundingPairScopeHandler.ts | 2 +- .../getDelimiterOccurrences.ts | 2 +- .../BaseTreeSitterScopeHandler.ts | 2 +- .../transformRecordedTests/checkMarks.ts | 4 ++-- packages/lib-engine/src/test/scopes.test.ts | 6 +++--- .../lib-engine/src/tokenizer/tokenizer.test.ts | 10 ++++------ .../src/util/allocateHats/getDisplayLineMap.ts | 2 +- .../src/util/allocateHats/getRankedTokens.ts | 17 +++++++---------- .../lib-engine/src/util/getMatchesInRange.ts | 2 +- .../tool-meta-updater/src/updatePackageJson.ts | 8 ++++---- .../src/updatesScopeSupportFacetInfos.ts | 2 +- 30 files changed, 55 insertions(+), 64 deletions(-) diff --git a/oxlint.config.mts b/oxlint.config.mts index 1abca6587a..255a71120f 100644 --- a/oxlint.config.mts +++ b/oxlint.config.mts @@ -1,15 +1,7 @@ import type { OxlintConfig } from "oxlint"; import { defineConfig } from "oxlint"; -// These rules should probably be re-enabled eventually -const temporarilyDisabledRules = [ - // Requires es2023 - "unicorn/no-array-sort", - "unicorn/no-array-reverse", -]; - const disabledRules = [ - ...temporarilyDisabledRules, "eslint/arrow-body-style", "eslint/capitalized-comments", "eslint/class-methods-use-this", diff --git a/packages/app-vscode/src/ScopeTreeProvider.ts b/packages/app-vscode/src/ScopeTreeProvider.ts index 761b418844..b6070c40a8 100644 --- a/packages/app-vscode/src/ScopeTreeProvider.ts +++ b/packages/app-vscode/src/ScopeTreeProvider.ts @@ -202,7 +202,7 @@ export class ScopeTreeProvider implements TreeDataProvider { getContainmentIcon?.(supportLevel.scopeType), ), ) - .sort((a, b) => { + .toSorted((a, b) => { if ( a.scopeTypeInfo.spokenForm.type !== b.scopeTypeInfo.spokenForm.type ) { diff --git a/packages/app-vscode/src/keyboard/KeyboardCommandsModal.ts b/packages/app-vscode/src/keyboard/KeyboardCommandsModal.ts index df8de3f8d3..8c63816fd0 100644 --- a/packages/app-vscode/src/keyboard/KeyboardCommandsModal.ts +++ b/packages/app-vscode/src/keyboard/KeyboardCommandsModal.ts @@ -90,7 +90,7 @@ export class KeyboardCommandsModal { const acceptableTokenTypeInfos = getAcceptableTokenTypes(this.parser); // FIXME: Here's where we'd update sidebar const acceptableTokenTypes = sortedUniq( - acceptableTokenTypeInfos.map(({ type }) => type).sort(), + acceptableTokenTypeInfos.map(({ type }) => type).toSorted(), ); let layer = this.layerCache.get(acceptableTokenTypes); if (layer == null) { diff --git a/packages/app-vscode/src/keyboard/KeyboardHandler.ts b/packages/app-vscode/src/keyboard/KeyboardHandler.ts index caddf11e79..8382648318 100644 --- a/packages/app-vscode/src/keyboard/KeyboardHandler.ts +++ b/packages/app-vscode/src/keyboard/KeyboardHandler.ts @@ -152,7 +152,9 @@ export class KeyboardHandler { // one. Eg if you're in the middle of typing a command and we turn off // the modal mode, we want to cancel the command if (index !== -1) { - const listenersToCancel = this.listeners.slice(index + 1).reverse(); + const listenersToCancel = this.listeners + .slice(index + 1) + .toReversed(); for (const entry of listenersToCancel) { entry.listener.handleCancelled(); } diff --git a/packages/app-vscode/src/keyboard/buildSuffixTrie.test.ts b/packages/app-vscode/src/keyboard/buildSuffixTrie.test.ts index 9c74edf403..129de01eb3 100644 --- a/packages/app-vscode/src/keyboard/buildSuffixTrie.test.ts +++ b/packages/app-vscode/src/keyboard/buildSuffixTrie.test.ts @@ -125,7 +125,7 @@ suite("buildSuffixTrie", () => { const { trie, conflicts } = buildSuffixTrie( input.map((key) => [key, key]), ); - const chars = uniq(input.flatMap((key) => key.split(""))).sort(); + const chars = uniq(input.flatMap((key) => key.split(""))).toSorted(); const actual = uniqWith( sortEntries(chars.flatMap((char) => trie.search(char))), isEqual, diff --git a/packages/app-vscode/src/keyboard/grammar/getAcceptableTokenTypes.test.ts b/packages/app-vscode/src/keyboard/grammar/getAcceptableTokenTypes.test.ts index 75bdf4f8a8..2ab050b939 100644 --- a/packages/app-vscode/src/keyboard/grammar/getAcceptableTokenTypes.test.ts +++ b/packages/app-vscode/src/keyboard/grammar/getAcceptableTokenTypes.test.ts @@ -133,6 +133,7 @@ const testCases: TestCase[] = [ suite("keyboard.getAcceptableTokenTypes", () => { let parser: nearley.Parser; + setup(() => { parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar)); }); diff --git a/packages/app-vscode/src/keyboard/grammar/grammar.test.ts b/packages/app-vscode/src/keyboard/grammar/grammar.test.ts index 419ae8832a..778848ba91 100644 --- a/packages/app-vscode/src/keyboard/grammar/grammar.test.ts +++ b/packages/app-vscode/src/keyboard/grammar/grammar.test.ts @@ -180,6 +180,7 @@ const testCases: TestCase[] = [ suite("keyboard grammar", () => { let parser: nearley.Parser; + setup(() => { parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar)); }); diff --git a/packages/app-web-docs/src/docs/components/ScopeVisualizer.tsx b/packages/app-web-docs/src/docs/components/ScopeVisualizer.tsx index 53065c78bf..008daa3707 100644 --- a/packages/app-web-docs/src/docs/components/ScopeVisualizer.tsx +++ b/packages/app-web-docs/src/docs/components/ScopeVisualizer.tsx @@ -315,7 +315,7 @@ function getScopeFixtures( } const result: Scopes = { public: [], internal: [] }; - const sortedScopes = Object.values(scopeMap).sort(nameComparator); + const sortedScopes = Object.values(scopeMap).toSorted(nameComparator); for (const scope of sortedScopes) { scope.facets.sort(facetComparator); diff --git a/packages/app-web-docs/src/docs/components/flattenHighlights.ts b/packages/app-web-docs/src/docs/components/flattenHighlights.ts index 2961130625..df55ec3181 100644 --- a/packages/app-web-docs/src/docs/components/flattenHighlights.ts +++ b/packages/app-web-docs/src/docs/components/flattenHighlights.ts @@ -53,7 +53,7 @@ function getUniquePositions(highlights: Highlight[]): Position[] { const result: Position[] = []; const positions = highlights .flatMap((h) => [h.range.start, h.range.end]) - .sort((a, b) => + .toSorted((a, b) => a.line === b.line ? a.character - b.character : a.line - b.line, ); for (let i = 0; i < positions.length; i++) { diff --git a/packages/app-web-docs/src/docs/contributing/MissingLanguageScopes.tsx b/packages/app-web-docs/src/docs/contributing/MissingLanguageScopes.tsx index 9954541e70..a27e5ac0dc 100644 --- a/packages/app-web-docs/src/docs/contributing/MissingLanguageScopes.tsx +++ b/packages/app-web-docs/src/docs/contributing/MissingLanguageScopes.tsx @@ -10,7 +10,7 @@ import { export function MissingLanguageScopes(): React.JSX.Element { const [showPrivate, setShowPrivate] = useState(false); - const languageIds = Object.keys(languageScopeSupport).sort(); + const languageIds = Object.keys(languageScopeSupport).toSorted(); return ( <> @@ -48,10 +48,10 @@ function Language({ .filter( (facet) => scopeSupport[facet] === ScopeSupportFacetLevel.unsupported, ) - .sort(); + .toSorted(); let unspecifiedFacets = scopeSupportFacets .filter((facet) => scopeSupport[facet] == null) - .sort(); + .toSorted(); if (!showPrivate) { unsupportedFacets = unsupportedFacets.filter((f) => !isPrivate(f)); diff --git a/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts b/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts index 21df2587ad..9617330870 100644 --- a/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts +++ b/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts @@ -42,7 +42,7 @@ function createChangeEvents( */ const sortedEdits = edits .map((edit, index) => ({ edit, index })) - .sort((a, b) => { + .toSorted((a, b) => { // Edits starting at the same position are sorted in reverse given order. if (a.edit.range.start.isEqual(b.edit.range.start)) { return b.index - a.index; diff --git a/packages/lib-engine/src/actions/Sort.ts b/packages/lib-engine/src/actions/Sort.ts index 3547cf6dd2..bcad152051 100644 --- a/packages/lib-engine/src/actions/Sort.ts +++ b/packages/lib-engine/src/actions/Sort.ts @@ -25,9 +25,9 @@ abstract class SortBase implements SimpleAction { } // First sort target by document order - const sortedTargets = targets - .slice() - .sort((a, b) => a.contentRange.start.compareTo(b.contentRange.start)); + const sortedTargets = targets.toSorted((a, b) => + a.contentRange.start.compareTo(b.contentRange.start), + ); const { returnValue: unsortedTexts } = await this.actions.getText.run( sortedTargets, @@ -49,7 +49,7 @@ abstract class SortBase implements SimpleAction { export class Sort extends SortBase { protected sortTexts(texts: string[]) { - return texts.sort((a, b) => + return texts.toSorted((a, b) => a.localeCompare(b, undefined, { numeric: true, caseFirst: "upper", @@ -60,7 +60,7 @@ export class Sort extends SortBase { export class Reverse extends SortBase { protected sortTexts(texts: string[]) { - return texts.reverse(); + return texts.toReversed(); } } diff --git a/packages/lib-engine/src/core/getPreferredSnippet.ts b/packages/lib-engine/src/core/getPreferredSnippet.ts index 1958319227..7583c15a6d 100644 --- a/packages/lib-engine/src/core/getPreferredSnippet.ts +++ b/packages/lib-engine/src/core/getPreferredSnippet.ts @@ -47,7 +47,7 @@ function getUniqueLanguagesString(snippets: CustomInsertSnippetArg[]): string { const languages = new Set( snippets.flatMap((snippet) => snippet.languages ?? []), ); - return Array.from(languages).sort().join(", "); + return Array.from(languages).toSorted().join(", "); } function tryToFindPreferredSnippet< diff --git a/packages/lib-engine/src/core/handleHoistedModifiers.ts b/packages/lib-engine/src/core/handleHoistedModifiers.ts index e589f01f02..94672dac72 100644 --- a/packages/lib-engine/src/core/handleHoistedModifiers.ts +++ b/packages/lib-engine/src/core/handleHoistedModifiers.ts @@ -56,7 +56,7 @@ export function handleHoistedModifiers( // modifier to the range owns the range. For example if you say "every line // every funk air past bat", the "every line" owns the range, and the "every // funk" is applied to the output. - for (const [modifier, idx] of indexedModifiers.reverse()) { + for (const [modifier, idx] of indexedModifiers.toReversed()) { for (const hoistedModifierType of hoistedModifierTypes) { const acceptanceInfo = hoistedModifierType.accept(modifier); if (acceptanceInfo.accepted) { diff --git a/packages/lib-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts b/packages/lib-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts index dfbc18db5c..b98850a867 100644 --- a/packages/lib-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts +++ b/packages/lib-engine/src/languages/TreeSitterQuery/checkCaptureStartEnd.ts @@ -31,12 +31,12 @@ export function checkCaptureStartEnd( const lastStart = captures .filter(({ name }) => name.endsWith(".start")) .map(({ range: { end } }) => end) - .sort((a, b) => a.compareTo(b)) + .toSorted((a, b) => a.compareTo(b)) .at(-1); const firstEnd = captures .filter(({ name }) => name.endsWith(".end")) .map(({ range: { start } }) => start) - .sort((a, b) => a.compareTo(b)) + .toSorted((a, b) => a.compareTo(b)) .at(0); if (lastStart != null && firstEnd != null) { if (lastStart.isAfter(firstEnd)) { diff --git a/packages/lib-engine/src/processTargets/TargetPipelineRunner.ts b/packages/lib-engine/src/processTargets/TargetPipelineRunner.ts index c23f308d07..7394f8f127 100644 --- a/packages/lib-engine/src/processTargets/TargetPipelineRunner.ts +++ b/packages/lib-engine/src/processTargets/TargetPipelineRunner.ts @@ -300,7 +300,7 @@ export function getModifierStagesFromTargetModifiers( ) { // Reverse target modifiers because they are returned in reverse order from // the api, to match the order in which they are spoken. - return targetModifiers.map(modifierStageFactory.create).reverse(); + return targetModifiers.map(modifierStageFactory.create).toReversed(); } /** Run all targets through the modifier stages */ diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BaseScopeHandler.test.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BaseScopeHandler.test.ts index fd0bf340b4..300e5426d0 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BaseScopeHandler.test.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/BaseScopeHandler.test.ts @@ -133,8 +133,8 @@ suite("BaseScopeHandler", () => { })); assert.deepEqual( - [...inputScopes] - .sort((a, b) => + inputScopes + .toSorted((a, b) => compareTargetScopes(testCase.direction, position, a, b), ) .map(fromScope), diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellApiScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellApiScopeHandler.ts index 0b2e2347a9..dc5393cb67 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellApiScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/NotebookCellApiScopeHandler.ts @@ -68,7 +68,7 @@ function getNotebookCells( ) { return direction === "forward" ? notebook.cells.slice(cell.index + 1) - : notebook.cells.slice(0, cell.index).reverse(); + : notebook.cells.slice(0, cell.index).toReversed(); } // Every scope @@ -81,7 +81,7 @@ function getNotebookCells( return direction === "forward" ? notebook.cells.slice(cell.index) - : notebook.cells.slice(0, cell.index + 1).reverse(); + : notebook.cells.slice(0, cell.index + 1).toReversed(); } function getNotebook(ide: IDE, editor: TextEditor) { diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts index 4111bb87dd..be295b1908 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts @@ -42,6 +42,6 @@ export class SentenceScopeHandler extends NestedScopeHandler { return direction === "forward" ? imap(sentences, sentenceToScope) - : Array.from(sentences, sentenceToScope).reverse(); + : Array.from(sentences, sentenceToScope).toReversed(); } } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts index fdc45dbdbf..e97af983c3 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts @@ -73,7 +73,7 @@ export class SurroundingPairScopeHandler extends BaseScopeHandler { this.scopeType.requireStrongContainment ?? false, ), ) - .sort((a, b) => compareTargetScopes(direction, position, a, b)); + .toSorted((a, b) => compareTargetScopes(direction, position, a, b)); } } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts index c5f05cd3ca..4b12f72f30 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts @@ -92,5 +92,5 @@ function getSortedCaptures(items?: QueryCapture[]): QueryCapture[] { if (items == null) { return []; } - return items.sort((a, b) => a.range.start.compareTo(b.range.start)); + return items.toSorted((a, b) => a.range.start.compareTo(b.range.start)); } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts index d9b3c236d4..77c956cea1 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts @@ -46,7 +46,7 @@ export abstract class BaseTreeSitterScopeHandler extends BaseScopeHandler { .matches(document, start, end) .map((match) => this.matchToScope(editor, match, isEveryScope)) .filter((scope): scope is ExtendedTargetScope => scope != null) - .sort((a, b) => compareTargetScopes(direction, position, a, b)); + .toSorted((a, b) => compareTargetScopes(direction, position, a, b)); // Merge scopes that have the same domain into a single scope with multiple // targets diff --git a/packages/lib-engine/src/scripts/transformRecordedTests/checkMarks.ts b/packages/lib-engine/src/scripts/transformRecordedTests/checkMarks.ts index 11ae9d3253..660a5e9764 100644 --- a/packages/lib-engine/src/scripts/transformRecordedTests/checkMarks.ts +++ b/packages/lib-engine/src/scripts/transformRecordedTests/checkMarks.ts @@ -29,8 +29,8 @@ export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined { const actualMarks = Object.keys(originalFixture.initialState.marks ?? {}); assert.deepEqual( - uniq(actualMarks.map(normalizeGraphemes)).sort(), - uniq(expectedMarks.map(normalizeGraphemes)).sort(), + uniq(actualMarks.map(normalizeGraphemes)).toSorted(), + uniq(expectedMarks.map(normalizeGraphemes)).toSorted(), ); return undefined; diff --git a/packages/lib-engine/src/test/scopes.test.ts b/packages/lib-engine/src/test/scopes.test.ts index 12d5cc92e6..d8f3e32559 100644 --- a/packages/lib-engine/src/test/scopes.test.ts +++ b/packages/lib-engine/src/test/scopes.test.ts @@ -43,7 +43,7 @@ suite("Scope test cases", () => { languages[language] ??= []; } - for (const languageId of Object.keys(languages).sort()) { + for (const languageId of Object.keys(languages).toSorted()) { const tests = languages[languageId]; test(`${languageId} facet coverage`, () => testLanguageSupport( @@ -91,7 +91,7 @@ function testLanguageSupport(languageId: string, testedFacets: string[]) { (testedFacet) => !supportedFacets.includes(testedFacet), ); if (unsupportedFacets.length > 0) { - const values = uniq(unsupportedFacets).sort().join(", "); + const values = uniq(unsupportedFacets).toSorted().join(", "); assert.fail( `Facets [${values}] are tested but not listed in getLanguageScopeSupport`, ); @@ -102,7 +102,7 @@ function testLanguageSupport(languageId: string, testedFacets: string[]) { (supportedFacet) => !testedFacets.includes(supportedFacet), ); if (untestedFacets.length > 0) { - const values = untestedFacets.sort().join(", "); + const values = untestedFacets.toSorted().join(", "); assert.fail(`Missing test for scope support facets [${values}]`); } } diff --git a/packages/lib-engine/src/tokenizer/tokenizer.test.ts b/packages/lib-engine/src/tokenizer/tokenizer.test.ts index 61f17c538c..4568786979 100644 --- a/packages/lib-engine/src/tokenizer/tokenizer.test.ts +++ b/packages/lib-engine/src/tokenizer/tokenizer.test.ts @@ -1,5 +1,5 @@ import * as assert from "node:assert/strict"; -import { flatten, range } from "lodash-es"; +import { range } from "lodash-es"; import { FakeIDE } from "@cursorless/lib-common"; import { tokenize } from "."; @@ -167,11 +167,9 @@ function getAsciiSymbols() { ["[", "`"], ["{", "~"], ]; - return flatten( - rangesToTest.map(([start, end]) => - range(start.codePointAt(0)!, end.codePointAt(0)! + 1).map((charCode) => - String.fromCodePoint(charCode), - ), + return rangesToTest.flatMap(([start, end]) => + range(start.codePointAt(0)!, end.codePointAt(0)! + 1).map((charCode) => + String.fromCodePoint(charCode), ), ); } diff --git a/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts b/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts index db579d44d6..cbe0964252 100644 --- a/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts +++ b/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts @@ -17,7 +17,7 @@ export function getDisplayLineMap( ...editor.visibleRanges.flatMap((visibleRange) => range(visibleRange.start.line, visibleRange.end.line + 1), ), - ]).sort((a, b) => a - b); + ]).toSorted((a, b) => a - b); return new Map(lines.map((value, index) => [value, index])); } diff --git a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts index d137550e8c..660e46c3fe 100644 --- a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts +++ b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts @@ -1,4 +1,3 @@ -import { flatten } from "lodash-es"; import type { CompositeKeyMap, IDE, @@ -35,14 +34,12 @@ export function getRankedTokens( */ const referencePosition = editor.selections[0].active; const displayLineMap = getDisplayLineMap(editor, [referencePosition.line]); - const tokens = flatten( - editor.visibleRanges.map((range) => - // oxlint-disable-next-line oxc/no-map-spread - getTokensInRange(ide, editor, range).map((partialToken) => ({ - ...partialToken, - displayLine: displayLineMap.get(partialToken.range.start.line)!, - })), - ), + const tokens = editor.visibleRanges.flatMap((range) => + // oxlint-disable-next-line oxc/no-map-spread + getTokensInRange(ide, editor, range).map((partialToken) => ({ + ...partialToken, + displayLine: displayLineMap.get(partialToken.range.start.line)!, + })), ); tokens.sort( @@ -69,7 +66,7 @@ function moveForcedHatsToFront( return tokens; } - return tokens.sort((a, b) => { + return tokens.toSorted((a, b) => { const aIsForced = forcedHatMap.has(a); const bIsForced = forcedHatMap.has(b); if (aIsForced && !bIsForced) { diff --git a/packages/lib-engine/src/util/getMatchesInRange.ts b/packages/lib-engine/src/util/getMatchesInRange.ts index 9103897433..edfaf605dd 100644 --- a/packages/lib-engine/src/util/getMatchesInRange.ts +++ b/packages/lib-engine/src/util/getMatchesInRange.ts @@ -43,5 +43,5 @@ export function generateMatchesInRange( return direction === "forward" ? imap(text.matchAll(regex), matchToRange) - : Array.from(text.matchAll(regex), matchToRange).reverse(); + : Array.from(text.matchAll(regex), matchToRange).toReversed(); } diff --git a/packages/tool-meta-updater/src/updatePackageJson.ts b/packages/tool-meta-updater/src/updatePackageJson.ts index 3f2103563b..d156df6b3c 100644 --- a/packages/tool-meta-updater/src/updatePackageJson.ts +++ b/packages/tool-meta-updater/src/updatePackageJson.ts @@ -119,7 +119,7 @@ function getScripts( }; return Object.fromEntries( - Object.entries(scripts).sort( + Object.entries(scripts).toSorted( ([keyA], [keyB]) => getOrder(keyA) - getOrder(keyB), ), ); @@ -180,21 +180,21 @@ function sortFields(obj: Record): Record { "pnpm", ]; const sorted = Object.fromEntries( - Object.entries(obj).sort( + Object.entries(obj).toSorted( ([keyA], [keyB]) => orderedKeys.indexOf(keyA) - orderedKeys.indexOf(keyB), ), ); if (sorted.dependencies != null) { sorted.dependencies = Object.fromEntries( - Object.entries(sorted.dependencies).sort(([keyA], [keyB]) => + Object.entries(sorted.dependencies).toSorted(([keyA], [keyB]) => keyA.localeCompare(keyB), ), ); } if (sorted.devDependencies != null) { sorted.devDependencies = Object.fromEntries( - Object.entries(sorted.devDependencies).sort(([keyA], [keyB]) => + Object.entries(sorted.devDependencies).toSorted(([keyA], [keyB]) => keyA.localeCompare(keyB), ), ); diff --git a/packages/tool-meta-updater/src/updatesScopeSupportFacetInfos.ts b/packages/tool-meta-updater/src/updatesScopeSupportFacetInfos.ts index 6b6d64bcbe..4c39aa4e44 100644 --- a/packages/tool-meta-updater/src/updatesScopeSupportFacetInfos.ts +++ b/packages/tool-meta-updater/src/updatesScopeSupportFacetInfos.ts @@ -22,7 +22,7 @@ export function updatesScopeSupportFacetInfos( ...plaintextScopeSupportFacetInfos, }; - const facets = Object.keys(facetsInfos).sort(); + const facets = Object.keys(facetsInfos).toSorted(); const rows: string[] = []; let currentScopeType: string | null = null; From 0f575da2fc2e607d8fbe3fc35be12cf254ef4cb4 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:37:13 +0200 Subject: [PATCH 2/6] Revert sort en performance critical areas --- .../SurroundingPairScopeHandler.ts | 3 ++- .../BaseTreeSitterScopeHandler.ts | 3 ++- .../src/util/allocateHats/getRankedTokens.ts | 16 ++++++++-------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts index e97af983c3..4a15380164 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/SurroundingPairScopeHandler.ts @@ -73,7 +73,8 @@ export class SurroundingPairScopeHandler extends BaseScopeHandler { this.scopeType.requireStrongContainment ?? false, ), ) - .toSorted((a, b) => compareTargetScopes(direction, position, a, b)); + // oxlint-disable-next-line unicorn/no-array-sort + .sort((a, b) => compareTargetScopes(direction, position, a, b)); } } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts index 77c956cea1..781bcff895 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/TreeSitterScopeHandler/BaseTreeSitterScopeHandler.ts @@ -46,7 +46,8 @@ export abstract class BaseTreeSitterScopeHandler extends BaseScopeHandler { .matches(document, start, end) .map((match) => this.matchToScope(editor, match, isEveryScope)) .filter((scope): scope is ExtendedTargetScope => scope != null) - .toSorted((a, b) => compareTargetScopes(direction, position, a, b)); + // oxlint-disable-next-line unicorn/no-array-sort + .sort((a, b) => compareTargetScopes(direction, position, a, b)); // Merge scopes that have the same domain into a single scope with multiple // targets diff --git a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts index 660e46c3fe..db23817749 100644 --- a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts +++ b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts @@ -52,21 +52,21 @@ export function getRankedTokens( return tokens; }); - return moveForcedHatsToFront(forcedHatMap, tokens).map((token, index) => ({ + if (forcedHatMap != null) { + moveForcedHatsToFront(forcedHatMap, tokens); + } + + return tokens.map((token, index) => ({ token, rank: -index, })); } function moveForcedHatsToFront( - forcedHatMap: CompositeKeyMap | undefined, + forcedHatMap: CompositeKeyMap, tokens: Token[], -) { - if (forcedHatMap == null) { - return tokens; - } - - return tokens.toSorted((a, b) => { +): void { + tokens.sort((a, b) => { const aIsForced = forcedHatMap.has(a); const bIsForced = forcedHatMap.has(b); if (aIsForced && !bIsForced) { From 75f97564c5dd6231348f2b1338e49f5ff262a113 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:47:37 +0200 Subject: [PATCH 3/6] More reverted --- packages/app-vscode/src/ScopeTreeProvider.ts | 43 +++++++++++-------- .../ide/inMemoryTextEditor/performEdits.ts | 3 +- .../SentenceScopeHandler.ts | 3 +- .../getDelimiterOccurrences.ts | 3 +- .../src/scopeProviders/ScopeSupportWatcher.ts | 1 - .../src/util/allocateHats/getRankedTokens.ts | 1 - .../lib-engine/src/util/getMatchesInRange.ts | 3 +- packages/lib-engine/src/util/unifyRanges.ts | 1 - 8 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/app-vscode/src/ScopeTreeProvider.ts b/packages/app-vscode/src/ScopeTreeProvider.ts index b6070c40a8..146c135cd2 100644 --- a/packages/app-vscode/src/ScopeTreeProvider.ts +++ b/packages/app-vscode/src/ScopeTreeProvider.ts @@ -182,7 +182,7 @@ export class ScopeTreeProvider implements TreeDataProvider { }; })(); - return this.supportLevels + const supportLevels = this.supportLevels .filter( (supportLevel) => supportLevel.support === scopeSupport && @@ -201,26 +201,11 @@ export class ScopeTreeProvider implements TreeDataProvider { isEqual(supportLevel.scopeType, this.scopeVisualizer.scopeType), getContainmentIcon?.(supportLevel.scopeType), ), - ) - .toSorted((a, b) => { - if ( - a.scopeTypeInfo.spokenForm.type !== b.scopeTypeInfo.spokenForm.type - ) { - // Scopes with no spoken form are sorted to the bottom - return a.scopeTypeInfo.spokenForm.type === "error" ? 1 : -1; - } + ); - if ( - a.scopeTypeInfo.isLanguageSpecific !== - b.scopeTypeInfo.isLanguageSpecific - ) { - // Then language-specific scopes are sorted to the top - return a.scopeTypeInfo.isLanguageSpecific ? -1 : 1; - } + supportLevels.sort(compareScopeTypes); - // Then alphabetical by label - return a.label.label.localeCompare(b.label.label); - }); + return supportLevels; } private getContainmentIcon( @@ -255,6 +240,26 @@ export class ScopeTreeProvider implements TreeDataProvider { } } +function compareScopeTypes( + a: ScopeSupportTreeItem, + b: ScopeSupportTreeItem, +): number { + if (a.scopeTypeInfo.spokenForm.type !== b.scopeTypeInfo.spokenForm.type) { + // Scopes with no spoken form are sorted to the bottom + return a.scopeTypeInfo.spokenForm.type === "error" ? 1 : -1; + } + + if ( + a.scopeTypeInfo.isLanguageSpecific !== b.scopeTypeInfo.isLanguageSpecific + ) { + // Then language-specific scopes are sorted to the top + return a.scopeTypeInfo.isLanguageSpecific ? -1 : 1; + } + + // Then alphabetical by label + return a.label.label.localeCompare(b.label.label); +} + function getSupportCategories(): SupportCategoryTreeItem[] { return [ new SupportCategoryTreeItem(ScopeSupport.supportedAndPresentInEditor), diff --git a/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts b/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts index 9617330870..189359d285 100644 --- a/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts +++ b/packages/lib-common/src/ide/inMemoryTextEditor/performEdits.ts @@ -42,7 +42,8 @@ function createChangeEvents( */ const sortedEdits = edits .map((edit, index) => ({ edit, index })) - .toSorted((a, b) => { + // oxlint-disable-next-line unicorn/no-array-sort + .sort((a, b) => { // Edits starting at the same position are sorted in reverse given order. if (a.edit.range.start.isEqual(b.edit.range.start)) { return b.index - a.index; diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts index be295b1908..67e5b2fe21 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts @@ -42,6 +42,7 @@ export class SentenceScopeHandler extends NestedScopeHandler { return direction === "forward" ? imap(sentences, sentenceToScope) - : Array.from(sentences, sentenceToScope).toReversed(); + : // oxlint-disable-next-line unicorn/no-array-sort + Array.from(sentences, sentenceToScope).sort(); } } diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts index 4b12f72f30..bb5e104d31 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SurroundingPairScopeHandler/getDelimiterOccurrences.ts @@ -92,5 +92,6 @@ function getSortedCaptures(items?: QueryCapture[]): QueryCapture[] { if (items == null) { return []; } - return items.toSorted((a, b) => a.range.start.compareTo(b.range.start)); + items.sort((a, b) => a.range.start.compareTo(b.range.start)); + return items; } diff --git a/packages/lib-engine/src/scopeProviders/ScopeSupportWatcher.ts b/packages/lib-engine/src/scopeProviders/ScopeSupportWatcher.ts index f0454328ef..ffdaceda9d 100644 --- a/packages/lib-engine/src/scopeProviders/ScopeSupportWatcher.ts +++ b/packages/lib-engine/src/scopeProviders/ScopeSupportWatcher.ts @@ -110,7 +110,6 @@ export class ScopeSupportWatcher { const scopeTypeInfos = this.scopeInfoProvider.getScopeTypeInfos(); - // oxlint-disable-next-line oxc/no-map-spread return scopeTypeInfos.map((scopeTypeInfo) => ({ ...scopeTypeInfo, support: getScopeTypeSupport(scopeTypeInfo.scopeType), diff --git a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts index db23817749..7eeef4b854 100644 --- a/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts +++ b/packages/lib-engine/src/util/allocateHats/getRankedTokens.ts @@ -35,7 +35,6 @@ export function getRankedTokens( const referencePosition = editor.selections[0].active; const displayLineMap = getDisplayLineMap(editor, [referencePosition.line]); const tokens = editor.visibleRanges.flatMap((range) => - // oxlint-disable-next-line oxc/no-map-spread getTokensInRange(ide, editor, range).map((partialToken) => ({ ...partialToken, displayLine: displayLineMap.get(partialToken.range.start.line)!, diff --git a/packages/lib-engine/src/util/getMatchesInRange.ts b/packages/lib-engine/src/util/getMatchesInRange.ts index edfaf605dd..2e6b3e3ab4 100644 --- a/packages/lib-engine/src/util/getMatchesInRange.ts +++ b/packages/lib-engine/src/util/getMatchesInRange.ts @@ -43,5 +43,6 @@ export function generateMatchesInRange( return direction === "forward" ? imap(text.matchAll(regex), matchToRange) - : Array.from(text.matchAll(regex), matchToRange).toReversed(); + : // oxlint-disable-next-line unicorn/no-array-sort + Array.from(text.matchAll(regex), matchToRange).sort(); } diff --git a/packages/lib-engine/src/util/unifyRanges.ts b/packages/lib-engine/src/util/unifyRanges.ts index 715e028ca2..9e407b475b 100644 --- a/packages/lib-engine/src/util/unifyRanges.ts +++ b/packages/lib-engine/src/util/unifyRanges.ts @@ -7,7 +7,6 @@ export function unifyRemovalTargets(targets: Target[]): Target[] { if (targets.length < 2) { return targets; } - // oxlint-disable-next-line oxc/no-map-spread return groupTargetsForEachEditor(targets).flatMap( ([_editor, editorTargets]) => { if (editorTargets.length < 2) { From 62bc2e7dbface80a0cd8fd5874533a6f513c5bd5 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:49:51 +0200 Subject: [PATCH 4/6] Another update --- .../lib-engine/src/util/allocateHats/getDisplayLineMap.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts b/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts index cbe0964252..d8d589f17d 100644 --- a/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts +++ b/packages/lib-engine/src/util/allocateHats/getDisplayLineMap.ts @@ -17,7 +17,9 @@ export function getDisplayLineMap( ...editor.visibleRanges.flatMap((visibleRange) => range(visibleRange.start.line, visibleRange.end.line + 1), ), - ]).toSorted((a, b) => a - b); + ]); + + lines.sort((a, b) => a - b); return new Map(lines.map((value, index) => [value, index])); } From f32734882f5f30cb58f3b68f66b7645f9ec9c8e1 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:50:56 +0200 Subject: [PATCH 5/6] reverse not sort --- packages/lib-engine/src/util/getMatchesInRange.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib-engine/src/util/getMatchesInRange.ts b/packages/lib-engine/src/util/getMatchesInRange.ts index 2e6b3e3ab4..dd049ffb10 100644 --- a/packages/lib-engine/src/util/getMatchesInRange.ts +++ b/packages/lib-engine/src/util/getMatchesInRange.ts @@ -43,6 +43,6 @@ export function generateMatchesInRange( return direction === "forward" ? imap(text.matchAll(regex), matchToRange) - : // oxlint-disable-next-line unicorn/no-array-sort - Array.from(text.matchAll(regex), matchToRange).sort(); + : // oxlint-disable-next-line unicorn/no-array-reverse + Array.from(text.matchAll(regex), matchToRange).reverse(); } From 9b890ec0e92ce1af2a15c49de4f35679b98ca30a Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 24 Apr 2026 13:54:15 +0200 Subject: [PATCH 6/6] Another reversal --- .../SentenceScopeHandler/SentenceScopeHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts index 67e5b2fe21..216348e94d 100644 --- a/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts +++ b/packages/lib-engine/src/processTargets/modifiers/scopeHandlers/SentenceScopeHandler/SentenceScopeHandler.ts @@ -42,7 +42,7 @@ export class SentenceScopeHandler extends NestedScopeHandler { return direction === "forward" ? imap(sentences, sentenceToScope) - : // oxlint-disable-next-line unicorn/no-array-sort - Array.from(sentences, sentenceToScope).sort(); + : // oxlint-disable-next-line unicorn/no-array-reverse + Array.from(sentences, sentenceToScope).reverse(); } }