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 { countOf } from '../../../../count-of' |
| 2 | import Git from '../../../../git' |
| 3 | import { keptOf } from '../../../../kept-of' |
| 4 | import Limits from '../../../../limits' |
| 5 | import type PaneState from '../../../../pane-state' |
| 6 | import Detail from '../../../detail' |
| 7 | import Entries from '../../../entries' |
| 8 | import Layout from '../../../layout' |
| 9 | import type { DockPlan, Segment } from '../../types' |
| 10 | |
| 11 | /** |
| 12 | * The docked pane's scrolling body as segments (ReplDiffSidebar's scroll |
| 13 | * box): each listed file's block, a blank between, the pre-session section. |
| 14 | * |
| 15 | * Under an empty state whose pre-session section is open, the empty lines |
| 16 | * head the body instead. Hunks are made printable and split under a leaf's |
| 17 | * cap (subHunksOf), each carrying the rows its lines take wrapped. |
| 18 | * |
| 19 | * @param model the pane's state: which file is armed, which section is open |
| 20 | * @param plan the docked pane's decisions over that state |
| 21 | * @returns the segments, top to bottom |
| 22 | */ |
| 23 | export function segmentsOf( |
| 24 | model: PaneState.PaneModel, |
| 25 | plan: DockPlan, |
| 26 | ): Segment[] { |
| 27 | const { earlier } = plan |
| 28 | const { columns } = model.place |
| 29 | const isEmptied = plan.message.length > 0 |
| 30 | const isOpen = model.isPreSessionShown && earlier.length > 0 |
| 31 | const isCapped = earlier.length > Limits.PRE_SESSION_BODY_CAP |
| 32 | |
| 33 | const gutterOf = (hunk: Git.Hunk) => |
| 34 | String( |
| 35 | Math.max( |
| 36 | 1, |
| 37 | hunk.oldStart + countOf(hunk.lines, line => !line.startsWith('+')) - 1, |
| 38 | hunk.newStart + countOf(hunk.lines, line => !line.startsWith('-')) - 1, |
| 39 | ), |
| 40 | ).length + Limits.GUTTER_CHROME |
| 41 | |
| 42 | function hunkOf(entry: Entries.BodyEntry, hunk: Git.Hunk): Segment { |
| 43 | const room = Math.max(1, columns - gutterOf(hunk)) |
| 44 | |
| 45 | const lineRows = hunk.lines.map(line => |
| 46 | Math.max( |
| 47 | 1, |
| 48 | Math.ceil( |
| 49 | Layout.cellWidth( |
| 50 | Layout.sanitizeLine( |
| 51 | line.slice(1).replaceAll('\t', ' '.repeat(Layout.TAB_WIDTH)), |
| 52 | ), |
| 53 | ) / room, |
| 54 | ), |
| 55 | ), |
| 56 | ) |
| 57 | |
| 58 | return { |
| 59 | kind: 'hunk', |
| 60 | path: entry.path, |
| 61 | hunk, |
| 62 | lineRows, |
| 63 | rows: lineRows.reduce((sum, rows) => sum + rows, 0), |
| 64 | } |
| 65 | } |
| 66 | |
| 67 | const splitOf = (hunk: Git.Hunk) => |
| 68 | Detail.subHunksOf( |
| 69 | { |
| 70 | ...hunk, |
| 71 | lines: hunk.lines.filter(Git.isBodyLine).map(Detail.drawnLineOf), |
| 72 | }, |
| 73 | Detail.MAX_CODE_CHARS, |
| 74 | ) |
| 75 | |
| 76 | function fileOf(entry: Entries.BodyEntry): Segment[] { |
| 77 | const detail = Entries.detailModelOf(entry, model) |
| 78 | const notes = Detail.placeholderOf(detail) |
| 79 | const splits = (entry.body?.hunks ?? []).map(splitOf) |
| 80 | |
| 81 | const isTruncated = |
| 82 | entry.body?.isTruncated === true || |
| 83 | splits.some(split => split.isTruncated) |
| 84 | |
| 85 | const body: Segment[] = notes |
| 86 | ? notes.map(text => ({ kind: 'note', text })) |
| 87 | : splits.flatMap(split => split.hunks).map(hunk => hunkOf(entry, hunk)) |
| 88 | |
| 89 | return [ |
| 90 | { kind: 'rule', path: entry.path }, |
| 91 | { |
| 92 | kind: 'name', |
| 93 | name: { ...detail, isTruncated, isAskable: notes === null }, |
| 94 | }, |
| 95 | { kind: 'rule', path: null }, |
| 96 | ...body, |
| 97 | ...(isTruncated ? [{ kind: 'footer' } as const] : []), |
| 98 | ] |
| 99 | } |
| 100 | |
| 101 | const spacedFileOf = (entry: Entries.BodyEntry): Segment[] => [ |
| 102 | ...fileOf(entry), |
| 103 | { kind: 'blank' }, |
| 104 | ] |
| 105 | |
| 106 | const messageOf = (): Segment[] => [ |
| 107 | { kind: 'blank' }, |
| 108 | ...plan.message.map(text => ({ |
| 109 | kind: 'text' as const, |
| 110 | text: Layout.sanitizeName(text), |
| 111 | })), |
| 112 | { kind: 'blank' }, |
| 113 | ] |
| 114 | |
| 115 | const hiddenNote: Segment = { |
| 116 | kind: 'text', |
| 117 | text: `diffs hidden above ${Limits.PRE_SESSION_BODY_CAP} files`, |
| 118 | } |
| 119 | |
| 120 | const bodiesOf = (): Segment[] => |
| 121 | isCapped ? [hiddenNote] : earlier.flatMap(fileOf) |
| 122 | |
| 123 | const openedOf = (): Segment[] => [ |
| 124 | { kind: 'blank' }, |
| 125 | ...earlier.map(entry => ({ kind: 'legend' as const, entry })), |
| 126 | ...bodiesOf(), |
| 127 | ] |
| 128 | |
| 129 | return [ |
| 130 | ...(isEmptied ? messageOf() : plan.rows.flatMap(spacedFileOf)), |
| 131 | ...keptOf([plan.earlierLabel]).map(label => ({ |
| 132 | kind: 'toggle' as const, |
| 133 | label, |
| 134 | })), |
| 135 | ...(isOpen ? openedOf() : []), |
| 136 | ] |
| 137 | } |