diff --git a/web/src/lib/components/diff-filtering/DiffFilterDialog.svelte b/web/src/lib/components/diff-filtering/DiffFilterDialog.svelte new file mode 100644 index 0000000..a15d82c --- /dev/null +++ b/web/src/lib/components/diff-filtering/DiffFilterDialog.svelte @@ -0,0 +1,131 @@ + + + + + + +
+ Edit Filters + + + +
+ +
+
File Status
+ + {#each FILE_STATUSES as status (status)} + {@const statusProps = getFileStatusProps(status)} + + + {statusProps.title} + + {/each} + + {#if instance.selectedFileStatuses.length === 0} +

No file statuses selected; all files will be excluded.

+ {/if} +
+ +
+
File Path
+
+
{ + e.preventDefault(); + if (!newFilePathFilterInputResult.success) return; + instance.addFilePathFilter(newFilePathFilterInputResult, newFilePathFilterMode); + newFilePathFilterInput = ""; + }} + > + + { + newFilePathFilterMode = newFilePathFilterMode === "exclude" ? "include" : "exclude"; + }} + > + {#if newFilePathFilterMode === "exclude"} + + {:else} + + {/if} + + + + +
+
    + {#each instance.reverseFilePathFilters as filter, i (i)} +
  • + + {filter.text} + +
    + {#if filter.mode === "exclude"} + + {:else} + + {/if} +
    + { + instance.filePathFilters.delete(filter); + }} + > + + +
  • + {/each} + {#if instance.reverseFilePathFilters.length === 0} +
  • No file path filters. Add one using the above form.
  • + {/if} +
+
+
+
+
+
diff --git a/web/src/lib/components/diff-filtering/index.svelte.ts b/web/src/lib/components/diff-filtering/index.svelte.ts new file mode 100644 index 0000000..a101ef8 --- /dev/null +++ b/web/src/lib/components/diff-filtering/index.svelte.ts @@ -0,0 +1,66 @@ +import type { FileDetails } from "$lib/diff-viewer.svelte"; +import { FILE_STATUSES } from "$lib/github.svelte"; +import type { TryCompileRegexSuccess } from "$lib/util"; +import { SvelteSet } from "svelte/reactivity"; + +export type FilePathFilterMode = "include" | "exclude"; +export class FilePathFilter { + text: string; + regex: RegExp; + mode: FilePathFilterMode; + + constructor(text: string, regex: RegExp, mode: FilePathFilterMode) { + this.text = $state(text); + this.regex = $state.raw(regex); + this.mode = $state(mode); + } +} + +export class DiffFilterDialogState { + filePathFilters = new SvelteSet(); + reverseFilePathFilters = $derived([...this.filePathFilters].toReversed()); + filterFunction = $derived(this.createFilterFunction()); + + selectedFileStatuses: string[] = $state([...FILE_STATUSES]); + + addFilePathFilter(regex: TryCompileRegexSuccess, mode: FilePathFilterMode) { + const newFilter = new FilePathFilter(regex.input, regex.regex, mode); + this.filePathFilters.add(newFilter); + } + + setDefaults() { + this.filePathFilters.clear(); + this.selectedFileStatuses = [...FILE_STATUSES]; + } + + filterFile(file: FileDetails): boolean { + return this.filterFunction(file); + } + + private createFilterFunction() { + const filePathInclusions = this.reverseFilePathFilters.filter((f) => f.mode === "include"); + const filePathExclusions = this.reverseFilePathFilters.filter((f) => f.mode === "exclude"); + const selectedFileStatuses = [...this.selectedFileStatuses]; + + return (file: FileDetails) => { + const statusAllowed = selectedFileStatuses.includes(file.status); + if (!statusAllowed) { + return false; + } + for (const exclude of filePathExclusions) { + if (exclude.regex.test(file.toFile) || exclude.regex.test(file.fromFile)) { + return false; + } + } + if (filePathInclusions.length > 0) { + for (const include of filePathInclusions) { + if (include.regex.test(file.toFile) || include.regex.test(file.fromFile)) { + return true; + } + } + return false; + } + return true; + }; + } +} diff --git a/web/src/lib/components/diff/concise-diff-view.svelte.ts b/web/src/lib/components/diff/concise-diff-view.svelte.ts index a19de04..a8a636b 100644 --- a/web/src/lib/components/diff/concise-diff-view.svelte.ts +++ b/web/src/lib/components/diff/concise-diff-view.svelte.ts @@ -1076,6 +1076,7 @@ async function getTheme(theme: BundledTheme | undefined): Promise; + cacheKey: unknown; syntaxHighlighting: boolean; syntaxHighlightingTheme: BundledTheme | undefined; omitPatchHeaderOnlyHunks: boolean; @@ -1083,6 +1084,7 @@ export class ConciseDiffViewCachedState { constructor(diffViewerPatch: Promise, props: ConciseDiffViewStateProps) { this.diffViewerPatch = diffViewerPatch; + this.cacheKey = props.cacheKey.current; this.syntaxHighlighting = props.syntaxHighlighting.current; this.syntaxHighlightingTheme = props.syntaxHighlightingTheme.current; this.omitPatchHeaderOnlyHunks = props.omitPatchHeaderOnlyHunks.current; @@ -1188,8 +1190,8 @@ export class ConciseDiffViewState { }); onDestroy(() => { - if (this.props.cache.current !== undefined && this.props.cacheKey.current !== undefined && this.cachedState !== undefined) { - this.props.cache.current.set(this.props.cacheKey.current, this.cachedState); + if (this.props.cache.current !== undefined && this.cachedState !== undefined) { + this.props.cache.current.set(this.cachedState.cacheKey as K, this.cachedState); } }); } diff --git a/web/src/lib/components/menu-bar/MenuBar.svelte b/web/src/lib/components/menu-bar/MenuBar.svelte index e376d8a..8da419c 100644 --- a/web/src/lib/components/menu-bar/MenuBar.svelte +++ b/web/src/lib/components/menu-bar/MenuBar.svelte @@ -53,7 +53,7 @@ { - viewer.openSettingsDialog(); + viewer.openDialog("settings"); }} > Open Settings @@ -69,7 +69,7 @@ { - viewer.openOpenDiffDialog(); + viewer.openDialog("open-diff"); }} > Open @@ -82,6 +82,14 @@ View + { + viewer.openDialog("diff-filter"); + }} + > + Edit Filters + { @@ -137,7 +145,7 @@ } }} > - Go to Selection + Jump to Selection diff --git a/web/src/routes/Sidebar.svelte b/web/src/lib/components/sidebar/Sidebar.svelte similarity index 90% rename from web/src/routes/Sidebar.svelte rename to web/src/lib/components/sidebar/Sidebar.svelte index b083aac..6040951 100644 --- a/web/src/routes/Sidebar.svelte +++ b/web/src/lib/components/sidebar/Sidebar.svelte @@ -1,16 +1,17 @@