Skip to content

Commit 80864c9

Browse files
committed
Use react-window for the c source line view
This is so both the log lines and the c-lines use the same rendering mechanism. It also simplifies scrolling to the right c-line.
1 parent 30fd5e2 commit 80864c9

File tree

6 files changed

+381
-451
lines changed

6 files changed

+381
-451
lines changed

src/App.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ describe("App", () => {
152152
);
153153

154154
const cSourceEl = document.getElementById("c-source-container");
155-
const cSourceFile = cSourceEl?.querySelector(".c-source-file");
155+
const cSourceContent = document.getElementById("c-source-content");
156156

157-
expect(cSourceFile).toBeVisible();
157+
expect(cSourceContent).toBeVisible();
158158

159159
const cSourceHideShow = cSourceEl?.querySelector(".hide-show-button");
160160

@@ -163,7 +163,7 @@ describe("App", () => {
163163
}
164164

165165
fireEvent(cSourceHideShow, createEvent.click(cSourceHideShow));
166-
expect(cSourceFile).not.toBeVisible();
166+
expect(cSourceContent).not.toBeVisible();
167167

168168
const statePanelEl = document.getElementById("state-panel");
169169
const statePanelHeader = document.getElementById("state-panel-header");

src/App.tsx

Lines changed: 78 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,13 @@
1-
import React, { RefObject } from "react";
1+
import React from "react";
22
import { useCallback, useEffect, useRef, useState } from "react";
3-
import { ListImperativeAPI, useListRef } from "react-window";
43

54
import {
65
VerifierLogState,
76
processRawLines,
87
getEmptyVerifierState,
98
} from "./analyzer";
109

11-
import {
12-
fetchLogFromUrl,
13-
scrollToCLine,
14-
getVisibleLogLines,
15-
getVisibleCLines,
16-
} from "./utils";
10+
import { fetchLogFromUrl } from "./utils";
1711

1812
import {
1913
VisualLogState,
@@ -23,8 +17,9 @@ import {
2317
SelectedLineHint,
2418
ToolTip,
2519
Examples,
20+
CSourceRow,
2621
} from "./components";
27-
import { ParsedLineType } from "./parser";
22+
import { getCLineId, ParsedLine, ParsedLineType } from "./parser";
2823

2924
function getEmptyVisualLogState(): VisualLogState {
3025
return {
@@ -45,6 +40,79 @@ function getEmptyLogLineState(): LogLineState {
4540
};
4641
}
4742

43+
function getVisibleLogLines(
44+
verifierLogState: VerifierLogState,
45+
fullLogView: boolean,
46+
): [ParsedLine[], Map<number, number>] {
47+
const logLines: ParsedLine[] = [];
48+
const logLineIdxToVisualIdx: Map<number, number> = new Map();
49+
50+
let visualIdx = 0;
51+
verifierLogState.lines.forEach((line) => {
52+
if (line.type !== ParsedLineType.C_SOURCE || fullLogView) {
53+
logLines.push(line);
54+
logLineIdxToVisualIdx.set(line.idx, visualIdx++);
55+
}
56+
});
57+
58+
return [logLines, logLineIdxToVisualIdx];
59+
}
60+
61+
function getVisibleCLines(
62+
verifierLogState: VerifierLogState,
63+
): [CSourceRow[], Map<string, number>] {
64+
const cLineIdToVisualIdx: Map<string, number> = new Map();
65+
const cLines: CSourceRow[] = [];
66+
67+
let j = 0;
68+
for (const [file, range] of verifierLogState.cSourceMap.fileRange) {
69+
cLines.push({
70+
type: "file_name",
71+
file,
72+
});
73+
++j;
74+
75+
let unknownStart = 0;
76+
for (let i = range[0]; i < range[1]; ++i) {
77+
const sourceId = getCLineId(file, i);
78+
const sourceLine = verifierLogState.cSourceMap.cSourceLines.get(sourceId);
79+
if (sourceLine?.lineNum === 0) {
80+
continue;
81+
}
82+
if (!sourceLine || sourceLine.ignore) {
83+
if (!unknownStart) {
84+
unknownStart = i;
85+
}
86+
continue;
87+
}
88+
if (unknownStart > 0) {
89+
cLines.push({
90+
type: "c_line",
91+
file,
92+
lineNum: unknownStart,
93+
lineText: `.. ${i - 1}`,
94+
sourceId: "none",
95+
ignore: true,
96+
});
97+
++j;
98+
}
99+
unknownStart = 0;
100+
cLines.push({
101+
type: "c_line",
102+
file,
103+
lineNum: i,
104+
lineText: sourceLine.content,
105+
sourceId,
106+
ignore: false,
107+
});
108+
cLineIdToVisualIdx.set(sourceId, j);
109+
++j;
110+
}
111+
}
112+
113+
return [cLines, cLineIdToVisualIdx];
114+
}
115+
48116
function getVisualLogState(
49117
verifierLogState: VerifierLogState,
50118
showFullLog: boolean,
@@ -73,7 +141,6 @@ const ContentRaw = ({
73141
handleLogLinesOver,
74142
handleLogLinesOut,
75143
handleFullLogToggle,
76-
logListRef,
77144
testListHeight,
78145
}: {
79146
loadError: string | null;
@@ -84,7 +151,6 @@ const ContentRaw = ({
84151
handleLogLinesOver: (event: React.MouseEvent<HTMLDivElement>) => void;
85152
handleLogLinesOut: (event: React.MouseEvent<HTMLDivElement>) => void;
86153
handleFullLogToggle: () => void;
87-
logListRef: RefObject<ListImperativeAPI | null>;
88154
testListHeight: number | undefined;
89155
}) => {
90156
if (loadError) {
@@ -98,7 +164,6 @@ const ContentRaw = ({
98164
handleLogLinesOver={handleLogLinesOver}
99165
handleLogLinesOut={handleLogLinesOut}
100166
handleFullLogToggle={handleFullLogToggle}
101-
logListRef={logListRef}
102167
testListHeight={testListHeight}
103168
/>
104169
);
@@ -132,50 +197,14 @@ function App({ testListHeight }: { testListHeight?: number }) {
132197
const [isLoading, setIsLoading] = useState<boolean>(false);
133198

134199
const fileInputRef = useRef<HTMLInputElement>(null);
135-
const logListRef = useListRef(null);
136200

137-
const {
138-
verifierLogState,
139-
cLines,
140-
cLineIdToVisualIdx,
141-
logLines,
142-
logLineIdxToVisualIdx,
143-
} = visualLogState;
201+
const { verifierLogState, logLineIdxToVisualIdx } = visualLogState;
144202

145203
const { line: selectedLine } = selectedState;
146204
const selectedLineVisualIdx = logLineIdxToVisualIdx.get(selectedLine) || 0;
147205
const hoveredLineVisualIdx =
148206
logLineIdxToVisualIdx.get(hoveredState.line) || 0;
149207

150-
const scrollToLogLine = useCallback(
151-
(index: number) => {
152-
if (index < 0) {
153-
return;
154-
}
155-
const list = logListRef.current;
156-
list?.scrollToRow({
157-
index,
158-
align: "center",
159-
});
160-
},
161-
[logListRef],
162-
);
163-
164-
const setSelectedAndScroll = useCallback(
165-
(
166-
nextInsLineId: number,
167-
nextCLineId: string,
168-
nextInsLineVisualIdx: number,
169-
nextCLineVisualIdx: number,
170-
memSlotId: string = "",
171-
) => {
172-
scrollToLogLine(nextInsLineVisualIdx);
173-
scrollToCLine(nextCLineVisualIdx, cLines.length);
174-
setSelectedState({ line: nextInsLineId, memSlotId, cLine: nextCLineId });
175-
},
176-
[logLines, cLineIdToVisualIdx],
177-
);
178-
179208
const onClear = useCallback(() => {
180209
setVisualLogState(getEmptyVisualLogState());
181210
setSelectedState({ line: 0, memSlotId: "", cLine: "" });
@@ -200,24 +229,6 @@ function App({ testListHeight }: { testListHeight?: number }) {
200229
});
201230
}, [verifierLogState]);
202231

203-
// When a new log is loaded go to the last instruction
204-
useEffect(() => {
205-
const visualIdx = logLineIdxToVisualIdx.get(verifierLogState.lastInsIdx);
206-
if (visualIdx === undefined) {
207-
return;
208-
}
209-
const clineId =
210-
verifierLogState.cSourceMap.logLineToCLine.get(
211-
verifierLogState.lastInsIdx,
212-
) || "";
213-
setSelectedAndScroll(
214-
verifierLogState.lastInsIdx,
215-
"",
216-
visualIdx,
217-
cLineIdToVisualIdx.get(clineId) || 0,
218-
);
219-
}, [verifierLogState]);
220-
221232
const loadInputText = useCallback((text: string) => {
222233
const newVerifierLogState = processRawLines(text.split("\n"));
223234
setVisualLogState(getVisualLogState(newVerifierLogState, false));
@@ -279,50 +290,6 @@ function App({ testListHeight }: { testListHeight?: number }) {
279290
}
280291
}, [loadInputText]);
281292

282-
useEffect(() => {
283-
let logLines: Set<number> = new Set();
284-
let cLine: string;
285-
let cLineEl: HTMLElement | null;
286-
if (selectedState.cLine) {
287-
cLineEl = document.getElementById(`line-${selectedState.cLine}`);
288-
if (cLineEl) {
289-
cLineEl.classList.add("selected-line");
290-
}
291-
logLines =
292-
verifierLogState.cSourceMap.cLineToLogLines.get(selectedState.cLine) ||
293-
new Set();
294-
for (let logLine of logLines) {
295-
const logLineEl = document.getElementById(`line-${logLine}`);
296-
if (logLineEl) {
297-
logLineEl.classList.add("selected-line");
298-
}
299-
}
300-
} else if (selectedState.line) {
301-
const parsedLine = verifierLogState.lines[selectedState.line];
302-
cLine =
303-
parsedLine.type === ParsedLineType.C_SOURCE
304-
? parsedLine.id
305-
: verifierLogState.cSourceMap.logLineToCLine.get(
306-
selectedState.line,
307-
) || "";
308-
cLineEl = document.getElementById(`line-${cLine}`);
309-
if (cLineEl) {
310-
cLineEl.classList.add("selected-line");
311-
}
312-
}
313-
return () => {
314-
if (cLineEl) {
315-
cLineEl.classList.remove("selected-line");
316-
}
317-
for (let logLine of logLines) {
318-
const logLineEl = document.getElementById(`line-${logLine}`);
319-
if (logLineEl) {
320-
logLineEl.classList.remove("selected-line");
321-
}
322-
}
323-
};
324-
}, [verifierLogState, selectedState]);
325-
326293
const handleLogLinesOver = useCallback(
327294
(e: React.MouseEvent<HTMLDivElement>) => {
328295
const hoveredElement = e.target as HTMLElement;
@@ -428,7 +395,6 @@ function App({ testListHeight }: { testListHeight?: number }) {
428395
handleLogLinesOver={handleLogLinesOver}
429396
handleLogLinesOut={handleLogLinesOut}
430397
handleFullLogToggle={handleFullLogToggle}
431-
logListRef={logListRef}
432398
testListHeight={testListHeight}
433399
/>
434400
<div id="hint">

src/__snapshots__/App.test.tsx.snap

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,14 @@ exports[`App renders the log visualizer when text is pasted 1`] = `
142142
id="c-source-content"
143143
>
144144
<div
145-
id="c-source-files"
146-
/>
145+
role="list"
146+
style="position: relative; max-height: 100%; flex-grow: 1; overflow-y: auto;"
147+
>
148+
<div
149+
aria-hidden="true"
150+
style="height: 0px; width: 100%; z-index: -1;"
151+
/>
152+
</div>
147153
</div>
148154
</div>
149155
<div

0 commit comments

Comments
 (0)