sticky.go

  1package ygo
  2
  3/*
  4#include "libyrs.h"
  5*/
  6import "C"
  7import (
  8	"fmt"
  9	"unsafe"
 10)
 11
 12// StickyIndex represents a position in a document that survives changes.
 13// Unlike numeric positions, it tracks the logical location in the document.
 14type StickyIndex struct {
 15	ptr *C.YStickyIndex
 16}
 17
 18// Assoc determines whether position is before or after a character.
 19type Assoc int8
 20
 21const (
 22	// AssocAfter means the position points after the referenced character.
 23	AssocAfter Assoc = 0
 24	// AssocBefore means the position points before the referenced character.
 25	AssocBefore Assoc = -1
 26)
 27
 28// NewStickyIndexFromIndex creates a sticky index at a human-readable position.
 29func NewStickyIndexFromIndex(t interface{ Branch() unsafe.Pointer }, txn *Transaction, index uint32, assoc Assoc) (*StickyIndex, error) {
 30	if t == nil {
 31		return nil, fmt.Errorf("target is nil")
 32	}
 33	if txn == nil || txn.ptr == nil {
 34		return nil, ErrNilTransaction
 35	}
 36	branch := t.Branch()
 37	if branch == nil {
 38		return nil, ErrNilBranch
 39	}
 40
 41	ptr := C.ysticky_index_from_index(
 42		(*C.Branch)(branch),
 43		txn.ptr,
 44		C.uint32_t(index),
 45		C.int8_t(assoc))
 46
 47	if ptr == nil {
 48		return nil, fmt.Errorf("failed to create sticky index")
 49	}
 50
 51	si := &StickyIndex{ptr: ptr}
 52	return si, nil
 53}
 54
 55// Destroy releases resources.
 56func (si *StickyIndex) Destroy() {
 57	if si.ptr != nil {
 58		C.ysticky_index_destroy(si.ptr)
 59		si.ptr = nil
 60	}
 61}
 62
 63// Assoc returns the association of this index.
 64func (si *StickyIndex) Assoc() (Assoc, error) {
 65	if si.ptr == nil {
 66		return AssocAfter, ErrNilBranch
 67	}
 68	return Assoc(C.ysticky_index_assoc(si.ptr)), nil
 69}
 70
 71// Encode serializes the sticky index to binary.
 72func (si *StickyIndex) Encode() ([]byte, error) {
 73	if si.ptr == nil {
 74		return nil, fmt.Errorf("sticky index is nil")
 75	}
 76	var length C.uint32_t
 77	ptr := C.ysticky_index_encode(si.ptr, &length)
 78	if ptr == nil {
 79		return nil, fmt.Errorf("failed to encode sticky index")
 80	}
 81	defer C.ybinary_destroy(ptr, length)
 82
 83	data := make([]byte, int(length))
 84	copy(data, (*[1 << 30]byte)(unsafe.Pointer(ptr))[:length:length])
 85	return data, nil
 86}
 87
 88// DecodeStickyIndex deserializes from binary.
 89func DecodeStickyIndex(data []byte) (*StickyIndex, error) {
 90	if len(data) == 0 {
 91		return nil, fmt.Errorf("data is empty")
 92	}
 93	ptr := C.ysticky_index_decode(
 94		(*C.char)(unsafe.Pointer(&data[0])),
 95		C.uint32_t(len(data)))
 96	if ptr == nil {
 97		return nil, fmt.Errorf("failed to decode sticky index")
 98	}
 99
100	si := &StickyIndex{ptr: ptr}
101	return si, nil
102}
103
104// ToJSON serializes to JSON format (Yjs RelativePosition compatible).
105func (si *StickyIndex) ToJSON() (string, error) {
106	if si.ptr == nil {
107		return "", fmt.Errorf("sticky index is nil")
108	}
109	return cStringToGoAndFree(C.ysticky_index_to_json(si.ptr)), nil
110}
111
112// ParseStickyIndexJSON deserializes from JSON.
113func ParseStickyIndexJSON(jsonStr string) (*StickyIndex, error) {
114	cStr := C.CString(jsonStr)
115	defer C.free(unsafe.Pointer(cStr))
116
117	ptr := C.ysticky_index_from_json(cStr)
118	if ptr == nil {
119		return nil, fmt.Errorf("failed to parse sticky index from JSON")
120	}
121
122	si := &StickyIndex{ptr: ptr}
123	return si, nil
124}
125
126// Read resolves the sticky index to a current branch and numeric position.
127// Returns nil branch if the position is no longer valid.
128func (si *StickyIndex) Read(txn *Transaction) (unsafe.Pointer, uint32, error) {
129	if si.ptr == nil {
130		return nil, 0, fmt.Errorf("sticky index is nil")
131	}
132	if txn == nil || txn.ptr == nil {
133		return nil, 0, ErrNilTransaction
134	}
135
136	var branch *C.Branch
137	var index C.uint32_t
138
139	C.ysticky_index_read(si.ptr, txn.ptr, &branch, &index)
140
141	if branch == nil {
142		return nil, 0, fmt.Errorf("sticky index position is no longer valid")
143	}
144	return unsafe.Pointer(branch), uint32(index), nil
145}