surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
| 1 | import Limits from '../../../limits' |
| 2 | import { EMPTY_FILE_HUNKS } from '../../empty-file-hunks' |
| 3 | import type Types from '../../types' |
| 4 | import { FILE_HEADER_START } from '../file-header-start' |
| 5 | import { isWholeListing } from '../is-whole-listing' |
| 6 | import { parseFileDiff } from '../parse-file-diff.js' |
| 7 | import { QUOTED_HEADER_START } from '../quoted-header-start' |
| 8 | import { UNMERGED_STATUS } from '../unmerged-status' |
| 9 | |
| 10 | /** |
| 11 | * The bodies one `git diff --raw -z -p` answer settles, by path: each file's |
| 12 | * patch parsed as one file's, under the name git listed for it. |
| 13 | * |
| 14 | * The n-th patch is the n-th NUL-ended name's, unmerged names having none and |
| 15 | * neighbours under one opening line being one file; an unquoted opening line |
| 16 | * must equal its name's. Where the host cut, a file waits unless large. |
| 17 | * |
| 18 | * @param stdout the child's standard output |
| 19 | * @param asked the paths the child was asked for |
| 20 | * @returns the settled bodies; none where names and patches disagree |
| 21 | */ |
| 22 | export function parseFileDiffs( |
| 23 | stdout: string, |
| 24 | asked: readonly string[], |
| 25 | ): ReadonlyMap<string, Types.FileHunks> { |
| 26 | const fields = stdout.split('\0') |
| 27 | |
| 28 | const patchAt = fields.findIndex( |
| 29 | (field, at) => at % 2 === 0 && !field.startsWith(':'), |
| 30 | ) |
| 31 | |
| 32 | const listed = fields |
| 33 | .slice(0, patchAt) |
| 34 | .filter( |
| 35 | (_, at) => at % 2 === 1 && !fields[at - 1]?.endsWith(UNMERGED_STATUS), |
| 36 | ) |
| 37 | |
| 38 | const isParted = fields[patchAt] === '' |
| 39 | |
| 40 | const rows = fields |
| 41 | .slice(patchAt + (isParted ? 1 : 0)) |
| 42 | .join('\0') |
| 43 | .split('\n') |
| 44 | |
| 45 | const opened = rows.flatMap((row, at) => |
| 46 | row.startsWith(FILE_HEADER_START) ? [at] : [], |
| 47 | ) |
| 48 | |
| 49 | const starts = opened.filter( |
| 50 | (at, nth) => rows[at] !== rows[opened[nth - 1] ?? -1], |
| 51 | ) |
| 52 | |
| 53 | const isWhole = isWholeListing(stdout, '\n') |
| 54 | |
| 55 | const arrived = starts |
| 56 | .map((start, nth) => { |
| 57 | const end = starts[nth + 1] |
| 58 | const path = listed[nth] ?? '' |
| 59 | const isLast = end === undefined |
| 60 | |
| 61 | return { |
| 62 | path, |
| 63 | isNamed: |
| 64 | rows[start]?.startsWith(QUOTED_HEADER_START) === true || |
| 65 | rows[start] === `${FILE_HEADER_START}a/${path} b/${path}`, |
| 66 | isCut: isLast && !isWhole, |
| 67 | text: rows.slice(start, end).join('\n') + (isLast ? '' : '\n'), |
| 68 | } |
| 69 | }) |
| 70 | .filter(patch => !patch.isCut || patch.text.length > Limits.MAX_DIFF_BYTES) |
| 71 | |
| 72 | const isPaired = |
| 73 | patchAt !== -1 && |
| 74 | starts.length <= listed.length && |
| 75 | (starts.length === listed.length || starts.length === 0 || !isWhole) && |
| 76 | arrived.every(patch => patch.isNamed) |
| 77 | |
| 78 | if (!isPaired) { |
| 79 | return new Map() |
| 80 | } |
| 81 | |
| 82 | const read = new Map( |
| 83 | arrived.map(patch => [patch.path, parseFileDiff(patch.text)]), |
| 84 | ) |
| 85 | |
| 86 | const waiting = isWhole ? [] : listed.slice(arrived.length) |
| 87 | |
| 88 | return new Map( |
| 89 | asked |
| 90 | .filter(path => !waiting.includes(path)) |
| 91 | .map(path => [path, read.get(path) ?? EMPTY_FILE_HUNKS]), |
| 92 | ) |
| 93 | } |