Operations for manipulating rich text content within Quillon documents.
Provides apply_mark/4, remove_mark/4, and toggle_mark/4 for
paragraph-level mark manipulation, plus normalization and position utilities.
Example
# Apply bold to characters 0-5
para = {:paragraph, %{}, [{:text, %{text: "Hello world", marks: []}, []}]}
result = Quillon.Transform.apply_mark(para, 0, 5, :bold)
# => {:paragraph, %{}, [
# {:text, %{text: "Hello", marks: [:bold]}, []},
# {:text, %{text: " world", marks: []}, []}
# ]}
Summary
Functions
Apply a mark to text within a block at the given offset range.
Normalize a block's children (merge adjacent, remove empty).
Check if all text in the given range has a specific mark.
Remove a mark from text within a block at the given offset range.
Toggle a mark within a block at the given offset range.
Types
Functions
@spec apply_mark(block(), non_neg_integer(), non_neg_integer(), mark()) :: block()
Apply a mark to text within a block at the given offset range.
Automatically splits text nodes at boundaries and normalizes after.
Examples
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: []}, []}]}
iex> Quillon.Transform.apply_mark(para, 0, 5, :bold)
{:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello world", marks: []}, []}]}
iex> Quillon.Transform.apply_mark(para, 0, 5, :bold)
{:paragraph, %{}, [
{:text, %{text: "Hello", marks: [:bold]}, []},
{:text, %{text: " world", marks: []}, []}
]}
iex> para = {:paragraph, %{}, [{:text, %{text: "link", marks: []}, []}]}
iex> Quillon.Transform.apply_mark(para, 0, 4, {:link, %{href: "/page"}})
{:paragraph, %{}, [{:text, %{text: "link", marks: [{:link, %{href: "/page"}}]}, []}]}
Normalize a block's children (merge adjacent, remove empty).
Examples
iex> para = {:paragraph, %{}, [
...> {:text, %{text: "Hello", marks: [:bold]}, []},
...> {:text, %{text: " world", marks: [:bold]}, []}
...> ]}
iex> Quillon.Transform.normalize(para)
{:paragraph, %{}, [{:text, %{text: "Hello world", marks: [:bold]}, []}]}
@spec range_has_mark?(block(), non_neg_integer(), non_neg_integer(), atom()) :: boolean()
Check if all text in the given range has a specific mark.
Examples
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
iex> Quillon.Transform.range_has_mark?(para, 0, 5, :bold)
true
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: []}, []}]}
iex> Quillon.Transform.range_has_mark?(para, 0, 5, :bold)
false
iex> para = {:paragraph, %{}, [
...> {:text, %{text: "Hello", marks: [:bold]}, []},
...> {:text, %{text: " world", marks: []}, []}
...> ]}
iex> Quillon.Transform.range_has_mark?(para, 0, 11, :bold)
false
@spec remove_mark(block(), non_neg_integer(), non_neg_integer(), atom()) :: block()
Remove a mark from text within a block at the given offset range.
Automatically splits text nodes at boundaries and normalizes after.
Examples
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
iex> Quillon.Transform.remove_mark(para, 0, 5, :bold)
{:paragraph, %{}, [{:text, %{text: "Hello", marks: []}, []}]}
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold, :italic]}, []}]}
iex> Quillon.Transform.remove_mark(para, 0, 5, :bold)
{:paragraph, %{}, [{:text, %{text: "Hello", marks: [:italic]}, []}]}
@spec toggle_mark(block(), non_neg_integer(), non_neg_integer(), mark()) :: block()
Toggle a mark within a block at the given offset range.
If all text in range has the mark, removes it. Otherwise, adds it.
Examples
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: []}, []}]}
iex> Quillon.Transform.toggle_mark(para, 0, 5, :bold)
{:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
iex> para = {:paragraph, %{}, [{:text, %{text: "Hello", marks: [:bold]}, []}]}
iex> Quillon.Transform.toggle_mark(para, 0, 5, :bold)
{:paragraph, %{}, [{:text, %{text: "Hello", marks: []}, []}]}