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
| @@ -3,6 +3,7 @@ | ||
| 3 | 3 | import Parse from '../parse' |
| 4 | 4 | import type Types from '../types' |
| 5 | 5 | import { isLastOfDiff } from './is-last-of-diff' |
| 6 | +import { withClosingLine } from './with-closing-line' | |
| 6 | 7 | |
| 7 | 8 | /** |
| 8 | 9 | * One file's hunks against the base its row was read against, so body and |
| @@ -10,7 +11,7 @@ | ||
| 10 | 11 | * |
| 11 | 12 | * An untracked, binary or renamed row (the built-in shows a rename's |
| 12 | 13 | * counts, never its body), or on an unborn HEAD a file edited after |
| 13 | - * staging, answers no hunks; the diff's last file, no closing empty line. | |
| 14 | + * staging, answers no hunks; each file but the last, a closing empty row. | |
| 14 | 15 | * |
| 15 | 16 | * @param run runs git against the pinned repository |
| 16 | 17 | * @param data the fetch the row belongs to |
| @@ -40,7 +41,11 @@ | ||
| 40 | 41 | file.path, |
| 41 | 42 | ]) |
| 42 | 43 | |
| 43 | - const printed = isLastOfDiff(data, file) ? stdout.replace(/\n$/, '') : stdout | |
| 44 | + if (exitCode !== 0) { | |
| 45 | + return null | |
| 46 | + } | |
| 44 | 47 | |
| 45 | - return exitCode === 0 ? Parse.parseFileDiff(printed) : null | |
| 48 | + const body = Parse.parseFileDiff(stdout) | |
| 49 | + | |
| 50 | + return isLastOfDiff(data, file) ? body : withClosingLine(body) | |
| 46 | 51 | } |
| @@ -1,4 +1,5 @@ | ||
| 1 | 1 | export * from './fetch-file-hunks.js' |
| 2 | 2 | export * from './is-last-of-diff' |
| 3 | +export * from './with-closing-line' | |
| 3 | 4 | |
| 4 | 5 | export * as default from '.' |
| @@ -5,7 +5,7 @@ | ||
| 5 | 5 | * fetch: the last tracked row, the untracked ones being listed apart. |
| 6 | 6 | * |
| 7 | 7 | * The built-in reads that diff in one piece and trims its end, so the last |
| 8 | - * file alone loses git's closing empty line and the context row it becomes. | |
| 8 | + * file alone has no closing empty line to draw (withClosingLine). | |
| 9 | 9 | * |
| 10 | 10 | * @param data the fetch the row belongs to |
| 11 | 11 | * @param file the row |
| @@ -1,0 +1,3 @@ | ||
| 1 | +export * from './with-closing-line.js' | |
| 2 | + | |
| 3 | +export * as default from '.' |
| @@ -1,0 +1,27 @@ | ||
| 1 | +import type Types from '../../types' | |
| 2 | + | |
| 3 | +/** | |
| 4 | + * A parsed body with one empty context row after its last hunk's lines, as | |
| 5 | + * the built-in panel draws the empty line that closes git's output. | |
| 6 | + * | |
| 7 | + * The built-in reads every file's diff in one piece: the newline before the | |
| 8 | + * next file's header parses as a context line of the file before it. A | |
| 9 | + * body with no hunk, or one already cut, gains nothing. | |
| 10 | + * | |
| 11 | + * @param body the parsed body | |
| 12 | + * @returns the body, its last hunk one row longer | |
| 13 | + */ | |
| 14 | +export function withClosingLine(body: Types.FileHunks): Types.FileHunks { | |
| 15 | + const last = body.hunks.at(-1) | |
| 16 | + const isClosed = last !== undefined && !body.isTruncated | |
| 17 | + | |
| 18 | + return isClosed | |
| 19 | + ? { | |
| 20 | + ...body, | |
| 21 | + hunks: [ | |
| 22 | + ...body.hunks.slice(0, -1), | |
| 23 | + { ...last, lines: [...last.lines, ' '] }, | |
| 24 | + ], | |
| 25 | + } | |
| 26 | + : body | |
| 27 | +} |
| @@ -1,8 +1,0 @@ | ||
| 1 | -/** | |
| 2 | - * A body line as a hunk keeps it: git's closing empty line becomes the | |
| 3 | - * empty context row the built-in panel draws for it; the rest stand. | |
| 4 | - * | |
| 5 | - * @param line a line isBodyLine passed | |
| 6 | - * @returns the line, a lone space for the empty one | |
| 7 | - */ | |
| 8 | -export const bodyLineOf = (line: string) => (line === '' ? ' ' : line) |
| @@ -1,3 +1,0 @@ | ||
| 1 | -export * from './body-line-of.js' | |
| 2 | - | |
| 3 | -export * as default from '.' |
| @@ -1,4 +1,3 @@ | ||
| 1 | -export * from './body-line-of' | |
| 2 | 1 | export * from './hunk-header' |
| 3 | 2 | export * from './is-body-line' |
| 4 | 3 | export * from './is-whole-answer' |
| @@ -2,14 +2,8 @@ | ||
| 2 | 2 | * Whether a line after a hunk header is a body line (added, removed or |
| 3 | 3 | * context) rather than a note such as `\ No newline at end of file`. |
| 4 | 4 | * |
| 5 | - * The empty line that ends git's output counts, as the built-in panel | |
| 6 | - * counts it: the last hunk of a file closes on one more context row. | |
| 7 | - * | |
| 8 | 5 | * @param line one line of `git diff` output |
| 9 | - * @returns whether it starts with `+`, `-` or a space, or is empty | |
| 6 | + * @returns whether it starts with `+`, `-` or a space | |
| 10 | 7 | */ |
| 11 | 8 | export const isBodyLine = (line: string) => |
| 12 | - line === '' || | |
| 13 | - line.startsWith('+') || | |
| 14 | - line.startsWith('-') || | |
| 15 | - line.startsWith(' ') | |
| 9 | + line.startsWith('+') || line.startsWith('-') || line.startsWith(' ') |
| @@ -3,7 +3,6 @@ | ||
| 3 | 3 | import { hunkOf } from '../hunk-of' |
| 4 | 4 | import { LARGE_FILE_HUNKS } from '../large-file-hunks' |
| 5 | 5 | import type Types from '../types' |
| 6 | -import { bodyLineOf } from './body-line-of' | |
| 7 | 6 | import { HUNK_HEADER } from './hunk-header' |
| 8 | 7 | import { isBodyLine } from './is-body-line' |
| 9 | 8 | |
| @@ -33,10 +32,7 @@ | ||
| 33 | 32 | headerRows.map((at, ordinal) => |
| 34 | 33 | hunkOf( |
| 35 | 34 | HUNK_HEADER.exec(rows[at] ?? '')?.groups, |
| 36 | - rows | |
| 37 | - .slice(at + 1, headerRows[ordinal + 1]) | |
| 38 | - .filter(isBodyLine) | |
| 39 | - .map(bodyLineOf), | |
| 35 | + rows.slice(at + 1, headerRows[ordinal + 1]).filter(isBodyLine), | |
| 40 | 36 | ), |
| 41 | 37 | ), |
| 42 | 38 | ), |
| @@ -15,8 +15,12 @@ | ||
| 15 | 15 | const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE)) |
| 16 | 16 | |
| 17 | 17 | expect(drawn).toContain('2 files changed +3 -1') |
| 18 | - expect(drawn).toContain('+const a = 2') | |
| 19 | - expect(drawn).toContain('+export const c = 2') | |
| 18 | + | |
| 19 | + expect(drawn, "a closing empty row, as the built-in's").toContain( | |
| 20 | + '+const a = 2\n ', | |
| 21 | + ) | |
| 22 | + | |
| 23 | + expect(drawn, 'none after the last file').toMatch(/\+export const c = 2$/) | |
| 20 | 24 | expect(drawn).not.toContain('❯') |
| 21 | 25 | }) |
| 22 | 26 |