Quillon.Transform.Split (Quillon v0.3.0)

Copy Markdown View Source

Text splitting operations at offset boundaries.

Splits text nodes so that mark operations can be applied to precise ranges.

Summary

Types

Any node tuple, including a custom type a consumer has introduced

List of inline children, which need not all be text nodes

A text node tuple

Functions

Find the node at a given offset.

Split text nodes at a given offset within the children list.

Split text nodes at both start and end offsets for a range.

Types

any_node()

@type any_node() :: {atom(), map(), list()}

Any node tuple, including a custom type a consumer has introduced

children()

@type children() :: [any_node()]

List of inline children, which need not all be text nodes

text_node()

@type text_node() :: {:text, %{text: String.t(), marks: list()}, []}

A text node tuple

Functions

find_node_at_offset(children, offset)

@spec find_node_at_offset(children(), non_neg_integer()) ::
  {children(), any_node() | nil, children(), non_neg_integer()}

Find the node at a given offset.

Returns {before_nodes, target_node, after_nodes, offset_in_node}.

Examples

iex> children = [
...>   {:text, %{text: "Hello", marks: []}, []},
...>   {:text, %{text: " world", marks: []}, []}
...> ]
iex> {before, target, after_nodes, offset_in_node} = Quillon.Transform.Split.find_node_at_offset(children, 7)
iex> before
[{:text, %{text: "Hello", marks: []}, []}]
iex> target
{:text, %{text: " world", marks: []}, []}
iex> after_nodes
[]
iex> offset_in_node
2

split_at_offset(children, offset)

@spec split_at_offset(children(), non_neg_integer()) :: children()

Split text nodes at a given offset within the children list.

Returns unchanged list if offset falls on a node boundary. Preserves marks when splitting a node.

Only text nodes split. An offset landing inside a node of another type is clamped rather than split - the list comes back unchanged - because splitting such a node would need a schema the library does not have.

Examples

iex> children = [{:text, %{text: "Hello world", marks: []}, []}]
iex> Quillon.Transform.Split.split_at_offset(children, 5)
[
  {:text, %{text: "Hello", marks: []}, []},
  {:text, %{text: " world", marks: []}, []}
]

iex> children = [{:text, %{text: "Hello world", marks: [:bold]}, []}]
iex> Quillon.Transform.Split.split_at_offset(children, 5)
[
  {:text, %{text: "Hello", marks: [:bold]}, []},
  {:text, %{text: " world", marks: [:bold]}, []}
]

iex> children = [{:text, %{text: "Hello", marks: []}, []}]
iex> Quillon.Transform.Split.split_at_offset(children, 0)
[{:text, %{text: "Hello", marks: []}, []}]

iex> children = [{:text, %{text: "Hello", marks: []}, []}]
iex> Quillon.Transform.Split.split_at_offset(children, 5)
[{:text, %{text: "Hello", marks: []}, []}]

split_range(children, start_offset, end_offset)

@spec split_range(children(), non_neg_integer(), non_neg_integer()) :: children()

Split text nodes at both start and end offsets for a range.

Splits at END first, then START to preserve start offset position. This is crucial for correct mark application.

Examples

iex> children = [{:text, %{text: "Hello world", marks: []}, []}]
iex> Quillon.Transform.Split.split_range(children, 2, 7)
[
  {:text, %{text: "He", marks: []}, []},
  {:text, %{text: "llo w", marks: []}, []},
  {:text, %{text: "orld", marks: []}, []}
]

iex> children = [{:text, %{text: "Hello", marks: []}, []}]
iex> Quillon.Transform.Split.split_range(children, 0, 5)
[{:text, %{text: "Hello", marks: []}, []}]