A horizontal line of Drafter.Draw.Segment structs representing one terminal row.
Strips are the unit passed between widgets and the compositor. They track total
display column width and a hash-based cache_key to skip redundant re-renders.
Common operations include crop/2, pad/2, combine/2, divide/2, slice/3,
center/2, and ANSI serialisation via to_ansi/1.
iex> alias Drafter.Draw.{Segment, Strip}
iex> strip = Strip.new([Segment.plain("ab"), Segment.new("cd", %{bold: true})])
iex> Strip.width(strip)
4
iex> Strip.to_ansi(strip)
"ab\e[1mcd\e[0m"
Summary
Functions
A strip with segment after the existing segments.
Merge style into every segment's style, style winning on shared keys.
The strip centred in target_width columns between unstyled space padding.
A strip whose segments are the first strip's followed by the second's.
Cut the strip down to crop_width display columns.
Split the strip into {left, right} at display column position.
A strip with no segments and zero width.
Whether the strip has no segments, or every segment has empty text.
The strip at exactly target_width columns, cropped or space-padded on the right.
A strip of one unstyled segment holding text.
A strip holding segments in order.
Narrow a strip to width.
Append an unstyled run of spaces until the strip is target_width columns wide,
or return it unchanged if it already is.
A strip with segment before the existing segments.
The length columns of the strip beginning at display column start.
The row as one ANSI string.
The strip's segment text joined, with no style codes added.
The strip's width in display columns.
Types
@type t() :: %Drafter.Draw.Strip{ cache_key: term(), segments: [Drafter.Draw.Segment.t()], width: non_neg_integer() }
Functions
@spec append(t(), Drafter.Draw.Segment.t()) :: t()
A strip with segment after the existing segments.
Examples
iex> Drafter.Draw.Strip.from_text("ab")
...> |> Drafter.Draw.Strip.append(Drafter.Draw.Segment.plain("c"))
...> |> Drafter.Draw.Strip.to_plain_text()
"abc"
@spec apply_style(t(), Drafter.Draw.Segment.style()) :: t()
Merge style into every segment's style, style winning on shared keys.
Examples
iex> Drafter.Draw.Strip.from_text("ab")
...> |> Drafter.Draw.Strip.apply_style(%{bold: true})
...> |> Map.fetch!(:segments)
[%Drafter.Draw.Segment{text: "ab", style: %{bold: true}, width: 2}]
@spec center(t(), non_neg_integer()) :: t()
The strip centred in target_width columns between unstyled space padding.
An odd remainder puts the extra column on the right. A strip at least as wide as
target_width is cropped to it instead.
Examples
iex> Drafter.Draw.Strip.from_text("ab")
...> |> Drafter.Draw.Strip.center(7)
...> |> Drafter.Draw.Strip.to_plain_text()
" ab "
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.center(3)
...> |> Drafter.Draw.Strip.to_plain_text()
"hel"
A strip whose segments are the first strip's followed by the second's.
Segments are never merged, even when their styles are equal.
Examples
iex> left = Drafter.Draw.Strip.from_text("ab")
iex> right = Drafter.Draw.Strip.from_text("cd")
iex> combined = Drafter.Draw.Strip.combine(left, right)
iex> {Drafter.Draw.Strip.to_plain_text(combined), length(combined.segments)}
{"abcd", 2}
@spec crop(t(), non_neg_integer()) :: t()
Cut the strip down to crop_width display columns.
A strip already that narrow is returned unchanged, and a crop_width of zero or
less gives an empty strip. The segment straddling the boundary is cropped and
the segments past it are dropped.
Examples
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.crop(3)
...> |> Drafter.Draw.Strip.to_plain_text()
"hel"
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.crop(0)
...> |> Drafter.Draw.Strip.width()
0
@spec divide(t(), non_neg_integer()) :: {t(), t()}
Split the strip into {left, right} at display column position.
left is position columns wide. A position of zero or less puts everything
in right; a position at or past the strip's width puts everything in left.
A segment straddling the split is divided between the two, both halves keeping
its style.
Examples
iex> {left, right} = Drafter.Draw.Strip.divide(Drafter.Draw.Strip.from_text("hello"), 2)
iex> {Drafter.Draw.Strip.to_plain_text(left), Drafter.Draw.Strip.to_plain_text(right)}
{"he", "llo"}
iex> {left, right} = Drafter.Draw.Strip.divide(Drafter.Draw.Strip.from_text("hello"), 0)
iex> {Drafter.Draw.Strip.to_plain_text(left), Drafter.Draw.Strip.to_plain_text(right)}
{"", "hello"}
iex> {left, right} = Drafter.Draw.Strip.divide(Drafter.Draw.Strip.from_text("hello"), 9)
iex> {Drafter.Draw.Strip.to_plain_text(left), Drafter.Draw.Strip.to_plain_text(right)}
{"hello", ""}
@spec empty() :: t()
A strip with no segments and zero width.
Examples
iex> Drafter.Draw.Strip.empty()
%Drafter.Draw.Strip{segments: [], width: 0, cache_key: nil}
Whether the strip has no segments, or every segment has empty text.
Examples
iex> Drafter.Draw.Strip.empty() |> Drafter.Draw.Strip.empty?()
true
iex> Drafter.Draw.Strip.from_text("") |> Drafter.Draw.Strip.empty?()
true
iex> Drafter.Draw.Strip.from_text(" ") |> Drafter.Draw.Strip.empty?()
false
@spec fit_to_width(t(), non_neg_integer()) :: t()
The strip at exactly target_width columns, cropped or space-padded on the right.
Examples
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.fit_to_width(3)
...> |> Drafter.Draw.Strip.to_plain_text()
"hel"
iex> Drafter.Draw.Strip.from_text("hi")
...> |> Drafter.Draw.Strip.fit_to_width(4)
...> |> Drafter.Draw.Strip.to_plain_text()
"hi "
A strip of one unstyled segment holding text.
Examples
iex> Drafter.Draw.Strip.from_text("hi").segments
[%Drafter.Draw.Segment{text: "hi", style: %{}, width: 2}]
@spec new([Drafter.Draw.Segment.t()]) :: t()
A strip holding segments in order.
The strip's width is the sum of the segment widths, and its cache_key a hash
of the segments.
Examples
iex> strip = Drafter.Draw.Strip.new([Drafter.Draw.Segment.plain("日本")])
iex> strip.width
4
@spec overflow(t(), non_neg_integer(), :clip | :ellipsis) :: t()
Narrow a strip to width.
mode defaults to :clip, which cuts at the boundary. :ellipsis cuts at
width - 1 and appends … in the trailing segment's style. A strip already
within width is returned unchanged, and a width of zero or less clips
whatever the mode.
Examples
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.overflow(3)
...> |> Drafter.Draw.Strip.to_plain_text()
"hel"
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.overflow(3, :ellipsis)
...> |> Drafter.Draw.Strip.to_plain_text()
"he…"
iex> Drafter.Draw.Strip.from_text("hi")
...> |> Drafter.Draw.Strip.overflow(5, :ellipsis)
...> |> Drafter.Draw.Strip.to_plain_text()
"hi"
@spec pad(t(), non_neg_integer()) :: t()
Append an unstyled run of spaces until the strip is target_width columns wide,
or return it unchanged if it already is.
Examples
iex> Drafter.Draw.Strip.from_text("ab")
...> |> Drafter.Draw.Strip.pad(5)
...> |> Drafter.Draw.Strip.to_plain_text()
"ab "
@spec prepend(t(), Drafter.Draw.Segment.t()) :: t()
A strip with segment before the existing segments.
Examples
iex> Drafter.Draw.Strip.from_text("bc")
...> |> Drafter.Draw.Strip.prepend(Drafter.Draw.Segment.plain("a"))
...> |> Drafter.Draw.Strip.to_plain_text()
"abc"
@spec slice(t(), non_neg_integer(), non_neg_integer()) :: t()
The length columns of the strip beginning at display column start.
Examples
iex> Drafter.Draw.Strip.from_text("hello")
...> |> Drafter.Draw.Strip.slice(1, 3)
...> |> Drafter.Draw.Strip.to_plain_text()
"ell"
The row as one ANSI string.
Style codes carry across segments: a segment whose style equals the previous
one's emits only its text, and a segment that adds keys emits only the codes for
those keys. A full reset (\e[0m) is emitted before a segment that drops a key
the previous segment had set, and once more at the end of the row unless the last
segment is unstyled.
Examples
iex> alias Drafter.Draw.{Segment, Strip}
iex> Strip.new([Segment.plain("ab"), Segment.new("cd", %{bold: true})])
...> |> Strip.to_ansi()
"ab\e[1mcd\e[0m"
iex> alias Drafter.Draw.{Segment, Strip}
iex> Strip.new([
...> Segment.new("A", %{bold: true}),
...> Segment.new("B", %{bold: true, italic: true}),
...> Segment.plain("C")
...> ])
...> |> Strip.to_ansi()
"\e[1mA\e[3mB\e[0mC"
The strip's segment text joined, with no style codes added.
Any ANSI sequences already embedded in a segment's text are kept.
Examples
iex> alias Drafter.Draw.{Segment, Strip}
iex> Strip.new([Segment.plain("ab"), Segment.new("cd", %{bold: true})])
...> |> Strip.to_plain_text()
"abcd"
@spec width(t()) :: non_neg_integer()
The strip's width in display columns.
Examples
iex> Drafter.Draw.Strip.from_text("日本") |> Drafter.Draw.Strip.width()
4