Skip to content

Commit c21e889

Browse files
committed
Simplify diff component structure
1 parent ce577b0 commit c21e889

6 files changed

Lines changed: 109 additions & 174 deletions

File tree

Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<script lang="ts">
2-
import ConciseDiffView from "$lib/components/diff/ConciseDiffView.svelte";
3-
import { MultiFileDiffViewerState, requireEitherImage, type FileDetails, type ImageDiffDetails } from "$lib/diff-viewer.svelte";
42
import Spinner from "$lib/components/Spinner.svelte";
5-
import ImageDiff from "$lib/components/diff/ImageDiff.svelte";
63
import AddedOrRemovedImage from "$lib/components/diff/AddedOrRemovedImage.svelte";
4+
import ImageDiff from "$lib/components/diff/ImageDiff.svelte";
5+
import TextDiff from "$lib/components/diff/TextDiff.svelte";
6+
import { MultiFileDiffViewerState, requireEitherImage, type FileDetails, type ImageDiffDetails } from "$lib/diff-viewer.svelte";
77
import { GlobalOptions } from "$lib/global-options.svelte";
88
99
interface Props {
10-
value: FileDetails;
10+
file: FileDetails;
1111
}
1212
13-
let { value }: Props = $props();
13+
let { file }: Props = $props();
1414
1515
const viewer = MultiFileDiffViewerState.get();
1616
const globalOptions = GlobalOptions.get();
1717
18-
let collapsed = $derived(viewer.fileStates[value.index].collapsed);
19-
let emptyTextDiff = $derived(value.type === "text" && value.patchHeaderDiffOnly && globalOptions.omitPatchHeaderOnlyHunks);
18+
let collapsed = $derived(viewer.fileStates[file.index].collapsed);
19+
let emptyTextDiff = $derived(file.type === "text" && file.patchHeaderDiffOnly && globalOptions.omitPatchHeaderOnlyHunks);
2020
</script>
2121

2222
{#snippet imageDiff(image: ImageDiffDetails)}
@@ -35,8 +35,8 @@
3535
{/if}
3636
{/snippet}
3737

38-
{#if value.type === "image" && !collapsed}
39-
{@const image = value.image}
38+
{#if file.type === "image" && !collapsed}
39+
{@const image = file.image}
4040
<div class="border-b text-sm">
4141
{#if image.load}
4242
{@render imageDiff(image)}
@@ -53,33 +53,8 @@
5353
{/if}
5454
</div>
5555
{/if}
56-
{#if value.type === "text" && !collapsed && !emptyTextDiff}
56+
{#if file.type === "text" && !collapsed && !emptyTextDiff}
5757
<div class="border-b">
58-
<ConciseDiffView
59-
patch={value.structuredPatch}
60-
syntaxHighlighting={globalOptions.syntaxHighlighting}
61-
syntaxHighlightingTheme={globalOptions.syntaxHighlightingTheme}
62-
omitPatchHeaderOnlyHunks={globalOptions.omitPatchHeaderOnlyHunks}
63-
wordDiffs={globalOptions.wordDiffs}
64-
lineWrap={globalOptions.lineWrap}
65-
searchQuery={viewer.searchQueryDebounced.current}
66-
searchMatchingLines={() => viewer.searchResults.then((r) => r.lines.get(value))}
67-
activeSearchResult={viewer.activeSearchResult && viewer.activeSearchResult.file === value ? viewer.activeSearchResult.idx : undefined}
68-
bind:jumpToSearchResult={viewer.jumpToSearchResult}
69-
cache={viewer.diffViewCache}
70-
cacheKey={value}
71-
unresolvedSelection={viewer.getSelection(value)?.unresolvedLines}
72-
bind:selection={
73-
() => viewer.getSelection(value)?.lines,
74-
(lines) => {
75-
if (lines === undefined && viewer.selection?.file === value) {
76-
viewer.clearSelection();
77-
} else {
78-
viewer.setSelection(value, lines);
79-
}
80-
}
81-
}
82-
bind:jumpToSelection={viewer.jumpToSelection}
83-
/>
58+
<TextDiff {file} />
8459
</div>
8560
{/if}

web/src/lib/components/diff/ConciseDiffView.svelte renamed to web/src/lib/components/diff/TextDiff.svelte

Lines changed: 44 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,55 @@
1-
<script lang="ts" generics="K">
1+
<script lang="ts">
22
import {
3-
type ConciseDiffViewProps,
4-
ConciseDiffViewState,
3+
TextDiffState,
54
type DiffViewerPatchHunk,
65
innerPatchLineTypeProps,
76
type InnerPatchLineTypeProps,
87
makeSearchSegments,
9-
parseSinglePatch,
108
type PatchLine,
119
PatchLineType,
1210
type PatchLineTypeProps,
1311
patchLineTypeProps,
1412
type SearchSegment,
15-
} from "$lib/components/diff/concise-diff-view.svelte";
13+
} from "$lib/components/diff/text-diff.svelte";
1614
import Spinner from "$lib/components/Spinner.svelte";
15+
import { GlobalOptions } from "$lib/global-options.svelte";
16+
import { MultiFileDiffViewerState, type TextFileDetails } from "$lib/diff-viewer.svelte";
1717
import { type MutableValue } from "$lib/util";
1818
import { box } from "svelte-toolbelt";
1919
import { boolAttr } from "runed";
2020
21-
let {
22-
rawPatchContent,
23-
patch,
24-
syntaxHighlighting = true,
25-
syntaxHighlightingTheme,
26-
omitPatchHeaderOnlyHunks = true,
27-
wordDiffs = true,
28-
lineWrap = true,
29-
searchQuery,
30-
searchMatchingLines,
31-
activeSearchResult = -1,
32-
jumpToSearchResult = $bindable(false),
33-
cache,
34-
cacheKey,
35-
unresolvedSelection,
36-
selection = $bindable(),
37-
jumpToSelection = $bindable(false),
38-
}: ConciseDiffViewProps<K> = $props();
21+
interface Props {
22+
file: TextFileDetails;
23+
}
3924
40-
const uid = $props.id();
25+
let { file }: Props = $props();
4126
42-
const parsedPatch = $derived.by(() => {
43-
if (rawPatchContent !== undefined) {
44-
return parseSinglePatch(rawPatchContent);
45-
} else if (patch !== undefined) {
46-
return patch;
47-
}
48-
throw Error("Either rawPatchContent or patch must be provided");
49-
});
27+
const uid = $props.id();
28+
const viewer = MultiFileDiffViewerState.get();
29+
const globalOptions = GlobalOptions.get();
5030
51-
const view = new ConciseDiffViewState({
31+
const view = new TextDiffState({
5232
rootElementId: uid,
5333
54-
patch: box.with(() => parsedPatch),
55-
syntaxHighlighting: box.with(() => syntaxHighlighting),
56-
syntaxHighlightingTheme: box.with(() => syntaxHighlightingTheme),
57-
omitPatchHeaderOnlyHunks: box.with(() => omitPatchHeaderOnlyHunks),
58-
wordDiffs: box.with(() => wordDiffs),
34+
patch: box.with(() => file.structuredPatch),
35+
syntaxHighlighting: box.with(() => globalOptions.syntaxHighlighting),
36+
syntaxHighlightingTheme: box.with(() => globalOptions.syntaxHighlightingTheme),
37+
omitPatchHeaderOnlyHunks: box.with(() => globalOptions.omitPatchHeaderOnlyHunks),
38+
wordDiffs: box.with(() => globalOptions.wordDiffs),
5939
60-
unresolvedSelection: box.with(() => unresolvedSelection),
40+
unresolvedSelection: box.with(() => viewer.getSelection(file)?.unresolvedLines),
6141
selection: box.with(
62-
() => selection,
63-
(v) => (selection = v),
42+
() => viewer.getSelection(file)?.lines,
43+
(lines) => {
44+
if (lines === undefined && viewer.selection?.file === file) {
45+
viewer.clearSelection();
46+
} else {
47+
viewer.setSelection(file, lines);
48+
}
49+
},
6450
),
6551
66-
cache: box.with(() => cache),
67-
cacheKey: box.with(() => cacheKey),
52+
cache: box.with(() => viewer.diffViewCache),
6853
});
6954
7055
function getDisplayLineNo(line: PatchLine, num: number | undefined) {
@@ -76,10 +61,11 @@
7661
}
7762
7863
let searchSegments: Promise<SearchSegment[][][]> = $derived.by(async () => {
79-
if (!searchQuery || !searchMatchingLines) {
64+
const searchQuery = viewer.searchQueryDebounced.current;
65+
if (!searchQuery) {
8066
return [];
8167
}
82-
const matchingLines = await searchMatchingLines();
68+
const matchingLines = (await viewer.searchResults).lines.get(file);
8369
if (!matchingLines || matchingLines.length === 0) {
8470
return [];
8571
}
@@ -117,6 +103,8 @@
117103
return segments;
118104
});
119105
106+
let activeSearchResult = $derived(viewer.activeSearchResult?.file === file ? viewer.activeSearchResult.idx : -1);
107+
let selection = $derived(viewer.getSelection(file)?.lines);
120108
let selectionMidpoint = $derived.by(() => {
121109
if (!selection) return null;
122110
const startIdx = selection.start.idx;
@@ -125,9 +113,8 @@
125113
});
126114
127115
let heightEstimateRem = $derived.by(() => {
128-
if (!parsedPatch) return 1.25;
129-
const rawLineCount = parsedPatch.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0);
130-
const headerAndSpacerLines = parsedPatch.hunks.length * 2;
116+
const rawLineCount = file.structuredPatch.hunks.reduce((sum, hunk) => sum + hunk.lines.length, 0);
117+
const headerAndSpacerLines = file.structuredPatch.hunks.length * 2;
131118
const totalLines = rawLineCount + headerAndSpacerLines;
132119
return totalLines * 1.25;
133120
});
@@ -163,16 +150,16 @@
163150
{#each lineSearchSegments as searchSegment, index (index)}
164151
{#if searchSegment.highlighted}<span
165152
{@attach (element) => {
166-
if (jumpToSearchResult && searchSegment.id === activeSearchResult) {
153+
if (viewer.jumpToSearchResult && searchSegment.id === activeSearchResult) {
167154
element.scrollIntoView({ block: "center", inline: "center" });
168-
jumpToSearchResult = false;
155+
viewer.jumpToSearchResult = false;
169156
// See similar code & comment below around jumping to selections
170157
//const scheduledJump = setTimeout(() => {
171-
// jumpToSearchResult = false;
158+
// viewer.jumpToSearchResult = false;
172159
// element.scrollIntoView({ block: "center", inline: "center" });
173160
//}, 200);
174161
//return () => {
175-
// jumpToSearchResult = false;
162+
// viewer.jumpToSearchResult = false;
176163
// clearTimeout(scheduledJump);
177164
//};
178165
}
@@ -226,19 +213,19 @@
226213
data-selection-start={boolAttr(view.isSelectionStart(hunkIndex, lineIndex))}
227214
data-selection-end={boolAttr(view.isSelectionEnd(hunkIndex, lineIndex))}
228215
{@attach (element) => {
229-
if (jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) {
216+
if (viewer.jumpToSelection && selection && selection.hunk === hunkIndex && selectionMidpoint === lineIndex) {
230217
element.scrollIntoView({ block: "center", inline: "center" });
231-
jumpToSelection = false;
218+
viewer.jumpToSelection = false;
232219
// Need to schedule because otherwise the vlist rendering surrounding elements may shift things
233220
// and cause the element to scroll to the wrong position
234221
// This is not 100% reliable but is good enough for now
235222
//const scheduledJump = setTimeout(() => {
236-
// jumpToSelection = false;
223+
// viewer.jumpToSelection = false;
237224
// element.scrollIntoView({ block: "center", inline: "center" });
238225
//}, 200);
239226
//return () => {
240227
// if (scheduledJump) {
241-
// jumpToSelection = false;
228+
// viewer.jumpToSelection = false;
242229
// clearTimeout(scheduledJump);
243230
// }
244231
//};
@@ -261,7 +248,7 @@
261248
id={uid}
262249
style={rootStyle}
263250
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)]"
264-
data-wrap={lineWrap}
251+
data-wrap={globalOptions.lineWrap}
265252
>
266253
{#each diffViewerPatch.hunks as hunk, hunkIndex (hunkIndex)}
267254
{#each hunk.lines as line, lineIndex (lineIndex)}

0 commit comments

Comments
 (0)