awareness

package
v0.29.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: BSD-3-Clause Imports: 4 Imported by: 0

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

View Source
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

type Cursor struct {
	Anchor int
	Head   int
}

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 New

func New() *Registry

New returns an empty registry.

func (*Registry) Apply

func (r *Registry) Apply(u Update) bool

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

func (r *Registry) Leave(site crdt.SiteID) Update

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

func (r *Registry) Peers() []Peer

Peers returns the participants still present, ordered by site so the result is stable across calls and across replicas.

func (*Registry) Publish

func (r *Registry) Publish(site crdt.SiteID, cursor Cursor, meta map[string]string) Update

Publish records this replica's own state and returns the update to broadcast. Each call advances the site's counter, so a later publication always wins over an earlier one.

func (*Registry) State

func (r *Registry) State() []Update

State returns the updates that reproduce this registry's view, which is what a joining peer needs in order to see everyone already present.

type Update

type Update struct {
	Site   crdt.SiteID
	Clock  uint64
	Gone   bool
	Cursor Cursor
	Meta   map[string]string
}

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

func (u Update) MarshalBinary() ([]byte, error)

MarshalBinary encodes the update. Metadata keys are written in sorted order, so the same state always produces the same bytes.

func (*Update) UnmarshalBinary

func (u *Update) UnmarshalBinary(data []byte) error

UnmarshalBinary decodes an update written by MarshalBinary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL