diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/modeled.ts b/extensions/ql-vscode/src/data-extensions-editor/shared/modeled-percentage.ts similarity index 81% rename from extensions/ql-vscode/src/view/data-extensions-editor/modeled.ts rename to extensions/ql-vscode/src/data-extensions-editor/shared/modeled-percentage.ts index dd29df262..92d22eb6e 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/modeled.ts +++ b/extensions/ql-vscode/src/data-extensions-editor/shared/modeled-percentage.ts @@ -1,4 +1,4 @@ -import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage"; +import { ExternalApiUsage } from "../external-api-usage"; export function calculateModeledPercentage( externalApiUsages: Array>, diff --git a/extensions/ql-vscode/src/data-extensions-editor/shared/sorting.ts b/extensions/ql-vscode/src/data-extensions-editor/shared/sorting.ts new file mode 100644 index 000000000..3eb42591e --- /dev/null +++ b/extensions/ql-vscode/src/data-extensions-editor/shared/sorting.ts @@ -0,0 +1,88 @@ +import { ExternalApiUsage } from "../external-api-usage"; +import { Mode } from "./mode"; +import { calculateModeledPercentage } from "./modeled-percentage"; + +export function groupMethods( + externalApiUsages: ExternalApiUsage[], + mode: Mode, +): Record { + const groupedByLibrary: Record = {}; + + for (const externalApiUsage of externalApiUsages) { + // Group by package if using framework mode + const key = + mode === Mode.Framework + ? externalApiUsage.packageName + : externalApiUsage.library; + + groupedByLibrary[key] ??= []; + groupedByLibrary[key].push(externalApiUsage); + } + + return groupedByLibrary; +} + +export function sortGroupNames( + methods: Record, +): string[] { + return Object.keys(methods).sort((a, b) => + compareGroups(methods[a], a, methods[b], b), + ); +} + +export function sortMethods( + externalApiUsages: ExternalApiUsage[], +): ExternalApiUsage[] { + const sortedExternalApiUsages = [...externalApiUsages]; + sortedExternalApiUsages.sort((a, b) => compareMethod(a, b)); + return sortedExternalApiUsages; +} + +function compareGroups( + a: ExternalApiUsage[], + aName: string, + b: ExternalApiUsage[], + bName: string, +): number { + const supportedPercentageA = calculateModeledPercentage(a); + const supportedPercentageB = calculateModeledPercentage(b); + + // Sort first by supported percentage ascending + if (supportedPercentageA > supportedPercentageB) { + return 1; + } + if (supportedPercentageA < supportedPercentageB) { + return -1; + } + + const numberOfUsagesA = a.reduce((acc, curr) => acc + curr.usages.length, 0); + const numberOfUsagesB = b.reduce((acc, curr) => acc + curr.usages.length, 0); + + // If the number of usages is equal, sort by number of methods descending + if (numberOfUsagesA === numberOfUsagesB) { + const numberOfMethodsA = a.length; + const numberOfMethodsB = b.length; + + // If the number of methods is equal, sort by library name ascending + if (numberOfMethodsA === numberOfMethodsB) { + return aName.localeCompare(bName); + } + + return numberOfMethodsB - numberOfMethodsA; + } + + // Then sort by number of usages descending + return numberOfUsagesB - numberOfUsagesA; +} + +function compareMethod(a: ExternalApiUsage, b: ExternalApiUsage): number { + // Sort first by supported, putting unmodeled methods first. + if (a.supported && !b.supported) { + return 1; + } + if (!a.supported && b.supported) { + return -1; + } + // Then sort by number of usages descending + return b.usages.length - a.usages.length; +} diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx index 6b2154227..a9991c4ef 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/DataExtensionsEditor.tsx @@ -10,7 +10,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag import { ModeledMethod } from "../../data-extensions-editor/modeled-method"; import { assertNever } from "../../common/helpers-pure"; import { vscode } from "../vscode-api"; -import { calculateModeledPercentage } from "./modeled"; +import { calculateModeledPercentage } from "../../data-extensions-editor/shared/modeled-percentage"; import { LinkIconButton } from "../variant-analysis/LinkIconButton"; import { ViewTitle } from "../common"; import { DataExtensionEditorViewState } from "../../data-extensions-editor/shared/view-state"; diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/LibraryRow.tsx index 3296ea2c3..2931b07f5 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/LibraryRow.tsx @@ -5,7 +5,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag import { ModeledMethod } from "../../data-extensions-editor/modeled-method"; import { pluralize } from "../../common/word"; import { ModeledMethodDataGrid } from "./ModeledMethodDataGrid"; -import { calculateModeledPercentage } from "./modeled"; +import { calculateModeledPercentage } from "../../data-extensions-editor/shared/modeled-percentage"; import { decimalFormatter, percentFormatter } from "./formatters"; import { Codicon } from "../common"; import { Mode } from "../../data-extensions-editor/shared/mode"; diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodDataGrid.tsx index 0ebdeaeda..c1328a4ca 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodDataGrid.tsx @@ -9,6 +9,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag import { ModeledMethod } from "../../data-extensions-editor/modeled-method"; import { useMemo } from "react"; import { Mode } from "../../data-extensions-editor/shared/mode"; +import { sortMethods } from "../../data-extensions-editor/shared/sorting"; type Props = { externalApiUsages: ExternalApiUsage[]; @@ -26,21 +27,10 @@ export const ModeledMethodDataGrid = ({ mode, onChange, }: Props) => { - const sortedExternalApiUsages = useMemo(() => { - const sortedExternalApiUsages = [...externalApiUsages]; - sortedExternalApiUsages.sort((a, b) => { - // Sort first by supported, putting unmodeled methods first. - if (a.supported && !b.supported) { - return 1; - } - if (!a.supported && b.supported) { - return -1; - } - // Then sort by number of usages descending - return b.usages.length - a.usages.length; - }); - return sortedExternalApiUsages; - }, [externalApiUsages]); + const sortedExternalApiUsages = useMemo( + () => sortMethods(externalApiUsages), + [externalApiUsages], + ); return ( diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodsList.tsx b/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodsList.tsx index c6b98b008..dbcff18c8 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodsList.tsx +++ b/extensions/ql-vscode/src/view/data-extensions-editor/ModeledMethodsList.tsx @@ -2,9 +2,12 @@ import * as React from "react"; import { useMemo } from "react"; import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage"; import { ModeledMethod } from "../../data-extensions-editor/modeled-method"; -import { calculateModeledPercentage } from "./modeled"; import { LibraryRow } from "./LibraryRow"; import { Mode } from "../../data-extensions-editor/shared/mode"; +import { + groupMethods, + sortGroupNames, +} from "../../data-extensions-editor/shared/sorting"; type Props = { externalApiUsages: ExternalApiUsage[]; @@ -22,62 +25,12 @@ export const ModeledMethodsList = ({ mode, onChange, }: Props) => { - const grouped = useMemo(() => { - const groupedByLibrary: Record = {}; + const grouped = useMemo( + () => groupMethods(externalApiUsages, mode), + [externalApiUsages, mode], + ); - for (const externalApiUsage of externalApiUsages) { - // Group by package if using framework mode - const key = - mode === Mode.Framework - ? externalApiUsage.packageName - : externalApiUsage.library; - - groupedByLibrary[key] ??= []; - groupedByLibrary[key].push(externalApiUsage); - } - - return groupedByLibrary; - }, [externalApiUsages, mode]); - - const sortedGroupNames = useMemo(() => { - return Object.keys(grouped).sort((a, b) => { - const supportedPercentageA = calculateModeledPercentage(grouped[a]); - const supportedPercentageB = calculateModeledPercentage(grouped[b]); - - // Sort first by supported percentage ascending - if (supportedPercentageA > supportedPercentageB) { - return 1; - } - if (supportedPercentageA < supportedPercentageB) { - return -1; - } - - const numberOfUsagesA = grouped[a].reduce( - (acc, curr) => acc + curr.usages.length, - 0, - ); - const numberOfUsagesB = grouped[b].reduce( - (acc, curr) => acc + curr.usages.length, - 0, - ); - - // If the number of usages is equal, sort by number of methods descending - if (numberOfUsagesA === numberOfUsagesB) { - const numberOfMethodsA = grouped[a].length; - const numberOfMethodsB = grouped[b].length; - - // If the number of methods is equal, sort by library name ascending - if (numberOfMethodsA === numberOfMethodsB) { - return a.localeCompare(b); - } - - return numberOfMethodsB - numberOfMethodsA; - } - - // Then sort by number of usages descending - return numberOfUsagesB - numberOfUsagesA; - }); - }, [grouped]); + const sortedGroupNames = useMemo(() => sortGroupNames(grouped), [grouped]); return ( <> diff --git a/extensions/ql-vscode/src/view/data-extensions-editor/__tests__/modeled.spec.ts b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/shared/modeled-percentage.spec.ts similarity index 90% rename from extensions/ql-vscode/src/view/data-extensions-editor/__tests__/modeled.spec.ts rename to extensions/ql-vscode/test/unit-tests/data-extensions-editor/shared/modeled-percentage.spec.ts index 6156ecd42..5380c37fa 100644 --- a/extensions/ql-vscode/src/view/data-extensions-editor/__tests__/modeled.spec.ts +++ b/extensions/ql-vscode/test/unit-tests/data-extensions-editor/shared/modeled-percentage.spec.ts @@ -1,4 +1,4 @@ -import { calculateModeledPercentage } from "../modeled"; +import { calculateModeledPercentage } from "../../../../src/data-extensions-editor/shared/modeled-percentage"; describe("calculateModeledPercentage", () => { it("when there are no external API usages", () => {