Packages

OT provides libraries for operational transformation, which is a method of achieving consistency in a collaborative software system.

Current section

Files

Jump to
ot_ex lib ot text operation.ex
Raw

lib/ot/text/operation.ex

defmodule OT.Text.Operation do
@moduledoc """
A list of components that iterates over and/or modifies a piece of text
"""
alias OT.Text.Component
@typedoc """
An operation, which is a list consisting of `t:OT.Text.Component.retain/0`,
`t:OT.Text.Component.insert/0`, and `t:OT.Text.Component.delete/0` components
"""
@type t :: [Component.t]
@doc """
Append a component to an operation.
## Example
iex> OT.Text.Operation.append([%{i: "Foo"}], %{i: "Bar"})
[%{i: "FooBar"}]
"""
@spec append(t, Component.t) :: t
def append([], comp), do: [comp]
def append(op, comp) do
last_component = List.last(op)
if Component.no_op?(comp) do
op
else
op
|> Enum.slice(0..-2)
|> Kernel.++(Component.join(last_component, comp))
end
end
@doc """
Invert an operation.
## Example
iex> OT.Text.Operation.invert([4, %{i: "Foo"}])
[4, %{d: "Foo"}]
"""
@spec invert(t) :: t
def invert(op), do: Enum.map(op, &Component.invert/1)
@doc """
Join two operations into a single operation.
## Example
iex> OT.Text.Operation.join([3, %{i: "Foo"}], [%{i: "Bar"}, 4])
[3, %{i: "FooBar"}, 4]
"""
@spec join(t, t) :: t
def join([], op_b), do: op_b
def join(op_a, []), do: op_a
def join(op_a, op_b) do
op_a
|> append(hd(op_b))
|> Kernel.++(tl(op_b))
end
@doc false
@spec random(OT.Text.datum) :: t
def random(text) do
text
|> do_random
|> Enum.reverse
end
@spec do_random(String.t, t) :: t
defp do_random(text, op \\ [])
defp do_random("", op), do: op
defp do_random(text, op) do
split_index = :rand.uniform(String.length(text) + 1) - 1
{chunk, new_text} = String.split_at(text, split_index)
comp = Component.random(chunk)
if Component.type(comp) == :insert do
do_random(text, [comp | op])
else
do_random(new_text, [comp | op])
end
end
end