# `Owl.Data`
[🔗](https://github.com/fuelen/owl/blob/v0.13.1/lib/owl/data.ex#L1)

A set of functions for `t:IO.chardata/0` with [tags](`Owl.Tag`).

# `sequence`

```elixir
@type sequence() ::
  :black
  | :red
  | :green
  | :yellow
  | :blue
  | :magenta
  | :cyan
  | :white
  | :black_background
  | :red_background
  | :green_background
  | :yellow_background
  | :blue_background
  | :magenta_background
  | :cyan_background
  | :white_background
  | :light_black_background
  | :light_red_background
  | :light_green_background
  | :light_yellow_background
  | :light_blue_background
  | :light_magenta_background
  | :light_cyan_background
  | :light_white_background
  | :default_color
  | :default_background
  | :blink_slow
  | :blink_rapid
  | :faint
  | :bright
  | :inverse
  | :underline
  | :italic
  | :overlined
  | :reverse
  | {:hyperlink, String.t()}
  | binary()
```

An atom alias of ANSI escape sequence, a binary representation, or a hyperlink tuple.

Examples:

  * An atom alias for color/effect like `:red`, `:underline`, `:bright`
  * A binary representation of a sequence like `"\e[38;5;33m"`
    (which is `IO.ANSI.color(33)` or `IO.ANSI.color(0, 2, 5)`)
  * A hyperlink tuple `{:hyperlink, "https://elixir-lang.org"}`

The `{:hyperlink, url}` form will be converted into an
[OSC 8 hyperlink sequence](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda).

# `t`

```elixir
@type t() ::
  [binary() | non_neg_integer() | t() | Owl.Tag.t(t())]
  | Owl.Tag.t(t())
  | binary()
```

A recursive data type similar to `t:IO.chardata/0`, with additional support for `t:Owl.Tag.t/1`.

Can be printed using `Owl.IO.puts/2`.

# `add_prefix`

```elixir
@spec add_prefix(t(), t()) :: t()
```

Adds a `prefix` before each line of the `data`.

An important feature is that the styling of the data is preserved for each line.

## Example

    iex> "first\nsecond" |> Owl.Data.tag(:red) |> Owl.Data.add_prefix(Owl.Data.tag("test: ", :yellow))
    [
      [Owl.Data.tag("test: ", :yellow), Owl.Data.tag(["first"], :red)],
      "\n",
      [Owl.Data.tag("test: ", :yellow), Owl.Data.tag(["second"], :red)]
    ]

# `chunk_every`

```elixir
@spec chunk_every(data :: t(), count :: pos_integer()) :: [t()]
```

Returns list of `t()` containing `count` elements each.

## Example

    iex> Owl.Data.chunk_every(
    ...>   ["first second ", Owl.Data.tag(["third", Owl.Data.tag(" fourth", :blue)], :red)],
    ...>   7
    ...> )
    [
      "first s",
      ["econd ", Owl.Data.tag(["t"], :red)],
      Owl.Data.tag(["hird", Owl.Data.tag([" fo"], :blue)], :red),
      Owl.Data.tag(["urth"], :blue)
    ]

# `from_chardata`

```elixir
@spec from_chardata(IO.chardata()) :: t()
```

Transforms chardata, replacing raw escape sequences with tags (see `tag/2`).

This allows data formatted outside Owl to be used with other Owl modules, such as `Owl.Box`.

## Examples

    iex> [:red, "hello"] |> IO.ANSI.format() |> Owl.Data.from_chardata()
    Owl.Data.tag("hello", :red)

    іex> {output, 0} = Owl.System.cmd("bat", ["README.md", "--color=always", "--style=plain"])
    ...> output
    ...> |> Owl.Data.from_chardata()
    ...> |> Owl.Box.new(title: Owl.Data.tag("README.md", :cyan), border_tag: :light_cyan)
    ...> |> Owl.IO.puts()

# `length`

```elixir
@spec length(t()) :: non_neg_integer()
```

Returns the length of the data.

## Examples

    iex> Owl.Data.length(["222"])
    3

    iex> Owl.Data.length([222])
    1

    iex> Owl.Data.length([[[]]])
    0

    iex> Owl.Data.length(["222", Owl.Data.tag(["333", "444"], :green)])
    9

    iex> Owl.Data.length(Owl.Data.tag("Example", hyperlink: "https://example.com"))
    7

    # if ucwidth dependency is present, then it is used to calculate the length of the string
    iex> Owl.Data.length("😂")
    2

# `lines`

```elixir
@spec lines(t()) :: [t()]
```

Splits data by newline characters.

A special case of `split/2`.

## Example

    iex> Owl.Data.lines(["first\nsecond\n", Owl.Data.tag("third\nfourth", :red)])
    ["first", "second", Owl.Data.tag(["third"], :red), Owl.Data.tag(["fourth"], :red)]

    iex> "Hello\nworld" |> Owl.Data.tag({:hyperlink, "https://example.com"}) |> Owl.Data.lines |> Enum.intersperse(" ")
    [
      Owl.Data.tag(["Hello"], {:hyperlink, "https://example.com"}),
      " ",
      Owl.Data.tag(["world"], {:hyperlink, "https://example.com"})
    ]

# `slice`

```elixir
@spec slice(t(), integer(), pos_integer()) :: t()
```

Returns data starting at the offset `start` and of the given `length`.

It is like `String.slice/3` but for `t:t/0`.

## Examples

    iex> Owl.Data.slice([Owl.Data.tag("Hello world", :red), Owl.Data.tag("!", :green)], 6, 7)
    [Owl.Data.tag(["world"], :red), Owl.Data.tag(["!"], :green)]

    iex> Owl.Data.slice(Owl.Data.tag("Hello world", :red), 20, 10)
    []

# `split`

```elixir
@spec split(t(), String.pattern() | Regex.t()) :: [t()]
```

Divides data into parts based on a pattern, preserving sequences for tagged data in new tags.

## Example

    iex> Owl.Data.split(["first second ", Owl.Data.tag("third fourth", :red)], " ")
    ["first", "second", Owl.Data.tag(["third"], :red), Owl.Data.tag(["fourth"], :red)]

    iex> Owl.Data.split(["first   second ", Owl.Data.tag("third    fourth", :red)], ~r/ +/)
    ["first", "second", Owl.Data.tag(["third"], :red), Owl.Data.tag(["fourth"], :red)]

# `tag`

```elixir
@spec tag(data, sequence() | [sequence()]) :: Owl.Tag.t(data) when data: t()
```

Builds a tag.

## Examples

    iex> Owl.Data.tag(["hello ", Owl.Data.tag("world", :green), "!!!"], :red)
    Owl.Data.tag(["hello ", Owl.Data.tag("world", :green), "!!!"], :red)

    iex> Owl.Data.tag("hello world", [:green, :red_background])
    Owl.Data.tag("hello world", [:green, :red_background])

    iex> Owl.Data.tag("Example", hyperlink: "https://example.com")
    Owl.Data.tag("Example", {:hyperlink, "https://example.com"})

# `to_chardata`

```elixir
@spec to_chardata(t()) :: IO.chardata()
```

Transforms data into `t:IO.chardata/0`, which can be consumed by the `IO` module.

## Examples

    iex> "hello" |> Owl.Data.tag(:red) |> Owl.Data.to_chardata()
    [[[[[] | "\e[31m"], "hello"] | "\e[39m"] | "\e[0m"]

# `truncate`

```elixir
@spec truncate(t(), pos_integer()) :: t()
```

Truncates data so the length of the returned data is <= `length`.

Appends an ellipsis if the data was truncated.

## Examples
    iex> Owl.Data.truncate([Owl.Data.tag("Hello", :red), Owl.Data.tag(" world!", :green)], 10)
    [Owl.Data.tag(["Hello"], :red), Owl.Data.tag([" wor"], :green), "…"]

    iex> Owl.Data.truncate("Hello", 10)
    "Hello"

    iex> Owl.Data.truncate("Hello", 4)
    ["Hel", "…"]

    iex> Owl.Data.truncate("Hello", 5)
    "Hello"

    iex> Owl.Data.truncate("Hello", 1)
    "…"

# `unlines`

```elixir
@spec unlines([t()]) :: [t()]
```

Creates a `t:t/0` from a list of `t:t/0`, inserting newline characters between the original elements.

## Examples

    iex> Owl.Data.unlines(["a", "b", "c"])
    ["a", "\n", "b", "\n", "c"]

    iex> ["first\nsecond\n", Owl.Data.tag("third\nfourth", :red)]
    ...> |> Owl.Data.lines()
    ...> |> Owl.Data.unlines()
    ...> |> Owl.Data.to_chardata()
    Owl.Data.to_chardata(["first\nsecond\n", Owl.Data.tag("third\nfourth", :red)])

# `untag`

```elixir
@spec untag(t()) :: IO.chardata()
```

Removes information about sequences and keeps only content of the tag.

## Examples

    iex> Owl.Data.tag("Hello", :red) |> Owl.Data.untag()
    "Hello"

    iex> Owl.Data.tag([72, 101, 108, 108, 111], :red) |> Owl.Data.untag()
    ~c"Hello"

    iex> Owl.Data.tag(["Hello", Owl.Data.tag("world", :green)], :red) |> Owl.Data.untag()
    ["Hello", "world"]

    iex> ["Hello ", Owl.Data.tag("world", :red), ["!"]] |> Owl.Data.untag()
    ["Hello ", "world", ["!"]]

    iex> Owl.Data.untag(Owl.Data.tag("Example", hyperlink: "https://example.com"))
    "Example"

# `zip`

```elixir
@spec zip(t(), t()) :: t()
```

Zips corresponding lines into a single line.

The zipping finishes as soon as either data completes.

## Examples

    iex> Owl.Data.zip("a\nb\nc", "d\ne\nf")
    [["a", "d"], "\n", ["b", "e"], "\n", ["c", "f"]]

    iex> Owl.Data.zip("a\nb", "c")
    [["a", "c"]]

    iex> 1..3
    ...> |> Enum.map(&to_string/1)
    ...> |> Enum.map(&Owl.Box.new/1) |> Enum.reduce(&Owl.Data.zip/2) |> to_string()
    """
    ┌─┐┌─┐┌─┐
    │3││2││1│
    └─┘└─┘└─┘
    """ |> String.trim_trailing()

---

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