feat: ignore diff whitespace (#1067)

Add an option to ignore diff whitespace for diffs.
This commit is contained in:
Solomon
2025-10-24 19:24:31 +01:00
committed by GitHub
parent c9a5609550
commit 3c36ee6cd5
7 changed files with 96 additions and 40 deletions

View File

@@ -25,7 +25,10 @@ import type { TaskAttempt } from 'shared/types';
import { useReview, type ReviewDraft } from '@/contexts/ReviewProvider';
import { CommentWidgetLine } from '@/components/diff/CommentWidgetLine';
import { ReviewCommentRenderer } from '@/components/diff/ReviewCommentRenderer';
import { useDiffViewMode } from '@/stores/useDiffViewStore';
import {
useDiffViewMode,
useIgnoreWhitespaceDiff,
} from '@/stores/useDiffViewStore';
import { useProject } from '@/contexts/project-context';
type Props = {
@@ -76,6 +79,7 @@ export default function DiffCard({
const theme = getActualTheme(config?.theme);
const { comments, drafts, setDraft } = useReview();
const globalMode = useDiffViewMode();
const ignoreWhitespace = useIgnoreWhitespaceDiff();
const { projectId } = useProject();
const oldName = diff.oldPath || undefined;
@@ -92,6 +96,11 @@ export default function DiffCard({
const newContentSafe = diff.newContent || '';
const isContentEqual = oldContentSafe === newContentSafe;
const diffOptions = useMemo(
() => (ignoreWhitespace ? { ignoreWhitespace: true as const } : undefined),
[ignoreWhitespace]
);
const diffFile = useMemo(() => {
if (isContentEqual || isOmitted) return null;
try {
@@ -103,7 +112,8 @@ export default function DiffCard({
newFileName,
newContentSafe,
oldLang,
newLang
newLang,
diffOptions
);
file.initRaw();
return file;
@@ -120,6 +130,7 @@ export default function DiffCard({
newLang,
oldContentSafe,
newContentSafe,
diffOptions,
]);
const add = isOmitted

View File

@@ -1,7 +1,11 @@
import { Columns, FileText } from 'lucide-react';
import { Columns, FileText, Pilcrow } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { cn } from '@/lib/utils';
import { useDiffViewMode, useDiffViewStore } from '@/stores/useDiffViewStore';
import {
useDiffViewMode,
useDiffViewStore,
useIgnoreWhitespaceDiff,
} from '@/stores/useDiffViewStore';
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
import {
Tooltip,
@@ -18,14 +22,19 @@ export default function DiffViewSwitch({ className }: Props) {
const { t } = useTranslation('tasks');
const mode = useDiffViewMode();
const setMode = useDiffViewStore((s) => s.setMode);
const ignoreWhitespace = useIgnoreWhitespaceDiff();
const setIgnoreWhitespace = useDiffViewStore((s) => s.setIgnoreWhitespace);
const whitespaceValue = ignoreWhitespace ? ['ignoreWhitespace'] : [];
return (
<TooltipProvider>
<div className={cn('inline-flex gap-4', className)}>
<ToggleGroup
type="single"
value={mode ?? ''}
onValueChange={(v) => v && setMode(v as 'unified' | 'split')}
className={cn('inline-flex gap-4', className)}
className="inline-flex gap-4"
aria-label="Diff view mode"
>
<Tooltip>
@@ -58,6 +67,32 @@ export default function DiffViewSwitch({ className }: Props) {
</TooltipContent>
</Tooltip>
</ToggleGroup>
<ToggleGroup
type="multiple"
value={whitespaceValue}
onValueChange={(values) =>
setIgnoreWhitespace(values.includes('ignoreWhitespace'))
}
className="inline-flex gap-4"
aria-label={t('diff.ignoreWhitespace')}
>
<Tooltip>
<TooltipTrigger asChild>
<ToggleGroupItem
value="ignoreWhitespace"
aria-label={t('diff.ignoreWhitespace')}
active={ignoreWhitespace}
>
<Pilcrow className="h-4 w-4" />
</ToggleGroupItem>
</TooltipTrigger>
<TooltipContent side="bottom">
{t('diff.ignoreWhitespace')}
</TooltipContent>
</Tooltip>
</ToggleGroup>
</div>
</TooltipProvider>
);
}

View File

@@ -91,6 +91,7 @@
"inline": "Inline view",
"split": "Split view"
},
"ignoreWhitespace": "Ignore whitespace changes",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
"collapseAll": "Collapse all diffs",

View File

@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
"ignoreWhitespace": "Ignorar cambios de espacios en blanco",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",

View File

@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
"ignoreWhitespace": "空白の変更を無視",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",

View File

@@ -75,6 +75,7 @@
"collapseAll": "Collapse all diffs",
"errorLoadingDiff": "Failed to load diff: {{error}}",
"expandAll": "Expand all diffs",
"ignoreWhitespace": "공백 변경 무시",
"filesChanged_one": "{{count}} file changed",
"filesChanged_other": "{{count}} files changed",
"noChanges": "No changes have been made yet",

View File

@@ -6,6 +6,8 @@ type State = {
mode: DiffViewMode;
setMode: (mode: DiffViewMode) => void;
toggle: () => void;
ignoreWhitespace: boolean;
setIgnoreWhitespace: (value: boolean) => void;
};
export const useDiffViewStore = create<State>((set) => ({
@@ -13,6 +15,10 @@ export const useDiffViewStore = create<State>((set) => ({
setMode: (mode) => set({ mode }),
toggle: () =>
set((s) => ({ mode: s.mode === 'unified' ? 'split' : 'unified' })),
ignoreWhitespace: true,
setIgnoreWhitespace: (value) => set({ ignoreWhitespace: value }),
}));
export const useDiffViewMode = () => useDiffViewStore((s) => s.mode);
export const useIgnoreWhitespaceDiff = () =>
useDiffViewStore((s) => s.ignoreWhitespace);