Add predicate-renaming support
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
import type { ChangeEvent } from "react";
|
import type { ChangeEvent } from "react";
|
||||||
import { Fragment, memo, useMemo, useRef, useState } from "react";
|
import {
|
||||||
|
Fragment,
|
||||||
|
memo,
|
||||||
|
useDeferredValue,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
import type {
|
import type {
|
||||||
SetPerformanceComparisonQueries,
|
SetPerformanceComparisonQueries,
|
||||||
ToComparePerformanceViewMessage,
|
ToComparePerformanceViewMessage,
|
||||||
@@ -13,6 +20,7 @@ import { formatDecimal } from "../../common/number";
|
|||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import { Codicon, ViewTitle, WarningBox } from "../common";
|
import { Codicon, ViewTitle, WarningBox } from "../common";
|
||||||
import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter";
|
import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter";
|
||||||
|
import { Renaming, RenamingInput } from "./RenamingInput";
|
||||||
|
|
||||||
const enum AbsentReason {
|
const enum AbsentReason {
|
||||||
NotSeen = "NotSeen",
|
NotSeen = "NotSeen",
|
||||||
@@ -381,9 +389,13 @@ function addOptionals(a: Optional<number>, b: Optional<number>) {
|
|||||||
/**
|
/**
|
||||||
* Returns a "fingerprint" from the given name, which is used to group together similar names.
|
* Returns a "fingerprint" from the given name, which is used to group together similar names.
|
||||||
*/
|
*/
|
||||||
export function getNameFingerprint(name: string) {
|
export function getNameFingerprint(name: string, renamings: Renaming[]) {
|
||||||
// For now just remove the hash from the name. We identify this as a '#' followed by exactly 8 hexadecimal characters.
|
for (const { patternRegexp, replacement } of renamings) {
|
||||||
return name.replace(/#[0-9a-f]{8}(?![0-9a-f])/g, "");
|
if (patternRegexp != null) {
|
||||||
|
name = name.replace(patternRegexp, replacement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Chevron({ expanded }: { expanded: boolean }) {
|
function Chevron({ expanded }: { expanded: boolean }) {
|
||||||
@@ -486,10 +498,17 @@ function ComparePerformanceWithData(props: {
|
|||||||
return { totalBefore, totalAfter, totalDiff };
|
return { totalBefore, totalAfter, totalDiff };
|
||||||
}, [rows, metric]);
|
}, [rows, metric]);
|
||||||
|
|
||||||
|
const [renamings, setRenamings] = useState<Renaming[]>(() => [
|
||||||
|
new Renaming("#[0-9a-f]{8}(?![0-9a-f])", "#"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Use deferred value to avoid expensive re-rendering for every keypress in the renaming editor
|
||||||
|
const deferredRenamings = useDeferredValue(renamings);
|
||||||
|
|
||||||
const rowGroups = useMemo(() => {
|
const rowGroups = useMemo(() => {
|
||||||
const groupedRows = new Map<string, Row[]>();
|
const groupedRows = new Map<string, Row[]>();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const fingerprint = getNameFingerprint(row.name);
|
const fingerprint = getNameFingerprint(row.name, deferredRenamings);
|
||||||
const rows = groupedRows.get(fingerprint);
|
const rows = groupedRows.get(fingerprint);
|
||||||
if (rows) {
|
if (rows) {
|
||||||
rows.push(row);
|
rows.push(row);
|
||||||
@@ -515,7 +534,7 @@ function ComparePerformanceWithData(props: {
|
|||||||
} satisfies RowGroup;
|
} satisfies RowGroup;
|
||||||
})
|
})
|
||||||
.sort(getSortOrder(sortOrder));
|
.sort(getSortOrder(sortOrder));
|
||||||
}, [rows, metric, sortOrder]);
|
}, [rows, metric, sortOrder, deferredRenamings]);
|
||||||
|
|
||||||
const rowGroupNames = useMemo(
|
const rowGroupNames = useMemo(
|
||||||
() => abbreviateRANames(rowGroups.map((group) => group.name)),
|
() => abbreviateRANames(rowGroups.map((group) => group.name)),
|
||||||
@@ -544,6 +563,7 @@ function ComparePerformanceWithData(props: {
|
|||||||
</label>
|
</label>
|
||||||
</WarningBox>
|
</WarningBox>
|
||||||
)}
|
)}
|
||||||
|
<RenamingInput renamings={renamings} setRenamings={setRenamings} />
|
||||||
Compare{" "}
|
Compare{" "}
|
||||||
<Dropdown
|
<Dropdown
|
||||||
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
|
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
import type { ChangeEvent } from "react";
|
||||||
|
import { styled } from "styled-components";
|
||||||
|
import {
|
||||||
|
VSCodeButton,
|
||||||
|
VSCodeTextField,
|
||||||
|
} from "@vscode/webview-ui-toolkit/react";
|
||||||
|
import { Codicon } from "../common";
|
||||||
|
|
||||||
|
export class Renaming {
|
||||||
|
patternRegexp: RegExp | undefined;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public pattern: string,
|
||||||
|
public replacement: string,
|
||||||
|
) {
|
||||||
|
this.patternRegexp = tryCompilePattern(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryCompilePattern(pattern: string): RegExp | undefined {
|
||||||
|
try {
|
||||||
|
return new RegExp(pattern, "i");
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Input = styled(VSCodeTextField)`
|
||||||
|
width: 20em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Row = styled.div`
|
||||||
|
display: flex;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Details = styled.details`
|
||||||
|
padding: 1em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface RenamingInputProps {
|
||||||
|
renamings: Renaming[];
|
||||||
|
setRenamings: (renamings: Renaming[]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RenamingInput(props: RenamingInputProps) {
|
||||||
|
const { renamings, setRenamings } = props;
|
||||||
|
return (
|
||||||
|
<Details>
|
||||||
|
<summary>Predicate renaming</summary>
|
||||||
|
<p>
|
||||||
|
The following regexp replacements are applied to every predicate name on
|
||||||
|
both sides. Predicates whose names clash after renaming are grouped
|
||||||
|
together. Can be used to correlate predicates that were renamed between
|
||||||
|
the two runs.
|
||||||
|
<br />
|
||||||
|
Can also be used to group related predicates, for example, renaming{" "}
|
||||||
|
<code>.*ssa.*</code> to <code>SSA</code> will group all SSA-related
|
||||||
|
predicates together.
|
||||||
|
</p>
|
||||||
|
{renamings.map((renaming, index) => (
|
||||||
|
<Row key={index}>
|
||||||
|
<Input
|
||||||
|
value={renaming.pattern}
|
||||||
|
placeholder="Pattern"
|
||||||
|
onInput={(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newRenamings = [...renamings];
|
||||||
|
newRenamings[index] = new Renaming(
|
||||||
|
e.target.value,
|
||||||
|
renaming.replacement,
|
||||||
|
);
|
||||||
|
setRenamings(newRenamings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Codicon name="search" slot="start" />
|
||||||
|
</Input>
|
||||||
|
<Input
|
||||||
|
value={renaming.replacement}
|
||||||
|
placeholder="Replacement"
|
||||||
|
onInput={(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const newRenamings = [...renamings];
|
||||||
|
newRenamings[index] = new Renaming(
|
||||||
|
renaming.pattern,
|
||||||
|
e.target.value,
|
||||||
|
);
|
||||||
|
setRenamings(newRenamings);
|
||||||
|
}}
|
||||||
|
></Input>
|
||||||
|
<VSCodeButton
|
||||||
|
onClick={() =>
|
||||||
|
setRenamings(renamings.filter((_, i) => i !== index))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Codicon name="trash" />
|
||||||
|
</VSCodeButton>
|
||||||
|
<br />
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
<VSCodeButton
|
||||||
|
onClick={() => setRenamings([...renamings, new Renaming("", "")])}
|
||||||
|
>
|
||||||
|
Add renaming rule
|
||||||
|
</VSCodeButton>
|
||||||
|
</Details>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user