mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 22:05:50 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -1,22 +1,22 @@
|
||||
import type { StructuredPatchHunk } from 'diff'
|
||||
import * as React from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useSettings } from '../hooks/useSettings.js'
|
||||
import { Box, NoSelect, RawAnsi, useTheme } from '@anthropic/ink'
|
||||
import { isFullscreenEnvEnabled } from '../utils/fullscreen.js'
|
||||
import sliceAnsi from '../utils/sliceAnsi.js'
|
||||
import { expectColorDiff } from './StructuredDiff/colorDiff.js'
|
||||
import { StructuredDiffFallback } from './StructuredDiff/Fallback.js'
|
||||
import type { StructuredPatchHunk } from 'diff';
|
||||
import * as React from 'react';
|
||||
import { memo } from 'react';
|
||||
import { useSettings } from '../hooks/useSettings.js';
|
||||
import { Box, NoSelect, RawAnsi, useTheme } from '@anthropic/ink';
|
||||
import { isFullscreenEnvEnabled } from '../utils/fullscreen.js';
|
||||
import sliceAnsi from '../utils/sliceAnsi.js';
|
||||
import { expectColorDiff } from './StructuredDiff/colorDiff.js';
|
||||
import { StructuredDiffFallback } from './StructuredDiff/Fallback.js';
|
||||
|
||||
type Props = {
|
||||
patch: StructuredPatchHunk
|
||||
dim: boolean
|
||||
filePath: string // File path for language detection
|
||||
firstLine: string | null // First line of file for shebang detection
|
||||
fileContent?: string // Full file content for syntax context (multiline strings, etc.)
|
||||
width: number
|
||||
skipHighlighting?: boolean // Skip syntax highlighting
|
||||
}
|
||||
patch: StructuredPatchHunk;
|
||||
dim: boolean;
|
||||
filePath: string; // File path for language detection
|
||||
firstLine: string | null; // First line of file for shebang detection
|
||||
fileContent?: string; // Full file content for syntax context (multiline strings, etc.)
|
||||
width: number;
|
||||
skipHighlighting?: boolean; // Skip syntax highlighting
|
||||
};
|
||||
|
||||
// REPL.tsx renders <Messages> at two disjoint tree positions (transcript
|
||||
// early-return vs prompt-mode nested in FullscreenLayout), so ctrl+o
|
||||
@@ -30,29 +30,22 @@ type Props = {
|
||||
// reactivating the per-line <DiffLine> branch that PR #20378 had bypassed.
|
||||
// Caching the split here restores the O(1)-leaves-per-diff invariant.
|
||||
type CachedRender = {
|
||||
lines: string[]
|
||||
lines: string[];
|
||||
// Two RawAnsi columns replace what was N DiffLine rows. sliceAnsi work
|
||||
// moves from per-remount to cold-cache-only; parseToSpans is eliminated
|
||||
// entirely (RawAnsi bypasses Ansi parsing).
|
||||
gutterWidth: number
|
||||
gutters: string[] | null
|
||||
contents: string[] | null
|
||||
}
|
||||
const RENDER_CACHE = new WeakMap<
|
||||
StructuredPatchHunk,
|
||||
Map<string, CachedRender>
|
||||
>()
|
||||
gutterWidth: number;
|
||||
gutters: string[] | null;
|
||||
contents: string[] | null;
|
||||
};
|
||||
const RENDER_CACHE = new WeakMap<StructuredPatchHunk, Map<string, CachedRender>>();
|
||||
|
||||
// Gutter width matches the Rust module's layout: marker (1) + space +
|
||||
// right-aligned line number (max_digits) + space. Depends only on patch
|
||||
// identity (the WeakMap key), so it's cacheable alongside the NAPI output.
|
||||
function computeGutterWidth(patch: StructuredPatchHunk): number {
|
||||
const maxLineNumber = Math.max(
|
||||
patch.oldStart + patch.oldLines - 1,
|
||||
patch.newStart + patch.newLines - 1,
|
||||
1,
|
||||
)
|
||||
return maxLineNumber.toString().length + 3 // marker + 2 padding spaces
|
||||
const maxLineNumber = Math.max(patch.oldStart + patch.oldLines - 1, patch.newStart + patch.newLines - 1, 1);
|
||||
return maxLineNumber.toString().length + 3; // marker + 2 padding spaces
|
||||
}
|
||||
|
||||
function renderColorDiff(
|
||||
@@ -65,54 +58,49 @@ function renderColorDiff(
|
||||
dim: boolean,
|
||||
splitGutter: boolean,
|
||||
): CachedRender | null {
|
||||
const ColorDiff = expectColorDiff()
|
||||
if (!ColorDiff) return null
|
||||
const ColorDiff = expectColorDiff();
|
||||
if (!ColorDiff) return null;
|
||||
|
||||
// Defensive: if the gutter would eat the whole render width (narrow
|
||||
// terminal), skip the split. Rust already wraps to `width` so the
|
||||
// single-column output stays correct; we just lose noSelect. Without
|
||||
// this, sliceAnsi(line, gutterWidth) would return empty content and
|
||||
// RawAnsi(width<=0) is untested.
|
||||
const rawGutterWidth = splitGutter ? computeGutterWidth(patch) : 0
|
||||
const gutterWidth =
|
||||
rawGutterWidth > 0 && rawGutterWidth < width ? rawGutterWidth : 0
|
||||
const rawGutterWidth = splitGutter ? computeGutterWidth(patch) : 0;
|
||||
const gutterWidth = rawGutterWidth > 0 && rawGutterWidth < width ? rawGutterWidth : 0;
|
||||
|
||||
const key = `${theme}|${width}|${dim ? 1 : 0}|${gutterWidth}|${firstLine ?? ''}|${filePath}`
|
||||
const key = `${theme}|${width}|${dim ? 1 : 0}|${gutterWidth}|${firstLine ?? ''}|${filePath}`;
|
||||
|
||||
let perHunk = RENDER_CACHE.get(patch)
|
||||
const hit = perHunk?.get(key)
|
||||
if (hit) return hit
|
||||
let perHunk = RENDER_CACHE.get(patch);
|
||||
const hit = perHunk?.get(key);
|
||||
if (hit) return hit;
|
||||
|
||||
const lines = new ColorDiff(patch, firstLine, filePath, fileContent).render(
|
||||
theme,
|
||||
width,
|
||||
dim,
|
||||
)
|
||||
if (lines === null) return null
|
||||
const lines = new ColorDiff(patch, firstLine, filePath, fileContent).render(theme, width, dim);
|
||||
if (lines === null) return null;
|
||||
|
||||
// Pre-split the gutter column once (cold-cache). sliceAnsi preserves
|
||||
// styles across the cut; the Rust module already pads the gutter to
|
||||
// gutterWidth so the narrow RawAnsi column's width matches its cells.
|
||||
let gutters: string[] | null = null
|
||||
let contents: string[] | null = null
|
||||
let gutters: string[] | null = null;
|
||||
let contents: string[] | null = null;
|
||||
if (gutterWidth > 0) {
|
||||
gutters = lines.map(l => sliceAnsi(l, 0, gutterWidth))
|
||||
contents = lines.map(l => sliceAnsi(l, gutterWidth))
|
||||
gutters = lines.map(l => sliceAnsi(l, 0, gutterWidth));
|
||||
contents = lines.map(l => sliceAnsi(l, gutterWidth));
|
||||
}
|
||||
|
||||
const entry: CachedRender = { lines, gutterWidth, gutters, contents }
|
||||
const entry: CachedRender = { lines, gutterWidth, gutters, contents };
|
||||
|
||||
if (!perHunk) {
|
||||
perHunk = new Map()
|
||||
RENDER_CACHE.set(patch, perHunk)
|
||||
perHunk = new Map();
|
||||
RENDER_CACHE.set(patch, perHunk);
|
||||
}
|
||||
// Cap the inner map: width is part of the key, so terminal resize while a
|
||||
// diff is visible accumulates a full render copy per distinct width. Four
|
||||
// variants (two widths × dim on/off) covers the steady state; beyond that
|
||||
// the user is actively resizing and old widths are stale.
|
||||
if (perHunk.size >= 4) perHunk.clear()
|
||||
perHunk.set(key, entry)
|
||||
return entry
|
||||
if (perHunk.size >= 4) perHunk.clear();
|
||||
perHunk.set(key, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
export const StructuredDiff = memo(function StructuredDiff({
|
||||
@@ -124,44 +112,34 @@ export const StructuredDiff = memo(function StructuredDiff({
|
||||
width,
|
||||
skipHighlighting = false,
|
||||
}: Props): React.ReactNode {
|
||||
const [theme] = useTheme()
|
||||
const settings = useSettings()
|
||||
const syntaxHighlightingDisabled =
|
||||
settings.syntaxHighlightingDisabled ?? false
|
||||
const [theme] = useTheme();
|
||||
const settings = useSettings();
|
||||
const syntaxHighlightingDisabled = settings.syntaxHighlightingDisabled ?? false;
|
||||
|
||||
// Ensure width is at least 1 to prevent crashes in the Rust NAPI module
|
||||
// which expects u32 (can't handle negative numbers)
|
||||
const safeWidth = Math.max(1, Math.floor(width))
|
||||
const safeWidth = Math.max(1, Math.floor(width));
|
||||
|
||||
// Only split out a noSelect gutter in fullscreen mode — terminal native
|
||||
// selection is used otherwise and noSelect is meaningless. Both branches
|
||||
// are now O(1) Yoga leaves per diff on remount (2 vs 1), so this gate
|
||||
// only saves cold-cache sliceAnsi work when fullscreen is off.
|
||||
const splitGutter = isFullscreenEnvEnabled()
|
||||
const splitGutter = isFullscreenEnvEnabled();
|
||||
|
||||
const cached =
|
||||
skipHighlighting || syntaxHighlightingDisabled
|
||||
? null
|
||||
: renderColorDiff(
|
||||
patch,
|
||||
firstLine,
|
||||
filePath,
|
||||
fileContent ?? null,
|
||||
theme,
|
||||
safeWidth,
|
||||
dim,
|
||||
splitGutter,
|
||||
)
|
||||
: renderColorDiff(patch, firstLine, filePath, fileContent ?? null, theme, safeWidth, dim, splitGutter);
|
||||
|
||||
if (!cached) {
|
||||
return (
|
||||
<Box>
|
||||
<StructuredDiffFallback patch={patch} dim={dim} width={width} />
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
const { lines, gutterWidth, gutters, contents } = cached
|
||||
const { lines, gutterWidth, gutters, contents } = cached;
|
||||
|
||||
// Two-column layout: gutter (noSelect) + content. NoSelect marks the
|
||||
// Box's computed bounds non-selectable; RawAnsi's measure func sets
|
||||
@@ -175,12 +153,12 @@ export const StructuredDiff = memo(function StructuredDiff({
|
||||
</NoSelect>
|
||||
<RawAnsi lines={contents} width={safeWidth - gutterWidth} />
|
||||
</Box>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<RawAnsi lines={lines} width={safeWidth} />
|
||||
</Box>
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user