Skip to content

Commit d469b9c

Browse files
committed
Consolidate diff viewer state, various cleanup around that change
1 parent 7ddc87d commit d469b9c

File tree

9 files changed

+301
-316
lines changed

9 files changed

+301
-316
lines changed

web/src/lib/components/diff/ConciseDiffView.svelte

Lines changed: 21 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import {
33
type ConciseDiffViewProps,
44
ConciseDiffViewState,
5-
getBaseColors,
65
innerPatchLineTypeProps,
76
type InnerPatchLineTypeProps,
87
makeSearchSegments,
@@ -54,20 +53,6 @@
5453
cacheKey: box.with(() => cacheKey),
5554
});
5655
57-
let baseColors: Promise<string> = $state(new Promise<string>(() => []));
58-
$effect(() => {
59-
const promise = getBaseColors(syntaxHighlightingTheme, syntaxHighlighting);
60-
// Same idea as above
61-
promise.then(
62-
() => {
63-
baseColors = promise;
64-
},
65-
() => {
66-
baseColors = promise;
67-
},
68-
);
69-
});
70-
7156
function getDisplayLineNo(line: PatchLine, num: number | undefined) {
7257
if (line.type == PatchLineType.HEADER) {
7358
return "...";
@@ -117,10 +102,11 @@
117102
if (!matchingLines || matchingLines.length === 0) {
118103
return [];
119104
}
120-
const lines = await view.patchLines;
105+
const diffViewerPatch = await view.diffViewerPatch;
106+
const hunks = diffViewerPatch.hunks;
121107
const segments: SearchSegment[][][] = [];
122108
const count: MutableValue<number> = { value: 0 };
123-
for (let i = 0; i < lines.length; i++) {
109+
for (let i = 0; i < hunks.length; i++) {
124110
const hunkMatchingLines = matchingLines[i];
125111
if (!hunkMatchingLines || hunkMatchingLines.length === 0) {
126112
continue;
@@ -129,7 +115,7 @@
129115
const hunkSegments: SearchSegment[][] = [];
130116
segments[i] = hunkSegments;
131117
132-
const hunkLines = lines[i];
118+
const hunkLines = hunks[i].lines;
133119
for (let j = 0; j < hunkLines.length; j++) {
134120
const line = hunkLines[j];
135121
@@ -170,24 +156,24 @@
170156
{#await searchSegments}
171157
{@render lineContent(line, lineType, innerLineType)}
172158
{:then completedSearchSegments}
173-
{@const hunkSegments = completedSearchSegments[hunkIndex]}
174-
{#if hunkSegments !== undefined && hunkSegments.length > 0}
175-
{@const lineSegments = hunkSegments[lineIndex]}
176-
{#if lineSegments !== undefined && lineSegments.length > 0}
159+
{@const hunkSearchSegments = completedSearchSegments[hunkIndex]}
160+
{#if hunkSearchSegments !== undefined && hunkSearchSegments.length > 0}
161+
{@const lineSearchSegments = hunkSearchSegments[lineIndex]}
162+
{#if lineSearchSegments !== undefined && lineSearchSegments.length > 0}
177163
<div class="relative">
178164
{@render lineContent(line, lineType, innerLineType)}
179165
<span class="pointer-events-none absolute top-0 left-0 text-transparent select-none">
180166
<span class="inline leading-[0.875rem]">
181-
{#each lineSegments as segment, index (index)}
182-
{#if segment.highlighted}<span
183-
bind:this={searchResultElements[segment.id ?? -1]}
167+
{#each lineSearchSegments as searchSegment, index (index)}
168+
{#if searchSegment.highlighted}<span
169+
bind:this={searchResultElements[searchSegment.id ?? -1]}
184170
class={{
185-
"bg-[#d4a72c66]": segment.id !== activeSearchResult,
186-
"bg-[#ff9632]": segment.id === activeSearchResult,
187-
"text-em-high-light": segment.id === activeSearchResult,
171+
"bg-[#d4a72c66]": searchSegment.id !== activeSearchResult,
172+
"bg-[#ff9632]": searchSegment.id === activeSearchResult,
173+
"text-em-high-light": searchSegment.id === activeSearchResult,
188174
}}
189-
data-match-id={segment.id}>{segment.text}</span
190-
>{:else}{segment.text}{/if}
175+
data-match-id={searchSegment.id}>{searchSegment.text}</span
176+
>{:else}{searchSegment.text}{/if}
191177
{/each}
192178
</span>
193179
</span>
@@ -214,16 +200,16 @@
214200
</div>
215201
{/snippet}
216202

217-
{#await Promise.all([baseColors, view.patchLines])}
203+
{#await Promise.all([view.rootStyle, view.diffViewerPatch])}
218204
<div class="flex items-center justify-center bg-neutral-2 p-4"><Spinner /></div>
219-
{:then [baseColors, lines]}
205+
{:then [rootStyle, diffViewerPatch]}
220206
<div
221-
style={baseColors}
207+
style={rootStyle}
222208
class="diff-content text-patch-line w-full bg-[var(--editor-bg)] font-mono text-xs leading-[1.25rem] text-[var(--editor-fg)] selection:bg-[var(--select-bg)]"
223209
data-wrap={lineWrap}
224210
>
225-
{#each lines as hunkLines, hunkIndex (hunkIndex)}
226-
{#each hunkLines as line, lineIndex (lineIndex)}
211+
{#each diffViewerPatch.hunks as hunk, hunkIndex (hunkIndex)}
212+
{#each hunk.lines as line, lineIndex (lineIndex)}
227213
{@render renderLine(line, hunkIndex, lineIndex)}
228214
{/each}
229215
{/each}

web/src/lib/components/diff/concise-diff-view.svelte.ts

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,24 @@ import { onDestroy } from "svelte";
1919
export const DEFAULT_THEME_LIGHT: BundledTheme = "github-light-default";
2020
export const DEFAULT_THEME_DARK: BundledTheme = "github-dark-default";
2121

22+
export type DiffViewerPatch = {
23+
hunks: DiffViewerPatchHunk[];
24+
};
25+
26+
export type DiffViewerPatchHunk = {
27+
lines: PatchLine[];
28+
innerPatchHeaderChangesOnly?: boolean;
29+
};
30+
31+
export type PatchLine = {
32+
type: PatchLineType;
33+
content: LineSegment[];
34+
lineBreak?: boolean;
35+
innerPatchLineType: InnerPatchLineType;
36+
oldLineNo?: number;
37+
newLineNo?: number;
38+
};
39+
2240
export type LineSegment = {
2341
text?: string | null;
2442
iconClass?: string | null;
@@ -35,6 +53,12 @@ export enum PatchLineType {
3553
SPACER,
3654
}
3755

56+
export enum InnerPatchLineType {
57+
ADD,
58+
REMOVE,
59+
NONE,
60+
}
61+
3862
export type PatchLineTypeProps = {
3963
classes: string;
4064
lineNoClasses: string;
@@ -69,12 +93,6 @@ export const patchLineTypeProps: Record<PatchLineType, PatchLineTypeProps> = {
6993
},
7094
};
7195

72-
export enum InnerPatchLineType {
73-
ADD,
74-
REMOVE,
75-
NONE,
76-
}
77-
7896
export type InnerPatchLineTypeProps = {
7997
style: string;
8098
};
@@ -99,15 +117,6 @@ export const innerPatchLineTypeProps: Record<InnerPatchLineType, InnerPatchLineT
99117
},
100118
};
101119

102-
export type PatchLine = {
103-
type: PatchLineType;
104-
content: LineSegment[];
105-
lineBreak?: boolean;
106-
innerPatchLineType: InnerPatchLineType;
107-
oldLineNo?: number;
108-
newLineNo?: number;
109-
};
110-
111120
const joiner = "\uE000";
112121
const noTrailingNewlineMarker: string = joiner + joiner + ["PATCH", "ROULETTE", "NO", "TRAILING", "NEWLINE", "MARKER"].join(joiner) + joiner + joiner;
113122

@@ -613,40 +622,39 @@ async function withLineProcessor<R>(fn: (proc: LineProcessor) => Promise<R>): Pr
613622
}
614623
}
615624

616-
export async function makeLines(
625+
export async function parseDiffViewerPatch(
617626
patchPromise: StructuredPatch | Promise<StructuredPatch>,
618627
syntaxHighlighting: boolean,
619628
syntaxHighlightingTheme: BundledTheme | undefined,
620629
omitPatchHeaderOnlyHunks: boolean,
621630
wordDiffs: boolean,
622-
): Promise<PatchLine[][]> {
631+
): Promise<DiffViewerPatch> {
623632
const patch = await patchPromise;
624-
const lines: PatchLine[][] = [];
633+
const hunks: DiffViewerPatchHunk[] = [];
625634

626635
for (let i = 0; i < patch.hunks.length; i++) {
627636
const hunk = patch.hunks[i];
628-
const hunkLines = await makeHunkLines(patch, hunk, syntaxHighlighting, syntaxHighlightingTheme, omitPatchHeaderOnlyHunks, wordDiffs);
629-
lines.push(hunkLines);
637+
hunks.push(await makeHunk(patch, hunk, syntaxHighlighting, syntaxHighlightingTheme, omitPatchHeaderOnlyHunks, wordDiffs));
630638
}
631639

632-
return lines;
640+
return { hunks };
633641
}
634642

635-
async function makeHunkLines(
643+
async function makeHunk(
636644
patch: StructuredPatch,
637645
hunk: StructuredPatchHunk,
638646
syntaxHighlighting: boolean,
639647
syntaxHighlightingTheme: BundledTheme | undefined,
640648
omitPatchHeaderOnlyHunks: boolean,
641649
wordDiffs: boolean,
642-
): Promise<PatchLine[]> {
643-
const lines: PatchLine[] = [];
644-
650+
): Promise<DiffViewerPatchHunk> {
645651
// Skip this hunk if it only contains header changes
646652
if (omitPatchHeaderOnlyHunks && !hasNonHeaderChanges(hunk.lines)) {
647-
return lines;
653+
return { innerPatchHeaderChangesOnly: true, lines: [] };
648654
}
649655

656+
const lines: PatchLine[] = [];
657+
650658
// Add the hunk header
651659
const header = `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`;
652660
lines.push({
@@ -665,7 +673,7 @@ async function makeHunkLines(
665673
// Add a separator between hunks
666674
lines.push({ content: [{ text: "" }], type: PatchLineType.SPACER, innerPatchLineType: InnerPatchLineType.NONE });
667675

668-
return lines;
676+
return { lines };
669677
}
670678

671679
export function hasNonHeaderChanges(contentLines: string[]) {
@@ -971,14 +979,14 @@ async function getTheme(theme: BundledTheme | undefined): Promise<null | { defau
971979
}
972980

973981
export class ConciseDiffViewCachedState {
974-
patchLines: Promise<PatchLine[][]>;
982+
diffViewerPatch: Promise<DiffViewerPatch>;
975983
syntaxHighlighting: boolean;
976984
syntaxHighlightingTheme: BundledTheme | undefined;
977985
omitPatchHeaderOnlyHunks: boolean;
978986
wordDiffs: boolean;
979987

980-
constructor(patchLines: Promise<PatchLine[][]>, props: ConciseDiffViewStateProps<unknown>) {
981-
this.patchLines = patchLines;
988+
constructor(diffViewerPatch: Promise<DiffViewerPatch>, props: ConciseDiffViewStateProps<unknown>) {
989+
this.diffViewerPatch = diffViewerPatch;
982990
this.syntaxHighlighting = props.syntaxHighlighting.current;
983991
this.syntaxHighlightingTheme = props.syntaxHighlightingTheme.current;
984992
this.omitPatchHeaderOnlyHunks = props.omitPatchHeaderOnlyHunks.current;
@@ -1034,8 +1042,9 @@ export type ConciseDiffViewStateProps<K> = ReadableBoxedValues<{
10341042
}>;
10351043

10361044
export class ConciseDiffViewState<K> {
1037-
patchLines: Promise<PatchLine[][]> = $state(new Promise<PatchLine[][]>(() => []));
1045+
diffViewerPatch: Promise<DiffViewerPatch> = $state(new Promise<DiffViewerPatch>(() => []));
10381046
cachedState: ConciseDiffViewCachedState | undefined = undefined;
1047+
rootStyle: Promise<string> = $state(new Promise<string>(() => []));
10391048

10401049
private readonly props: ConciseDiffViewStateProps<K>;
10411050

@@ -1046,6 +1055,18 @@ export class ConciseDiffViewState<K> {
10461055
this.update();
10471056
});
10481057

1058+
$effect(() => {
1059+
const promise = getBaseColors(this.props.syntaxHighlightingTheme.current, this.props.syntaxHighlighting.current);
1060+
promise.then(
1061+
() => {
1062+
this.rootStyle = promise;
1063+
},
1064+
() => {
1065+
this.rootStyle = promise;
1066+
},
1067+
);
1068+
});
1069+
10491070
onDestroy(() => {
10501071
if (this.props.cache.current !== undefined && this.props.cacheKey.current !== undefined && this.cachedState !== undefined) {
10511072
this.props.cache.current.set(this.props.cacheKey.current, this.cachedState);
@@ -1068,7 +1089,7 @@ export class ConciseDiffViewState<K> {
10681089
}
10691090
}
10701091

1071-
const promise = makeLines(
1092+
const promise = parseDiffViewerPatch(
10721093
this.props.patch.current,
10731094
this.props.syntaxHighlighting.current,
10741095
this.props.syntaxHighlightingTheme.current,
@@ -1079,17 +1100,17 @@ export class ConciseDiffViewState<K> {
10791100
promise.then(
10801101
() => {
10811102
// Don't replace a potentially completed promise with a pending one, wait until the replacement is ready for smooth transitions
1082-
this.patchLines = promise;
1103+
this.diffViewerPatch = promise;
10831104
},
10841105
() => {
10851106
// Propagate errors
1086-
this.patchLines = promise;
1107+
this.diffViewerPatch = promise;
10871108
},
10881109
);
10891110
}
10901111

10911112
restore(state: ConciseDiffViewCachedState) {
1092-
this.patchLines = state.patchLines;
1113+
this.diffViewerPatch = state.diffViewerPatch;
10931114
this.cachedState = state;
10941115
}
10951116
}

0 commit comments

Comments
 (0)