File size: 2,017 Bytes
c9c900b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import Git from '../../../git'
import { hunkSourceOf } from '../hunk-source-of'
import { leadingHunkOf } from '../leading-hunk-of'
import { MAX_CODE_CHARS } from '../max-code-chars'
import { subHunksOf } from '../sub-hunks-of'
import type Types from '../types'
import { drawnLineOf } from './drawn-line-of'

/**
 * A file's hunks as the sources of `Code` blocks, one a hunk, stacked with
 * nothing between as the built-in stacks a file's hunks.
 *
 * A hunk longer than MAX_CODE_CHARS is split (subHunksOf) into several. The
 * body stops, truncated, at the whole lines the room holds as the host
 * counts, a block a node; the room left comes back.
 *
 * @param hunks the file's hunks
 * @param room the characters and nodes the pane's bodies have left
 * @returns the sources in order, whether any line was left out or cut, and
 *   the room after them
 */
export function codeBlocksOf(
  hunks: readonly Git.Hunk[],
  room: Types.BodyRoom,
): Types.CodeBody {
  const sources: string[] = []

  let chars = 0
  let isTruncated = false

  const left = (): Types.BodyRoom => ({
    chars: room.chars - chars,
    nodes: room.nodes - sources.length,
  })

  for (const hunk of hunks) {
    const lines = hunk.lines.filter(Git.isBodyLine).map(drawnLineOf)
    const split = subHunksOf({ ...hunk, lines }, MAX_CODE_CHARS)
    isTruncated ||= split.isTruncated

    for (const piece of split.hunks) {
      const fits = room.chars - chars
      const isWhole = hunkSourceOf(piece).length <= fits
      const fitted = isWhole ? piece : leadingHunkOf(piece, fits)
      const isOverNodes = sources.length + 1 > room.nodes

      if (isOverNodes || fitted.lines.length === 0) {
        return { sources, isTruncated: true, room: left() }
      }

      const text = hunkSourceOf(fitted)
      sources.push(text)
      chars += text.length

      if (fitted.lines.length < piece.lines.length) {
        return { sources, isTruncated: true, room: left() }
      }
    }
  }

  return { sources, isTruncated, room: left() }
}