Skip to content

Commit 2cae71c

Browse files
committed
Add predicate-renaming support
1 parent 568f082 commit 2cae71c

File tree

2 files changed

+132
-6
lines changed

2 files changed

+132
-6
lines changed

extensions/ql-vscode/src/view/compare-performance/ComparePerformance.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import type { ChangeEvent } from "react";
2-
import { Fragment, memo, useMemo, useRef, useState } from "react";
2+
import {
3+
Fragment,
4+
memo,
5+
useDeferredValue,
6+
useMemo,
7+
useRef,
8+
useState,
9+
} from "react";
310
import type {
411
SetPerformanceComparisonQueries,
512
ToComparePerformanceViewMessage,
@@ -13,6 +20,7 @@ import { formatDecimal } from "../../common/number";
1320
import { styled } from "styled-components";
1421
import { Codicon, ViewTitle, WarningBox } from "../common";
1522
import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter";
23+
import { Renaming, RenamingInput } from "./RenamingInput";
1624

1725
const enum AbsentReason {
1826
NotSeen = "NotSeen",
@@ -381,9 +389,13 @@ function addOptionals(a: Optional<number>, b: Optional<number>) {
381389
/**
382390
* Returns a "fingerprint" from the given name, which is used to group together similar names.
383391
*/
384-
export function getNameFingerprint(name: string) {
385-
// For now just remove the hash from the name. We identify this as a '#' followed by exactly 8 hexadecimal characters.
386-
return name.replace(/#[0-9a-f]{8}(?![0-9a-f])/g, "");
392+
export function getNameFingerprint(name: string, renamings: Renaming[]) {
393+
for (const { patternRegexp, replacement } of renamings) {
394+
if (patternRegexp != null) {
395+
name = name.replace(patternRegexp, replacement);
396+
}
397+
}
398+
return name;
387399
}
388400

389401
function Chevron({ expanded }: { expanded: boolean }) {
@@ -486,10 +498,17 @@ function ComparePerformanceWithData(props: {
486498
return { totalBefore, totalAfter, totalDiff };
487499
}, [rows, metric]);
488500

501+
const [renamings, setRenamings] = useState<Renaming[]>(() => [
502+
new Renaming("#[0-9a-f]{8}(?![0-9a-f])", "#"),
503+
]);
504+
505+
// Use deferred value to avoid expensive re-rendering for every keypress in the renaming editor
506+
const deferredRenamings = useDeferredValue(renamings);
507+
489508
const rowGroups = useMemo(() => {
490509
const groupedRows = new Map<string, Row[]>();
491510
for (const row of rows) {
492-
const fingerprint = getNameFingerprint(row.name);
511+
const fingerprint = getNameFingerprint(row.name, deferredRenamings);
493512
const rows = groupedRows.get(fingerprint);
494513
if (rows) {
495514
rows.push(row);
@@ -515,7 +534,7 @@ function ComparePerformanceWithData(props: {
515534
} satisfies RowGroup;
516535
})
517536
.sort(getSortOrder(sortOrder));
518-
}, [rows, metric, sortOrder]);
537+
}, [rows, metric, sortOrder, deferredRenamings]);
519538

520539
const rowGroupNames = useMemo(
521540
() => abbreviateRANames(rowGroups.map((group) => group.name)),
@@ -544,6 +563,7 @@ function ComparePerformanceWithData(props: {
544563
</label>
545564
</WarningBox>
546565
)}
566+
<RenamingInput renamings={renamings} setRenamings={setRenamings} />
547567
Compare{" "}
548568
<Dropdown
549569
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import type { ChangeEvent } from "react";
2+
import { styled } from "styled-components";
3+
import {
4+
VSCodeButton,
5+
VSCodeTextField,
6+
} from "@vscode/webview-ui-toolkit/react";
7+
import { Codicon } from "../common";
8+
9+
export class Renaming {
10+
patternRegexp: RegExp | undefined;
11+
12+
constructor(
13+
public pattern: string,
14+
public replacement: string,
15+
) {
16+
this.patternRegexp = tryCompilePattern(pattern);
17+
}
18+
}
19+
20+
function tryCompilePattern(pattern: string): RegExp | undefined {
21+
try {
22+
return new RegExp(pattern, "i");
23+
} catch {
24+
return undefined;
25+
}
26+
}
27+
28+
const Input = styled(VSCodeTextField)`
29+
width: 20em;
30+
`;
31+
32+
const Row = styled.div`
33+
display: flex;
34+
padding-bottom: 0.25em;
35+
`;
36+
37+
const Details = styled.details`
38+
padding: 1em;
39+
`;
40+
41+
interface RenamingInputProps {
42+
renamings: Renaming[];
43+
setRenamings: (renamings: Renaming[]) => void;
44+
}
45+
46+
export function RenamingInput(props: RenamingInputProps) {
47+
const { renamings, setRenamings } = props;
48+
return (
49+
<Details>
50+
<summary>Predicate renaming</summary>
51+
<p>
52+
The following regexp replacements are applied to every predicate name on
53+
both sides. Predicates whose names clash after renaming are grouped
54+
together. Can be used to correlate predicates that were renamed between
55+
the two runs.
56+
<br />
57+
Can also be used to group related predicates, for example, renaming{" "}
58+
<code>.*ssa.*</code> to <code>SSA</code> will group all SSA-related
59+
predicates together.
60+
</p>
61+
{renamings.map((renaming, index) => (
62+
<Row key={index}>
63+
<Input
64+
value={renaming.pattern}
65+
placeholder="Pattern"
66+
onInput={(e: ChangeEvent<HTMLInputElement>) => {
67+
const newRenamings = [...renamings];
68+
newRenamings[index] = new Renaming(
69+
e.target.value,
70+
renaming.replacement,
71+
);
72+
setRenamings(newRenamings);
73+
}}
74+
>
75+
<Codicon name="search" slot="start" />
76+
</Input>
77+
<Input
78+
value={renaming.replacement}
79+
placeholder="Replacement"
80+
onInput={(e: ChangeEvent<HTMLInputElement>) => {
81+
const newRenamings = [...renamings];
82+
newRenamings[index] = new Renaming(
83+
renaming.pattern,
84+
e.target.value,
85+
);
86+
setRenamings(newRenamings);
87+
}}
88+
></Input>
89+
<VSCodeButton
90+
onClick={() =>
91+
setRenamings(renamings.filter((_, i) => i !== index))
92+
}
93+
>
94+
<Codicon name="trash" />
95+
</VSCodeButton>
96+
<br />
97+
</Row>
98+
))}
99+
<VSCodeButton
100+
onClick={() => setRenamings([...renamings, new Renaming("", "")])}
101+
>
102+
Add renaming rule
103+
</VSCodeButton>
104+
</Details>
105+
);
106+
}

0 commit comments

Comments
 (0)