# `Harlock.Width`
[🔗](https://github.com/thatsme/harlock/blob/v0.2.0/lib/harlock/width.ex#L1)

Display-column width of strings and graphemes for terminal rendering.

`String.length/1` counts graphemes, not columns. For ASCII the two are
the same; for CJK, emoji, and combining marks they aren't. Use this
module wherever a width is meant for cursor positioning, padding, or
truncation against the terminal grid.

Width rules (Unicode 15.1 East Asian Width + emoji presentation):

  * Control characters → 0
  * Combining marks, variation selectors, ZWJ, zero-width spaces → 0
  * East Asian Wide / Fullwidth → 2
  * Regional indicator pairs (flag emoji) → 2
  * Other emoji → 2 (we use coarse 0x1F000–0x1FAFF ranges; over-claim
    is safer than under-claim — alignment off by one column beats text
    bleeding into the next cell)
  * Everything else → 1

The grapheme width is the maximum width of any codepoint in the cluster:
combining marks attach at width 0 to a base of 1 or 2, so the cluster
width equals the base width. ZWJ sequences (e.g. 👨‍👩‍👧) take the
width of any wide codepoint in the cluster (terminals render them as one
glyph, which is 2 cells; we accept that as the cluster width).

# `cells`

```elixir
@type cells() :: 0 | 1 | 2
```

Width of a grapheme in terminal cells.

# `pad_leading`

```elixir
@spec pad_leading(String.t(), non_neg_integer(), String.t()) :: String.t()
```

Pad a string on the left with `pad` so its display width equals
`target_cols`. Returns the original string unchanged if it's already
wider than the target.

# `pad_trailing`

```elixir
@spec pad_trailing(String.t(), non_neg_integer(), String.t()) :: String.t()
```

Pad a string on the right with `pad` so its display width equals
`target_cols`. Returns the original string unchanged if it's already
wider than the target.

# `slice`

```elixir
@spec slice(String.t(), non_neg_integer()) :: String.t()
```

Truncate a string to at most `max_cols` display columns. A wide grapheme
that wouldn't fit entirely in the remaining budget is dropped, not split.

# `string_width`

```elixir
@spec string_width(String.t()) :: non_neg_integer()
```

Total display width of a string — the sum of its grapheme widths.

# `width`

```elixir
@spec width(String.t()) :: cells()
```

Display width of a single grapheme. Returns 0 for empty input.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
