Current section

Files

Jump to
y_ex lib sticky_index.ex
Raw

lib/sticky_index.ex

defmodule Yex.StickyIndex do
@moduledoc """
A sticky index provides position references that are unaffected by document changes, based on the Yjs model.
It maintains its relative position when placed before or after specific characters or elements.
## Features
- Stable position references unaffected by document changes
- Compatible with shared types like Text, Array, and XML
- Maintains relative positions after insert/delete operations
- Ideal for tracking cursor positions and text selections
## Usage
Numeric indices are unsuitable for tracking user selections because their positions
change when content is inserted or deleted. Sticky indices provide stable references
that maintain their relative positions through such changes:
iex> alias Yex.{StickyIndex, Doc, Text}
iex> doc = Doc.new()
iex> txt = Doc.get_text(doc, "text")
iex> Doc.transaction(doc, fn ->
...> Text.insert(txt, 0, "abc")
...> # => 'abc'
...> # Create position tracker (marked as . in comments)
...> pos = StickyIndex.new(txt, 2, :after)
...> # => 'ab.c'
...>
...> # Modify text
...> Text.insert(txt, 1, "def")
...> # => 'adefb.c'
...> Text.delete(txt, 4, 1)
...> # => 'adef.c'
...>
...> # Get current offset index
...> {:ok, a} = StickyIndex.get_offset(pos)
...> # => 4
...> assert a.index == 4
...> end)
"""
defstruct [
:doc,
:reference,
:assoc
]
@type t :: %__MODULE__{
doc: Yex.Doc.t(),
reference: reference(),
assoc: :before | :after
}
alias Yex.Doc
require Yex.Doc
@type shared_type ::
%Yex.Array{}
| %Yex.Text{}
| %Yex.XmlElement{}
| %Yex.XmlText{}
| %Yex.XmlFragment{}
@spec new(shared_type, integer(), :before | :after) :: t
def new(%{doc: doc} = shared_type, index, assoc) do
Doc.run_in_worker_process(doc,
do: Yex.Nif.sticky_index_new(shared_type, cur_txn(shared_type), index, assoc)
)
end
@spec get_offset(t) :: {:ok, %{index: integer(), assoc: :before | :after}} | :error
def get_offset(%__MODULE__{doc: doc} = sticky_index) do
Doc.run_in_worker_process(doc,
do: Yex.Nif.sticky_index_get_offset(sticky_index, cur_txn(sticky_index))
)
end
defp cur_txn(%{doc: %Yex.Doc{reference: doc_ref}}) do
Process.get(doc_ref, nil)
end
end