Documentation
¶
Overview ¶
Package types holds the user-facing shared CRDT collection types: Map, Array, Text, XmlElement / XmlFragment / XmlText.
Each type is a thin wrapper around a *block.Branch — the branch owns the actual CRDT state (linked list of Items + map of map-key tails); the type wrapper exposes idiomatic Go APIs that hide the block-layer mechanics.
Construction pattern: a Doc registers root branches by name via Doc.Branch(name); the types-layer constructor (NewMap, NewArray, NewText) wraps the returned branch.
Mutating methods take a *doc.TransactionMut. Read methods do not require a transaction parameter for ergonomic reasons but the caller is responsible for holding at least a read lock on the Doc (typically via a ReadTxn).
See docs/yrs-port-notes/types-map.md for the per-method contract of the Map type, and DESIGN.md for the API style decisions.
Index ¶
- Variables
- func EncodeRelativePosition(rpos RelativePosition) ([]byte, error)
- type AbsolutePosition
- type Array
- func (a *Array) Branch() *block.Branch
- func (a *Array) Delete(txn *doc.TransactionMut, idx, length uint64)
- func (a *Array) Get(idx uint64) any
- func (a *Array) Insert(txn *doc.TransactionMut, idx uint64, value any)
- func (a *Array) InsertArray(txn *doc.TransactionMut, idx uint64) *Array
- func (a *Array) InsertMap(txn *doc.TransactionMut, idx uint64) *Map
- func (a *Array) InsertRange(txn *doc.TransactionMut, idx uint64, values []any)
- func (a *Array) InsertText(txn *doc.TransactionMut, idx uint64) *Text
- func (a *Array) Len() uint64
- func (a *Array) Observe(fn func(*ArrayEvent)) func()
- func (a *Array) ObserveDeep(fn func([]any)) func()
- func (a *Array) Push(txn *doc.TransactionMut, values ...any)
- func (a *Array) Range(fn func(idx uint64, value any) bool)
- func (a *Array) ToSlice() []any
- type ArrayDeltaOp
- type ArrayEvent
- type Attrs
- type BranchHolder
- type ChunkKind
- type DeltaOp
- type KeyChange
- type Map
- func (m *Map) Branch() *block.Branch
- func (m *Map) Clear(txn *doc.TransactionMut)
- func (m *Map) Delete(txn *doc.TransactionMut, key string)
- func (m *Map) Get(key string) any
- func (m *Map) GetDoc(d *doc.Doc, key string) (*doc.Doc, bool)
- func (m *Map) Has(key string) bool
- func (m *Map) Len() int
- func (m *Map) Observe(fn func(*MapEvent)) func()
- func (m *Map) ObserveDeep(fn func([]any)) func()
- func (m *Map) Range(fn func(key string, value any) bool)
- func (m *Map) Set(txn *doc.TransactionMut, key string, value any)
- func (m *Map) SetArray(txn *doc.TransactionMut, key string) *Array
- func (m *Map) SetDoc(txn *doc.TransactionMut, key string) *doc.Doc
- func (m *Map) SetDocWithOptions(txn *doc.TransactionMut, key string, autoLoad bool) *doc.Doc
- func (m *Map) SetMap(txn *doc.TransactionMut, key string) *Map
- func (m *Map) SetText(txn *doc.TransactionMut, key string) *Text
- type MapEvent
- type RelativePosition
- type Text
- func (t *Text) ApplyDelta(txn *doc.TransactionMut, ops []DeltaOp) error
- func (t *Text) Branch() *block.Branch
- func (t *Text) Delete(txn *doc.TransactionMut, idx, length uint64) error
- func (t *Text) Format(txn *doc.TransactionMut, idx, length uint64, attrs Attrs) error
- func (t *Text) Insert(txn *doc.TransactionMut, idx uint64, str string) error
- func (t *Text) InsertEmbed(txn *doc.TransactionMut, idx uint64, value any) error
- func (t *Text) InsertWithAttributes(txn *doc.TransactionMut, idx uint64, str string, attrs Attrs) error
- func (t *Text) Length() uint64
- func (t *Text) Observe(fn func(*TextEvent)) func()
- func (t *Text) Range(fn func(kind ChunkKind, value any, attrs Attrs) bool)
- func (t *Text) String() string
- func (t *Text) ToDelta() []DeltaOp
- type TextEvent
- type XmlElement
- func (e *XmlElement) Attributes() map[string]string
- func (e *XmlElement) Branch() *block.Branch
- func (e *XmlElement) Delete(txn *doc.TransactionMut, idx, length uint64)
- func (e *XmlElement) Get(idx uint64) any
- func (e *XmlElement) GetAttribute(name string) (string, bool)
- func (e *XmlElement) InsertXmlElement(txn *doc.TransactionMut, idx uint64, nodeName string) *XmlElement
- func (e *XmlElement) InsertXmlText(txn *doc.TransactionMut, idx uint64) *XmlText
- func (e *XmlElement) Length() uint64
- func (e *XmlElement) NodeName() string
- func (e *XmlElement) Range(fn func(idx uint64, child any) bool)
- func (e *XmlElement) RemoveAttribute(txn *doc.TransactionMut, name string)
- func (e *XmlElement) SetAttribute(txn *doc.TransactionMut, name, value string)
- func (e *XmlElement) ToString() string
- type XmlFragment
- func (f *XmlFragment) Branch() *block.Branch
- func (f *XmlFragment) Delete(txn *doc.TransactionMut, idx, length uint64)
- func (f *XmlFragment) Get(idx uint64) any
- func (f *XmlFragment) InsertXmlElement(txn *doc.TransactionMut, idx uint64, nodeName string) *XmlElement
- func (f *XmlFragment) InsertXmlText(txn *doc.TransactionMut, idx uint64) *XmlText
- func (f *XmlFragment) Length() uint64
- func (f *XmlFragment) Range(fn func(idx uint64, child any) bool)
- func (f *XmlFragment) ToString() string
- type XmlText
Constants ¶
This section is empty.
Variables ¶
var ErrDetachedType = errors.New("ygo: relative position: type is not anchored in a document")
ErrDetachedType reports a wrapper whose branch is neither a root type nor attached to a live item, so no stable anchor exists for it.
var ErrEmptyAnchor = errors.New("ygo: relative position: empty anchor")
ErrEmptyAnchor reports a RelativePosition with no anchor set (none of Item, TName, Type). Such a value cannot come from Create or Decode; it indicates a caller-constructed zero value.
Functions ¶
func EncodeRelativePosition ¶ added in v1.1.0
func EncodeRelativePosition(rpos RelativePosition) ([]byte, error)
EncodeRelativePosition serialises rpos to the binary form of Y.encodeRelativePosition: a varuint tag (0 item / 1 tname / 2 type), the payload, then assoc as a signed varint. Returns ErrEmptyAnchor for a zero-value rpos (yjs throws unexpectedCase here).
Types ¶
type AbsolutePosition ¶ added in v1.1.0
AbsolutePosition is a resolved RelativePosition: the branch of the shared type the position lives in and the current numeric index within it (UTF-16 code units for Text, elements for Array).
func CreateAbsolutePositionFromRelativePosition ¶ added in v1.1.0
func CreateAbsolutePositionFromRelativePosition(d *doc.Doc, rpos RelativePosition) (AbsolutePosition, bool)
CreateAbsolutePositionFromRelativePosition resolves rpos against the current state of d. Returns ok=false when the anchor refers to state this replica has not seen, or to a garbage-collected range.
Follows undone deletions (the yjs default): an anchor on a character that was deleted and restored by undo resolves to the restored copy.
Mirrors yjs createAbsolutePositionFromRelativePosition.
type Array ¶
type Array struct {
// contains filtered or unexported fields
}
Array is the user-facing wrapper around a positional Branch. The branch's Start linked list holds the document-order sequence; we count live (non-deleted, countable) items to translate user-facing positions to *Item references.
Construct via NewArray; usually obtained as types.NewArray(d.Branch("name")).
Array shares its underlying Branch with Map's possible usage of the same name — Map uses Branch.Map (string-keyed), Array uses Branch.Start (positional). The two surfaces operate on disjoint fields of the same Branch and may technically coexist, though in practice each root branch is used as one type.
func (*Array) Delete ¶
func (a *Array) Delete(txn *doc.TransactionMut, idx, length uint64)
Delete removes length elements starting at idx. Items that straddle the [idx, idx+length) range are split via SplitBlock so the deletion lands on Item boundaries.
length == 0 is a no-op. Out-of-range deletions clip to Len().
func (*Array) Get ¶
Get returns the value at idx, or nil if idx is out of range or the underlying content kind is not unpacked by extractValueAt.
O(N) over the linked list. Search markers help only when callers alternate Get with Insert/Delete near the same area; pure Get loops still walk from start (no marker write occurs here).
func (*Array) Insert ¶
func (a *Array) Insert(txn *doc.TransactionMut, idx uint64, value any)
Insert inserts a single value at idx. Convenience for InsertRange with a one-element slice.
idx must be in [0, Len]; out-of-range inserts append at the end (matching JS Y.Array.insert tolerant behaviour).
func (*Array) InsertArray ¶
func (a *Array) InsertArray(txn *doc.TransactionMut, idx uint64) *Array
InsertArray inserts a freshly-constructed nested Array at idx.
func (*Array) InsertMap ¶
func (a *Array) InsertMap(txn *doc.TransactionMut, idx uint64) *Map
InsertMap inserts a freshly-constructed nested Map at idx and returns the wrapper. Per docs/yrs-port-notes/nested-types.md §6.
Like InsertRange, idx is clamped to [0, Len]; the new Map element occupies one slot (Len contribution = 1).
func (*Array) InsertRange ¶
func (a *Array) InsertRange(txn *doc.TransactionMut, idx uint64, values []any)
InsertRange inserts every value at idx in order, packed into a single ContentAny Item with Len == len(values). Per types-array.md finding 3, this matches yrs's RangePrelim squash-on-insert.
Mid-block inserts trigger Store.SplitBlock to materialize a clean boundary at idx.
func (*Array) InsertText ¶
func (a *Array) InsertText(txn *doc.TransactionMut, idx uint64) *Text
InsertText inserts a freshly-constructed nested Text at idx (plain-text only).
func (*Array) Len ¶
Len returns the number of live elements. O(1) — reads branch.BlockLen, which Item.Integrate (+countable.Len) and TransactionMut.Delete (-countable.Len) maintain.
Per types-array.md finding 5: Map keys are excluded from BlockLen; the counter only tracks positional, countable, live items.
func (*Array) Observe ¶ added in v1.2.0
func (a *Array) Observe(fn func(*ArrayEvent)) func()
Observe registers fn to run after every transaction that changes this array, with an ArrayEvent carrying the change delta. Returns an unsubscribe function. Fires on local edits and remote updates.
Mirrors yjs yarray.observe. The callback runs while the doc write lock is held; it must not start a new transaction on the same doc.
func (*Array) ObserveDeep ¶ added in v1.2.0
ObserveDeep registers fn to run after any transaction that changes this array OR any shared type nested under it, with the list of events (each carrying its Path relative to this array). Returns an unsubscribe function. Mirrors yjs yarray.observeDeep.
func (*Array) Push ¶
func (a *Array) Push(txn *doc.TransactionMut, values ...any)
Push appends one or more values to the end of the array. Equivalent to InsertRange(txn, Len(), values).
type ArrayDeltaOp ¶ added in v1.2.0
ArrayDeltaOp is one operation of an array change delta. Exactly one of the three is set: Insert (the inserted values), Delete (a count of removed elements), or Retain (a count of unchanged elements). The text DeltaOp (text_format.go) is the string-valued analogue for Text.
type ArrayEvent ¶ added in v1.2.0
type ArrayEvent struct {
Target *Array
// Path locates Target relative to the ObserveDeep type firing;
// empty for shallow Observe.
Path []any
Delta []ArrayDeltaOp
}
ArrayEvent is delivered to Array observers (Array.Observe) after a transaction that changed the array. Delta is the Quill-style change description. Mirrors yjs YArrayEvent.
type Attrs ¶
Attrs is the format-attribute map carried by KindFormat markers and exposed in Range / ToDelta output. Per docs/yrs-port-notes/types-text-rich.md §9.
Nil values inside Attrs mean "clear this attribute" — they encode to the Any-Null tag on the wire and are interpreted by currentAttributes as removing the key. To distinguish "key not present" from "key present with nil value", check via the second return of a map lookup.
type BranchHolder ¶ added in v1.1.0
BranchHolder is satisfied by every shared-type wrapper (Map, Array, Text, XmlFragment, XmlElement); it exposes the underlying Branch.
type ChunkKind ¶
type ChunkKind uint8
ChunkKind discriminates the variants emitted by Text.Range.
const ( // ChunkString is a contiguous run of UTF-8 text from a KindString // item; the value passed to fn is a string. ChunkString ChunkKind = iota // ChunkEmbed is a single embedded value from a KindEmbed item; // the value passed to fn is whatever the embed payload's runtime // type is (typically map[string]any or a primitive Any). ChunkEmbed )
type DeltaOp ¶
type DeltaOp struct {
// Insert is the text to insert at the cursor. Mutually exclusive
// with Embed, Retain, Delete.
Insert string
// Embed is a single embedded value (image, mention, etc.). Mutually
// exclusive with Insert, Retain, Delete. The Embed value's runtime
// type matches whatever Y.encodeStateAsUpdate-produced JS clients
// would round-trip through Any — typically a map[string]any.
Embed any
// Retain advances the cursor by N UTF-16 units, optionally
// applying Attributes to the retained range. Mutually exclusive
// with Insert, Embed, Delete.
Retain uint64
// Delete removes N UTF-16 units from the cursor. Mutually exclusive
// with Insert, Embed, Retain.
Delete uint64
// Attributes applies to Insert, Embed, or Retain. Nil-valued keys
// inside Attributes mean "clear this attribute" on the affected
// range; absent keys preserve the current value.
Attributes Attrs
}
DeltaOp is a single op from a Quill-style delta. Exactly one of Insert / Retain / Delete is non-zero for any given op. Used by ToDelta to expose the doc and by ApplyDelta to ingest one (the ApplyDelta API ships in a follow-up commit; see tech-debt.md).
type KeyChange ¶ added in v1.2.0
type KeyChange struct {
// Action is "add", "update", or "delete".
Action string
// OldValue is the value the key held before this transaction; nil
// for an "add".
OldValue any
}
KeyChange describes one map key's change within a transaction: the kind of change and the value the key held before it. Mirrors yjs's YMapEvent change entry ({ action, oldValue }).
type Map ¶
type Map struct {
// contains filtered or unexported fields
}
Map is the user-facing wrapper around a map-like Branch. Construct via NewMap; usually obtained as types.NewMap(d.Branch("name")).
Map exposes Set / Get / Delete / Has / Len / Range / Clear plus a Branch accessor for low-level integration.
func NewMap ¶
NewMap wraps the given branch as a Map. The branch's Map field is initialized lazily if nil — typical for a freshly-created root branch from Doc.Branch(name).
func (*Map) Branch ¶
Branch returns the underlying *block.Branch. Useful for cross-package wiring (e.g. observers, encoders) that need to operate at the block layer.
func (*Map) Clear ¶
func (m *Map) Clear(txn *doc.TransactionMut)
Clear deletes every entry in the map.
Per types-map.md finding 4: yrs's Map::clear calls txn.delete on every entry, including already-tombstoned ones. Our txn.Delete is idempotent (early-returns on IsDeleted), so we filter here purely to avoid the no-op — semantics match yrs either way.
func (*Map) Delete ¶
func (m *Map) Delete(txn *doc.TransactionMut, key string)
Delete tombstones the entry under key. Calling Delete when the key is absent or already tombstoned is a no-op.
Per types-map.md finding 3: Delete does NOT clear branch.Map[key]. The map entry continues to point at the tombstoned item so a subsequent Set can chain off it as Origin/Left, preserving YATA convergence for concurrent writers that didn't see the delete yet.
func (*Map) Get ¶
Get returns the current value under key, or nil if the key is absent or its tail item is tombstoned.
Per types-map.md finding 5: this is one-step. branch.Map[key] is the LWW winner; if it's deleted there is no live predecessor to fall back to.
func (*Map) GetDoc ¶ added in v0.10.0
GetDoc returns the subdocument referenced under key within parent d, or (nil, false) if key holds no live subdoc. The returned handle is the registered instance for the referenced GUID (created on first access for subdocs surfaced from a decoded update).
func (*Map) Len ¶
Len returns the number of live (non-tombstoned) entries.
O(N) over the map size: iterates and counts. yrs has a TODO at map.rs:158 about caching this on the Branch — we'd inherit that optimization for free if we add the cache to Branch. Tracked in tech-debt.md if it shows up in benchmarks.
func (*Map) Observe ¶ added in v1.2.0
Observe registers fn to run after every transaction that changes this map, with a MapEvent describing the changed keys. Returns an unsubscribe function. Observers fire on local edits and on remote updates applied via ApplyUpdate.
Mirrors yjs ymap.observe. The callback runs while the doc write lock is held; it must not start a new transaction on the same doc.
func (*Map) ObserveDeep ¶ added in v1.2.0
ObserveDeep registers fn to run after any transaction that changes this map OR any shared type nested under it, with the list of events (each carrying its Path relative to this map). Returns an unsubscribe function. Mirrors yjs ymap.observeDeep.
func (*Map) Range ¶
Range iterates over live (key, value) pairs in unspecified order (Go map iteration). The callback returns false to stop early.
func (*Map) Set ¶
func (m *Map) Set(txn *doc.TransactionMut, key string, value any)
Set stores value under key in this map. Concurrent writes from different replicas converge to the same final value — the writer with the higher (clientID, clock) wins per YATA.
value may be of any type; the encoding-layer port (deferred per tech-debt.md) will decide how each Go type maps to a wire variant. For now value is stored as ContentAny, which round-trips through reflection.
Mirrors yrs Map::insert (yrs/src/types/map.rs ~line 73).
func (*Map) SetArray ¶
func (m *Map) SetArray(txn *doc.TransactionMut, key string) *Array
SetArray inserts a freshly-constructed nested Array under key.
func (*Map) SetDoc ¶ added in v0.10.0
setNested is the shared scaffolding for SetMap/SetArray/SetText: build an Item with KindType content and the supplied Branch, chain off the existing tail at this key (Origin = left.LastID()), push to store, integrate. The Item.Integrate KindType arm wires SetDoc nests a new subdocument under key and returns its handle. The subdocument carries its own GUID; its content syncs as a separate update stream, while the parent stores only the reference (GUID + options). Mirrors yjs `map.set(key, new Y.Doc())`.
func (*Map) SetDocWithOptions ¶ added in v1.0.0
SetDocWithOptions is SetDoc with subdocument options. When autoLoad is true the reference carries the autoLoad flag on the wire, so peers that decode it mark the subdocument to load automatically (it appears in their SubdocsLoaded set).
func (*Map) SetMap ¶
func (m *Map) SetMap(txn *doc.TransactionMut, key string) *Map
SetMap inserts a freshly-constructed nested Map under key and returns the wrapper bound to it. Subsequent edits via the returned *Map are addressed to this map-key slot in m; concurrent peers see the same nested-map structure once they apply the resulting updates.
Per docs/yrs-port-notes/nested-types.md §5 — recommended API over overloading Set(any) so the caller never has to type-assert the returned wrapper.
type MapEvent ¶ added in v1.2.0
type MapEvent struct {
// Target is the map that changed.
Target *Map
// Path is the location of Target relative to the type whose
// ObserveDeep callback is firing; empty for a shallow Observe and
// for a deep observer on Target itself.
Path []any
// Keys maps each changed key to how it changed. Only keys whose
// observable value actually changed appear (a key added and
// removed in the same transaction is omitted, matching yjs).
Keys map[string]KeyChange
}
MapEvent is delivered to Map observers after a transaction that changed the map. Mirrors yjs YMapEvent.
func (*MapEvent) KeysChanged ¶ added in v1.2.0
KeysChanged returns the set of keys that changed, for callers that only need the names.
type RelativePosition ¶ added in v1.1.0
RelativePosition is a position anchored to the Yjs document model rather than to a numeric index, so it stays attached to the same logical character as concurrent edits land before or after it. The canonical use is collaborative cursors and selections.
Exactly one of Item, TName, Type identifies the anchor:
- Item: the position sits on a concrete character, identified by the ID of the item (plus offset baked into the clock).
- TName: the position sits at the end of a ROOT type with this name.
- Type: the position sits at the end of a NESTED type, identified by the ID of the item whose content holds the type.
Assoc is the side the position sticks to: >= 0 associates with the character after the position (default), < 0 with the character before it.
Mirrors yjs RelativePosition (utils/RelativePosition.js); the binary form produced by Encode is byte-compatible with Y.encodeRelativePosition.
TName is a pointer because the empty string is a legal root-type name in yjs (doc.get(”)); nil means "not a root anchor".
func CreateRelativePositionFromTypeIndex ¶ added in v1.1.0
func CreateRelativePositionFromTypeIndex(t BranchHolder, index uint64, assoc int64) (RelativePosition, error)
CreateRelativePositionFromTypeIndex anchors the numeric position index within the shared type t as a RelativePosition. index counts UTF-16 code units for Text and elements for Array. assoc chooses the side the position sticks to (>= 0 right, < 0 left).
Mirrors yjs createRelativePositionFromTypeIndex. The caller must not hold an open write transaction mutating t concurrently.
func DecodeRelativePosition ¶ added in v1.1.0
func DecodeRelativePosition(buf []byte) (RelativePosition, error)
DecodeRelativePosition parses the binary form produced by EncodeRelativePosition / Y.encodeRelativePosition. A missing assoc (older producers) decodes as 0, matching yjs.
type Text ¶
type Text struct {
// contains filtered or unexported fields
}
Text is the user-facing wrapper around a Branch holding plain text. The branch's positional linked list (branch.Start) holds ContentString items; per types-text.md their Item.Len is in UTF-16 code units (matching JS Yjs and the wire format).
Plain-text only in this commit. Rich-text formatting (Format markers, applyDelta, embeds, attributes) is tracked in docs/tech-debt.md.
All offsets and lengths in Insert / Delete are UTF-16 code units, not Go bytes and not runes. Use utf16.Length(string) to convert a Go string to its UTF-16 length when computing positions.
func NewText ¶
NewText wraps the given branch as a Text. It tags the branch as a Text type (TypeRefText) so the observer dispatcher can tell a root Text from a root Array, whose branches both default to TypeRef zero otherwise. The root TypeRef is never serialized (roots are written by name), so this does not affect the wire format.
func (*Text) ApplyDelta ¶
func (t *Text) ApplyDelta(txn *doc.TransactionMut, ops []DeltaOp) error
ApplyDelta walks ops in order and dispatches each to the appropriate primitive (InsertWithAttributes / InsertEmbed / Format / Delete) while maintaining a cursor through the text.
Op semantics (mirrors JS Y.Text.applyDelta from testdata/gen/node_modules/yjs/src/types/YText.js):
- Insert string with optional Attributes — insert text at cursor, advance cursor by len(text)
- Insert non-string (Embed) — insert single embed at cursor, advance cursor by 1
- Retain N with optional Attributes — if Attributes is non-nil, apply Format(cursor, N, Attributes); advance cursor by N
- Delete N — delete N units at cursor; cursor unchanged (the text shifts left under the cursor)
All ops execute inside the supplied txn so the entire delta is atomic from peers' perspective. Errors abort mid-delta — the txn caller must decide whether to commit the partial result or roll back (the doc layer's TransactionMut has no Rollback, so the partial mutation persists; this mirrors yrs's behaviour).
Per docs/yrs-port-notes/types-text-rich.md §7. Empty ops slice is a no-op; nil ops also a no-op.
func (*Text) Delete ¶
func (t *Text) Delete(txn *doc.TransactionMut, idx, length uint64) error
Delete removes length UTF-16 code units starting at idx. Items straddling the [idx, idx+length) range are split via Store.SplitBlock at UTF-16 boundaries so the deletion lands on Item edges.
Returns an error if the range exceeds the current Length.
Mirrors yrs Text::remove_range (text.rs:361-368) → remove (text.rs:806-863) for the plain-text path; the rich-text clean_format_gap call is omitted.
func (*Text) Format ¶
Format applies attrs to the range [idx, idx+length). For each attribute whose target value differs from the current formatting: open a marker at idx, walk through the range rewriting any intermediate markers for the same key (to ensure they don't re-establish the old value mid-range), and close with a marker at idx+length restoring the prior state.
length == 0 is a no-op. nil attrs is a no-op. Per docs/yrs-port-notes/types-text-rich.md §4.
func (*Text) Insert ¶
Insert inserts str at the UTF-16 code-unit position idx. idx must be in [0, Length()]. str may contain any Unicode characters; non-BMP chars (e.g. emoji) contribute 2 UTF-16 units each.
Multiple consecutive Insert calls produce separate Items; commit-time TrySquash merges adjacent same-client adjacent-clock String items into one (yrs/src/block.rs:1987-1990).
Returns an error if idx is out of range.
func (*Text) InsertEmbed ¶
InsertEmbed inserts a single embedded value at idx. The value must be a type representable through lib0 Any encoding — typically map[string]any or one of the scalar Any types.
Embeds count as one UTF-16 position in Text.Length. They are emitted by Range / ToDelta as an Embed op rather than a string chunk.
func (*Text) InsertWithAttributes ¶
func (t *Text) InsertWithAttributes(txn *doc.TransactionMut, idx uint64, str string, attrs Attrs) error
InsertWithAttributes inserts str at idx, opening format markers before the text for any attribute whose value differs from the current formatting, and closing them afterward to restore the prior state.
nil attrs is equivalent to Insert(idx, str). An empty map (zero keys) is also equivalent.
Per docs/yrs-port-notes/types-text-rich.md §5, the algorithm mirrors yrs Text::insert_with_attributes (text.rs:280-295) + JS Y.Text._insertWithAttributes (ytype.js:330-350).
func (*Text) Length ¶
Length returns the total number of UTF-16 code units in live content. O(1) — reads branch.BlockLen which Item.Integrate (+) and TransactionMut.Delete (-) maintain.
Per types-text.md §4: for our UTF-16-only port, BlockLen is already the count of UTF-16 units across live String items.
func (*Text) Observe ¶ added in v1.2.0
Observe registers fn to run after every transaction that changes this text, with a TextEvent carrying the formatting-aware change delta. Returns an unsubscribe function. Fires on local edits and remote updates.
Mirrors yjs ytext.observe. The callback runs while the doc write lock is held; it must not start a new transaction on the same doc.
func (*Text) Range ¶
Range walks the document and invokes fn for each (kind, value, attrs) chunk. attrs reflects the current formatting at this chunk's start.
Stops early when fn returns false. Returns immediately for an empty doc.
Per docs/yrs-port-notes/types-text-rich.md §8 — companion to ToDelta. Explicit ChunkKind discriminator means a string-typed embed payload is not misidentified as a text chunk by callers that switch on the runtime type of the value.
func (*Text) String ¶
String returns the concatenation of every live ContentString in document order as a Go UTF-8 string. O(N) over the linked list.
Mirrors yrs Text::get_string (text.rs:120-132).
func (*Text) ToDelta ¶
ToDelta returns the Quill-style delta representation of the doc. Adjacent same-attribute string chunks are coalesced into single insert ops. Embeds appear as separate ops.
The returned slice has no Retain / Delete ops — those exist only in deltas representing CHANGES, not snapshots.
type TextEvent ¶ added in v1.2.0
type TextEvent struct {
Target *Text
// Path locates Target relative to the ObserveDeep type firing;
// empty for shallow Observe.
Path []any
Delta []DeltaOp
}
TextEvent is delivered to Text observers (Text.Observe) after a transaction that changed the text. Delta is the Quill-style, formatting-aware change description (the same DeltaOp shape ToDelta produces). Mirrors yjs YTextEvent.
type XmlElement ¶
type XmlElement struct {
// contains filtered or unexported fields
}
XmlElement is an XML element — a tag name (NodeName), an attribute map (Map-like), and a list of children (Array-like) — all backed by the same Branch.
Attributes are stored as map-keyed Items with ContentAny string values. Children are stored as positional Items. The two surfaces (map and positional) coexist on the same Branch because they use disjoint fields (branch.Map vs branch.Start).
func NewXmlElement ¶
func NewXmlElement(branch *block.Branch) *XmlElement
NewXmlElement wraps the given branch as an XmlElement. Sets TypeRef = TypeRefXmlElement if not already set; the caller is responsible for setting Branch.Name (which is the nodeName) if the branch is freshly constructed at the root level. For nested elements, use the InsertXmlElement family which sets Name automatically.
func (*XmlElement) Attributes ¶
func (e *XmlElement) Attributes() map[string]string
Attributes returns a snapshot of all live attribute (name, value) pairs. Returned map is a copy; mutate freely.
func (*XmlElement) Branch ¶
func (e *XmlElement) Branch() *block.Branch
Branch returns the underlying *block.Branch.
func (*XmlElement) Delete ¶
func (e *XmlElement) Delete(txn *doc.TransactionMut, idx, length uint64)
Delete removes length children starting at idx.
func (*XmlElement) Get ¶
func (e *XmlElement) Get(idx uint64) any
Get returns the child at idx, wrapped as *XmlElement or *XmlText.
func (*XmlElement) GetAttribute ¶
func (e *XmlElement) GetAttribute(name string) (string, bool)
GetAttribute returns the value of the named attribute. The second return reports whether the attribute is set.
func (*XmlElement) InsertXmlElement ¶
func (e *XmlElement) InsertXmlElement(txn *doc.TransactionMut, idx uint64, nodeName string) *XmlElement
InsertXmlElement inserts a new XmlElement child with nodeName at position idx. Returns the wrapper.
func (*XmlElement) InsertXmlText ¶
func (e *XmlElement) InsertXmlText(txn *doc.TransactionMut, idx uint64) *XmlText
InsertXmlText inserts a new XmlText child at position idx.
func (*XmlElement) Length ¶
func (e *XmlElement) Length() uint64
Length returns the number of live children.
func (*XmlElement) NodeName ¶
func (e *XmlElement) NodeName() string
NodeName returns the element's tag name (the JS Yjs nodeName).
func (*XmlElement) Range ¶
func (e *XmlElement) Range(fn func(idx uint64, child any) bool)
Range iterates over children. fn returns false to stop early.
func (*XmlElement) RemoveAttribute ¶
func (e *XmlElement) RemoveAttribute(txn *doc.TransactionMut, name string)
RemoveAttribute tombstones the named attribute. Calling on an unset attribute is a no-op.
func (*XmlElement) SetAttribute ¶
func (e *XmlElement) SetAttribute(txn *doc.TransactionMut, name, value string)
SetAttribute sets the named attribute to value, replacing any prior value. Reuses the Map.Set machinery — attributes are map-keyed entries on the same Branch.
func (*XmlElement) ToString ¶
func (e *XmlElement) ToString() string
ToString renders the element as an XML-like string:
<nodeName attr1="value1" attr2="value2">children…</nodeName>
or, for empty elements:
<nodeName attr1="value1"/>
Attribute keys are sorted ascending for deterministic output. Attribute values are inlined without escaping — tests should avoid attribute values containing quotes; a real HTML encoder would XML-escape them.
type XmlFragment ¶
type XmlFragment struct {
// contains filtered or unexported fields
}
XmlFragment is a root container of XmlElement and XmlText children. Mirrors Y.XmlFragment — the top-level entry for collaborative XML/DOM documents (ProseMirror, Tiptap, BlockNote all use this shape). Children are accessed positionally.
Per docs/yrs-port-notes/nested-types.md §1, the underlying Branch carries TypeRef = TypeRefXmlFragment; NewXmlFragment sets it on wrap so cross-client receivers that wrap the same root by name end up with consistent state.
func NewXmlFragment ¶
func NewXmlFragment(branch *block.Branch) *XmlFragment
NewXmlFragment wraps the given branch as an XmlFragment. Sets Branch.TypeRef = TypeRefXmlFragment.
func (*XmlFragment) Branch ¶
func (f *XmlFragment) Branch() *block.Branch
Branch returns the underlying *block.Branch.
func (*XmlFragment) Delete ¶
func (f *XmlFragment) Delete(txn *doc.TransactionMut, idx, length uint64)
Delete removes length children starting at idx.
func (*XmlFragment) Get ¶
func (f *XmlFragment) Get(idx uint64) any
Get returns the child at idx as *XmlElement, *XmlText, or nil if idx is out of range or the underlying item is of an unsupported kind.
func (*XmlFragment) InsertXmlElement ¶
func (f *XmlFragment) InsertXmlElement(txn *doc.TransactionMut, idx uint64, nodeName string) *XmlElement
InsertXmlElement inserts a new XmlElement child with the given nodeName at position idx. Returns the wrapper bound to the new child branch.
func (*XmlFragment) InsertXmlText ¶
func (f *XmlFragment) InsertXmlText(txn *doc.TransactionMut, idx uint64) *XmlText
InsertXmlText inserts a new XmlText child at position idx. Use the returned wrapper to populate the text content.
func (*XmlFragment) Length ¶
func (f *XmlFragment) Length() uint64
Length returns the number of live children.
func (*XmlFragment) Range ¶
func (f *XmlFragment) Range(fn func(idx uint64, child any) bool)
Range iterates over children in document order. fn receives the index and the wrapped child; return false to stop early.
func (*XmlFragment) ToString ¶
func (f *XmlFragment) ToString() string
ToString renders the fragment as an XML-like string by concatenating child renderings. Attribute keys are sorted ascending for deterministic output (tests and snapshots compare stable strings).
type XmlText ¶
type XmlText struct {
Text
}
XmlText is rich-text content within an XML tree. Embeds the regular Text wrapper, so every Insert / InsertWithAttributes / Format / InsertEmbed / Range / ToDelta method is available directly. The Branch's TypeRef is set to TypeRefXmlText so wire round-trips identify the child as text rather than a generic Y.Text.
func NewXmlText ¶
NewXmlText wraps the given branch as an XmlText, setting TypeRef = TypeRefXmlText.