Add predicate-renaming support

This commit is contained in:
Asger F
2024-11-25 16:37:26 +01:00
parent 568f0827b2
commit 2cae71c657
2 changed files with 132 additions and 6 deletions

View File

@@ -1,5 +1,12 @@
import type { ChangeEvent } from "react";
import { Fragment, memo, useMemo, useRef, useState } from "react";
import {
Fragment,
memo,
useDeferredValue,
useMemo,
useRef,
useState,
} from "react";
import type {
SetPerformanceComparisonQueries,
ToComparePerformanceViewMessage,
@@ -13,6 +20,7 @@ import { formatDecimal } from "../../common/number";
import { styled } from "styled-components";
import { Codicon, ViewTitle, WarningBox } from "../common";
import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter";
import { Renaming, RenamingInput } from "./RenamingInput";
const enum AbsentReason {
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.
*/
export function getNameFingerprint(name: string) {
// For now just remove the hash from the name. We identify this as a '#' followed by exactly 8 hexadecimal characters.
return name.replace(/#[0-9a-f]{8}(?![0-9a-f])/g, "");
export function getNameFingerprint(name: string, renamings: Renaming[]) {
for (const { patternRegexp, replacement } of renamings) {
if (patternRegexp != null) {
name = name.replace(patternRegexp, replacement);
}
}
return name;
}
function Chevron({ expanded }: { expanded: boolean }) {
@@ -486,10 +498,17 @@ function ComparePerformanceWithData(props: {
return { totalBefore, totalAfter, totalDiff };
}, [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 groupedRows = new Map<string, Row[]>();
for (const row of rows) {
const fingerprint = getNameFingerprint(row.name);
const fingerprint = getNameFingerprint(row.name, deferredRenamings);
const rows = groupedRows.get(fingerprint);
if (rows) {
rows.push(row);
@@ -515,7 +534,7 @@ function ComparePerformanceWithData(props: {
} satisfies RowGroup;
})
.sort(getSortOrder(sortOrder));
}, [rows, metric, sortOrder]);
}, [rows, metric, sortOrder, deferredRenamings]);
const rowGroupNames = useMemo(
() => abbreviateRANames(rowGroups.map((group) => group.name)),
@@ -544,6 +563,7 @@ function ComparePerformanceWithData(props: {
</label>
</WarningBox>
)}
<RenamingInput renamings={renamings} setRenamings={setRenamings} />
Compare{" "}
<Dropdown
onChange={(e: ChangeEvent<HTMLSelectElement>) =>

View File

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