text_delta v1.4.0 TextDelta.Operation View Source
Operations represent a smallest possible change applicable to a text.
In case of text, there are exactly 3 possible operations we might want to perform:
TextDelta.Operation.insert/0
: insert a new piece of text or an embedded elementTextDelta.Operation.retain/0
: preserve given number of characters in sequenceTextDelta.Operation.delete/0
: delete given number of characters in sequence
insert
and retain
operations can also have optional
TextDelta.Attributes.t/0
attached to them. This is how Delta manages rich
text formatting without breaking the Operational Transformation
paradigm.
Link to this section Summary
Types
The result of comparison operation
Delete operation represents an intention to delete a sequence of characters from the text. It is always a number and it is always positive
An insertable rich text element. Either a piece of text, a number or an embed
Insert operation represents an intention to add a text or an embedded element to a text state. Text additions are represented with binary strings and embedded elements are represented with either an integer or an object
Retain operation represents an intention to keep a sequence of characters unchanged in the text. It is always a number and it is always positive
An operation. Either insert
, retain
or delete
Atom representing type of operation
Functions
Attempts to compact two given operations into one
Compares the length of two operations
Creates a new delete operation
Creates a new insert operation
Returns length of text affected by a given operation
Creates a new retain operation
Splits operations into two halves around the given index
Checks if given operation is trimmable
Returns atom representing type of the given operation
Link to this section Types
The result of comparison operation.
Delete operation represents an intention to delete a sequence of characters from the text. It is always a number and it is always positive.
An insertable rich text element. Either a piece of text, a number or an embed.
insert() :: %{insert: element()} | %{insert: element(), attributes: TextDelta.Attributes.t()}
Insert operation represents an intention to add a text or an embedded element to a text state. Text additions are represented with binary strings and embedded elements are represented with either an integer or an object.
Insert also allows us to attach attributes to the element being inserted.
retain() :: %{retain: non_neg_integer()} | %{retain: non_neg_integer(), attributes: TextDelta.Attributes.t()}
Retain operation represents an intention to keep a sequence of characters unchanged in the text. It is always a number and it is always positive.
In addition to indicating preservation of existing text, retain also allows us to change formatting of retained text or element via optional attributes.
An operation. Either insert
, retain
or delete
.
Atom representing type of operation.
Link to this section Functions
Attempts to compact two given operations into one.
If successful, will return a list with just a single, compacted operation. In any other case both operations will be returned back unchanged.
Compacting works by combining same operations with the same attributes
together. Easiest way to think about this function is that it produces an
exact opposite effect of TextDelta.Operation.slice/2
.
Text insert
is compacted by concatenating strings, retain
or delete
is
compacted by adding the sequence numbers. Only operations with the same
attribute set are compacted. This is mostly used to keep deltas short and
canonical.
Examples
Text inserts are compacted into a single insert:
iex> TextDelta.Operation.compact(%{insert: "hel"}, %{insert: "lo"})
[%{insert: "hello"}]
Retains and deletes are compacted by adding their sequence numbers:
iex> TextDelta.Operation.compact(%{retain: 2}, %{retain: 3})
[%{retain: 5}]
Compares the length of two operations.
Example
iex> TextDelta.Operation.compare(%{insert: "hello!"}, %{delete: 3})
:gt
Creates a new delete operation.
Example
To delete 3 next characters from the text, we can create a following operation:
iex> TextDelta.Operation.delete(3)
%{delete: 3}
insert(element(), TextDelta.Attributes.t()) :: insert()
Creates a new insert operation.
Attributes are optional and are ignored if empty map or nil
is provided.
Examples
To indicate that we need to insert a text “hello” into the text, we can use following insert:
iex> TextDelta.Operation.insert("hello")
%{insert: "hello"}
In addition, we can indicate that “hello” should be inserted with specific attributes:
iex> TextDelta.Operation.insert("hello", %{bold: true, color: "magenta"})
%{insert: "hello", attributes: %{bold: true, color: "magenta"}}
We can also insert non-text objects, such as an image:
iex> TextDelta.Operation.insert(%{img: "me.png"}, %{alt: "My photo"})
%{insert: %{img: "me.png"}, attributes: %{alt: "My photo"}}
Returns length of text affected by a given operation.
Length for insert
operations is calculated by counting the length of text
itself being inserted, length for retain
or delete
operations is a length
of sequence itself. Attributes have no effect over the length.
Examples
For text inserts it is a length of text itself:
iex> TextDelta.Operation.length(%{insert: "hello!"})
6
For embed inserts, however, length is always 1:
iex> TextDelta.Operation.length(%{insert: 3})
1
For retain and deletes, the number itself is the length:
iex> TextDelta.Operation.length(%{retain: 4})
4
retain(non_neg_integer(), TextDelta.Attributes.t()) :: retain()
Creates a new retain operation.
Attributes are optional and are ignored if empty map or nil
is provided.
Examples
To keep 5 next characters inside the text, we can use the following retain:
iex> TextDelta.Operation.retain(5)
%{retain: 5}
To make those exact 5 characters bold, while keeping them, we can use attributes:
iex> TextDelta.Operation.retain(5, %{bold: true})
%{retain: 5, attributes: %{bold: true}}
Splits operations into two halves around the given index.
Text insert
is split via slicing the text itself, retain
or delete
is
split by subtracting the sequence number. Attributes are preserved during
splitting. This is mostly used for normalisation of deltas during iteration.
Examples
Text inserts
are split via slicing the text itself:
iex> TextDelta.Operation.slice(%{insert: "hello"}, 3)
{%{insert: "hel"}, %{insert: "lo"}}
retain
and delete
are split by subtracting the sequence number:
iex> TextDelta.Operation.slice(%{retain: 5}, 2)
{%{retain: 2}, %{retain: 3}}
Checks if given operation is trimmable.
Technically only retain
operations are trimmable, but the creator of this
library didn’t feel comfortable exposing that knowledge outside of this
module.
Example
iex> TextDelta.Operation.trimmable?(%{retain: 3})
true