/ ink / measure-text.ts
measure-text.ts
 1  import { lineWidth } from './line-width-cache.js'
 2  
 3  type Output = {
 4    width: number
 5    height: number
 6  }
 7  
 8  // Single-pass measurement: computes both width and height in one
 9  // iteration instead of two (widestLine + countVisualLines).
10  // Uses indexOf to avoid array allocation from split('\n').
11  function measureText(text: string, maxWidth: number): Output {
12    if (text.length === 0) {
13      return {
14        width: 0,
15        height: 0,
16      }
17    }
18  
19    // Infinite or non-positive width means no wrapping — each line is one visual line.
20    // Must check before the loop since Math.ceil(w / Infinity) = 0.
21    const noWrap = maxWidth <= 0 || !Number.isFinite(maxWidth)
22  
23    let height = 0
24    let width = 0
25    let start = 0
26  
27    while (start <= text.length) {
28      const end = text.indexOf('\n', start)
29      const line = end === -1 ? text.substring(start) : text.substring(start, end)
30  
31      const w = lineWidth(line)
32      width = Math.max(width, w)
33  
34      if (noWrap) {
35        height++
36      } else {
37        height += w === 0 ? 1 : Math.ceil(w / maxWidth)
38      }
39  
40      if (end === -1) break
41      start = end + 1
42    }
43  
44    return { width, height }
45  }
46  
47  export default measureText