feat: ignore diff whitespace (#1067)
Add an option to ignore diff whitespace for diffs.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,46 +22,77 @@ 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>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={mode ?? ''}
|
||||
onValueChange={(v) => v && setMode(v as 'unified' | 'split')}
|
||||
className={cn('inline-flex gap-4', className)}
|
||||
aria-label="Diff view mode"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<ToggleGroupItem
|
||||
value="unified"
|
||||
aria-label="Inline view"
|
||||
active={mode === 'unified'}
|
||||
>
|
||||
<FileText className="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{t('diff.viewModes.inline')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<div className={cn('inline-flex gap-4', className)}>
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={mode ?? ''}
|
||||
onValueChange={(v) => v && setMode(v as 'unified' | 'split')}
|
||||
className="inline-flex gap-4"
|
||||
aria-label="Diff view mode"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<ToggleGroupItem
|
||||
value="unified"
|
||||
aria-label="Inline view"
|
||||
active={mode === 'unified'}
|
||||
>
|
||||
<FileText className="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{t('diff.viewModes.inline')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<ToggleGroupItem
|
||||
value="split"
|
||||
aria-label="Split view"
|
||||
active={mode === 'split'}
|
||||
>
|
||||
<Columns className="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{t('diff.viewModes.split')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</ToggleGroup>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<ToggleGroupItem
|
||||
value="split"
|
||||
aria-label="Split view"
|
||||
active={mode === 'split'}
|
||||
>
|
||||
<Columns className="h-4 w-4" />
|
||||
</ToggleGroupItem>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{t('diff.viewModes.split')}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user