Documentation
¶
Overview ¶
Package awareness tracks who else has a document open and where their cursor is — the coloured carets and name labels a collaborative editor shows.
Awareness is deliberately not part of the CRDT. It is ephemeral: it is never persisted, it is not part of the document's history, and a peer's state is simply dropped when the peer leaves. Merging it needs nothing stronger than last-writer-wins per peer, so it carries a counter of its own rather than borrowing the document's.
Offsets are rune positions in the document text as the publishing peer saw it. A concurrent edit can leave them briefly stale; a renderer should clamp them to the current length rather than trust them.
Why offsets here stay in runes ¶
crdt.Doc has a UTF-16 addressing surface because a browser's offsets have to reach the document exactly. A cursor does not, and giving it a second unit would make it worse rather than better.
Both ends have to agree what an offset means, and an Update has nowhere to say. Adding a unit to the encoding changes the wire format for every peer; not adding one leaves a browser peer publishing UTF-16 and a server peer reading runes, with no error anywhere and a caret drawn in the wrong place — which is the failure the document API was given this treatment to prevent, moved somewhere nothing can detect it.
What makes runes the safe choice rather than merely the incumbent one is that nothing here is authoritative. A cursor is advisory, it is stale before it is drawn, it is clamped rather than trusted, and the next keystroke replaces it. An offset a character out draws a caret a character out for as long as it takes the next update to arrive; it can never edit anything and it is never stored. That is the whole of the damage, and it is why this is the one place in the library where rounding is the right answer.
A peer that counts in UTF-16 converts at its own edge, where it has to clamp in any case:
pos := min(max(peer.Cursor.Head, 0), doc.Len()) // it may describe a longer document head, err := doc.UTF16Offset(pos)
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrMalformed = errors.New("awareness: malformed encoding")
ErrMalformed reports bytes that are not a valid encoded update.
Functions ¶
This section is empty.
Types ¶
type Cursor ¶
A Cursor is a peer's selection. Anchor is where the selection started and Head is where it ends — Head may precede Anchor when selecting backwards, and the two are equal for a plain caret.
type Peer ¶
type Peer struct {
Site crdt.SiteID
Cursor Cursor
// Meta carries presentation details the editor chooses — typically a display
// name and a colour. The package neither interprets nor requires it.
Meta map[string]string
// contains filtered or unexported fields
}
A Peer is one participant's published state.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
A Registry is one replica's view of every participant, including itself. It is not safe for concurrent use.
The zero Registry is unusable — construct one with New.
func (*Registry) Apply ¶
Apply merges a peer's update and reports whether it changed anything. A stale or duplicate update changes nothing and returns false.
func (*Registry) Leave ¶
Leave marks a peer as gone and returns the update announcing it. The peer's counter is kept so that an update still in flight cannot resurrect it.
func (*Registry) Peers ¶
Peers returns the participants still present, ordered by site so the result is stable across calls and across replicas.
type Update ¶
An Update is one peer's state as it travels between replicas. Updates are idempotent and may arrive in any order: each carries the publishing peer's counter, and a lower counter than the receiver already holds is ignored.
func (Update) MarshalBinary ¶
MarshalBinary encodes the update. Metadata keys are written in sorted order, so the same state always produces the same bytes.
func (*Update) UnmarshalBinary ¶
UnmarshalBinary decodes an update written by MarshalBinary.