Drafter.Draw.Segment (drafter v0.3.1)

Copy Markdown View Source

The fundamental rendering unit: a string of text with a single style applied.

A %Segment{} stores the text content, a style map, and the pre-computed display column width (accounting for double-width CJK and emoji codepoints). Style keys: :fg and :bg (RGB 3-tuples or color strings normalised to RGB), :bold, :dim, :italic, :underline, :reverse (booleans). Multiple segments are assembled into a Drafter.Draw.Strip to form a single terminal line.

iex> Drafter.Draw.Segment.new("hi", %{bold: true})
%Drafter.Draw.Segment{text: "hi", style: %{bold: true}, width: 2}

Summary

Functions

The style key holding the alpha channel for :fg or :bg.

Merge style into the segment's own style, style winning on shared keys.

Cut the segment down to crop_width display columns.

Whether the segment's text is the empty string.

A segment of text carrying style.

Append spaces until the segment is target_width columns wide, or return it unchanged if it already is.

A segment of text with an empty style.

The SGR sequence that turns on style.

The style keys that are attribute flags rather than colours.

The segment as SGR codes, its text, and a reset.

The segment's width in display columns, measured when it was built.

Types

style()

@type style() :: %{
  optional(:fg) => {0..255, 0..255, 0..255},
  optional(:bg) => {0..255, 0..255, 0..255},
  optional(:fg_alpha) => float(),
  optional(:bg_alpha) => float(),
  optional(:bold) => boolean(),
  optional(:dim) => boolean(),
  optional(:italic) => boolean(),
  optional(:underline) => boolean(),
  optional(:reverse) => boolean()
}

t()

@type t() :: %Drafter.Draw.Segment{
  style: style(),
  text: String.t(),
  width: non_neg_integer()
}

Functions

alpha_key(atom)

@spec alpha_key(:fg | :bg) :: :fg_alpha | :bg_alpha

The style key holding the alpha channel for :fg or :bg.

A translucent colour is stored as two style keys: the RGB triple under :fg or :bg, and its alpha, a float in 0.0..1.0, under the key this returns. A style with no such key is fully opaque.

Only the compositor reads these keys. It blends the colour against the cell beneath and removes the alpha key, so a style reaching the ANSI encoders never carries one.

Examples

iex> Drafter.Draw.Segment.alpha_key(:fg)
:fg_alpha

iex> Drafter.Draw.Segment.alpha_key(:bg)
:bg_alpha

apply_style(segment, style)

@spec apply_style(t(), style()) :: t()

Merge style into the segment's own style, style winning on shared keys.

Colour values are not normalised; pass RGB triples.

Examples

iex> Drafter.Draw.Segment.plain("x") |> Drafter.Draw.Segment.apply_style(%{bold: true})
%Drafter.Draw.Segment{text: "x", style: %{bold: true}, width: 1}

iex> segment = Drafter.Draw.Segment.new("x", %{bold: true, italic: true})
iex> Drafter.Draw.Segment.apply_style(segment, %{bold: false}).style
%{bold: false, italic: true}

crop(segment, crop_width)

@spec crop(t(), non_neg_integer()) :: t()

Cut the segment down to crop_width display columns.

A segment already that narrow is returned unchanged, and a crop_width of zero or less gives an empty segment. A double-width grapheme that would straddle the boundary is dropped whole, so the result can be one column narrower than asked. ANSI SGR sequences in the text are preserved and cost no columns.

Examples

iex> Drafter.Draw.Segment.plain("hello") |> Drafter.Draw.Segment.crop(3)
%Drafter.Draw.Segment{text: "hel", style: %{}, width: 3}

iex> Drafter.Draw.Segment.plain("hello") |> Drafter.Draw.Segment.crop(9)
%Drafter.Draw.Segment{text: "hello", style: %{}, width: 5}

iex> Drafter.Draw.Segment.plain("hello") |> Drafter.Draw.Segment.crop(0)
%Drafter.Draw.Segment{text: "", style: %{}, width: 0}

iex> Drafter.Draw.Segment.plain("日本語") |> Drafter.Draw.Segment.crop(3)
%Drafter.Draw.Segment{text: "日", style: %{}, width: 2}

empty?(segment)

@spec empty?(t()) :: boolean()

Whether the segment's text is the empty string.

Examples

iex> Drafter.Draw.Segment.plain("") |> Drafter.Draw.Segment.empty?()
true

iex> Drafter.Draw.Segment.plain(" ") |> Drafter.Draw.Segment.empty?()
false

new(text, style \\ %{})

@spec new(String.t(), style()) :: t()

A segment of text carrying style.

The display width is measured on construction, with ANSI SGR sequences in text excluded from the count. Colour values in style are normalised to RGB triples, a translucent colour additionally storing its alpha under alpha_key/1.

style defaults to %{}.

Examples

iex> Drafter.Draw.Segment.new("abc")
%Drafter.Draw.Segment{text: "abc", style: %{}, width: 3}

iex> Drafter.Draw.Segment.new("日本")
%Drafter.Draw.Segment{text: "日本", style: %{}, width: 4}

iex> Drafter.Draw.Segment.new("x", %{fg: "#ff0000"})
%Drafter.Draw.Segment{text: "x", style: %{fg: {255, 0, 0}}, width: 1}

iex> Drafter.Draw.Segment.new("x", %{fg: "rgba(255, 0, 0, 0.5)"})
%Drafter.Draw.Segment{text: "x", style: %{fg: {255, 0, 0}, fg_alpha: 0.5}, width: 1}

pad(segment, target_width)

@spec pad(t(), non_neg_integer()) :: t()

Append spaces until the segment is target_width columns wide, or return it unchanged if it already is.

Examples

iex> Drafter.Draw.Segment.plain("ab") |> Drafter.Draw.Segment.pad(5)
%Drafter.Draw.Segment{text: "ab   ", style: %{}, width: 5}

iex> Drafter.Draw.Segment.plain("abcde") |> Drafter.Draw.Segment.pad(2)
%Drafter.Draw.Segment{text: "abcde", style: %{}, width: 5}

plain(text)

@spec plain(String.t()) :: t()

A segment of text with an empty style.

Examples

iex> Drafter.Draw.Segment.plain("héllo")
%Drafter.Draw.Segment{text: "héllo", style: %{}, width: 5}

style_codes(style)

@spec style_codes(style()) :: String.t()

The SGR sequence that turns on style.

Returns the empty string for an empty style, or one that sets nothing. Colours are emitted as 24-bit 38;2 and 48;2 codes. Alpha keys emit nothing.

Examples

iex> Drafter.Draw.Segment.style_codes(%{})
""

iex> Drafter.Draw.Segment.style_codes(%{bold: true})
"\e[1m"

iex> Drafter.Draw.Segment.style_codes(%{fg: {255, 0, 0}})
"\e[38;2;255;0;0m"

iex> Drafter.Draw.Segment.style_codes(%{bold: false})
""

style_flags()

@spec style_flags() :: [atom()]

The style keys that are attribute flags rather than colours.

Examples

iex> Drafter.Draw.Segment.style_flags()
[:bold, :dim, :italic, :underline, :reverse]

to_ansi(segment)

@spec to_ansi(t()) :: String.t()

The segment as SGR codes, its text, and a reset.

A segment with an empty style returns its text alone, with no codes and no reset.

Examples

iex> Drafter.Draw.Segment.new("x", %{bold: true}) |> Drafter.Draw.Segment.to_ansi()
"\e[1mx\e[0m"

iex> Drafter.Draw.Segment.plain("x") |> Drafter.Draw.Segment.to_ansi()
"x"

width(segment)

@spec width(t()) :: non_neg_integer()

The segment's width in display columns, measured when it was built.

Examples

iex> Drafter.Draw.Segment.plain("日本") |> Drafter.Draw.Segment.width()
4