spacehub

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

claude-code / mods / diff / hooks / views / body / plan / segments / segments-of.ts
137 lines · 3.9 KBRaw
1import { countOf } from '../../../../count-of'
2import Git from '../../../../git'
3import { keptOf } from '../../../../kept-of'
4import Limits from '../../../../limits'
5import type PaneState from '../../../../pane-state'
6import Detail from '../../../detail'
7import Entries from '../../../entries'
8import Layout from '../../../layout'
9import 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 */
23export 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}