simplify exposed APi
This commit is contained in:
parent
4582832a71
commit
07a6a0044b
@ -1,7 +1,7 @@
|
|||||||
# 
|
# 
|
||||||
> The shared editing library
|
> The shared editing library
|
||||||
|
|
||||||
Yjs is a library for automatic conflict resolution on shared state. It implements an operation-based CRDT and exposes its internal CRDT model as shared types. Shared types are common data types like `Map` or `Array` with superpowers! - changes are automatically distributed to other peers and merged without merge conflicts.
|
Yjs is a library for automatic conflict resolution on shared state. It implements an operation-based CRDT and exposes its internal CRDT model as shared types. Shared types are common data types like `Map` or `Array` with superpowers - changes are automatically distributed to other peers and merged without merge conflicts.
|
||||||
|
|
||||||
Yjs is **network agnostic** (p2p!), supports many existing **rich text editors**, **offline editing**, **version snapshots**, **shared cursors**, and encodes update messages using **binary protocol encoding**.
|
Yjs is **network agnostic** (p2p!), supports many existing **rich text editors**, **offline editing**, **version snapshots**, **shared cursors**, and encodes update messages using **binary protocol encoding**.
|
||||||
|
|
||||||
|
20
src/index.js
20
src/index.js
@ -9,28 +9,18 @@ export {
|
|||||||
YXmlHook as XmlHook,
|
YXmlHook as XmlHook,
|
||||||
YXmlElement as XmlElement,
|
YXmlElement as XmlElement,
|
||||||
YXmlFragment as XmlFragment,
|
YXmlFragment as XmlFragment,
|
||||||
createRelativePosition,
|
createCursorFromTypeOffset,
|
||||||
createRelativePositionByOffset,
|
createCursorFromJSON,
|
||||||
createAbsolutePosition,
|
createAbsolutePositionFromCursor,
|
||||||
compareRelativePositions,
|
writeCursor,
|
||||||
writeRelativePosition,
|
readCursor,
|
||||||
readRelativePosition,
|
|
||||||
createRelativePositionFromJSON,
|
|
||||||
toAbsolutePosition,
|
|
||||||
AbsolutePosition,
|
|
||||||
RelativePosition,
|
|
||||||
ID,
|
ID,
|
||||||
createID,
|
createID,
|
||||||
compareIDs,
|
compareIDs,
|
||||||
writeStructsFromTransaction,
|
|
||||||
readStructs,
|
|
||||||
getState,
|
getState,
|
||||||
getStates,
|
getStates,
|
||||||
readStatesAsMap,
|
readStatesAsMap,
|
||||||
writeStates,
|
writeStates,
|
||||||
readDeleteSet,
|
|
||||||
writeDeleteSet,
|
|
||||||
createDeleteSetFromStructStore,
|
|
||||||
writeModel,
|
writeModel,
|
||||||
readModel
|
readModel
|
||||||
} from './internals.js'
|
} from './internals.js'
|
||||||
|
@ -2,7 +2,7 @@ export * from './utils/DeleteSet.js'
|
|||||||
export * from './utils/EventHandler.js'
|
export * from './utils/EventHandler.js'
|
||||||
export * from './utils/ID.js'
|
export * from './utils/ID.js'
|
||||||
export * from './utils/isParentOf.js'
|
export * from './utils/isParentOf.js'
|
||||||
export * from './utils/relativePosition.js'
|
export * from './utils/cursor.js'
|
||||||
export * from './utils/Snapshot.js'
|
export * from './utils/Snapshot.js'
|
||||||
export * from './utils/StructStore.js'
|
export * from './utils/StructStore.js'
|
||||||
export * from './utils/Transaction.js'
|
export * from './utils/Transaction.js'
|
||||||
|
@ -263,16 +263,6 @@ export const replaceStruct = (store, struct, newStruct) => {
|
|||||||
structs[findIndexSS(structs, struct.id.clock)] = newStruct
|
structs[findIndexSS(structs, struct.id.clock)] = newStruct
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {StructStore} store
|
|
||||||
* @param {ID} id
|
|
||||||
* @return {boolean}
|
|
||||||
*
|
|
||||||
* @private
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
export const exists = (store, id) => id.clock < getState(store, id.client)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read StateMap from Decoder and return as Map
|
* Read StateMap from Decoder and return as Map
|
||||||
*
|
*
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
/**
|
/**
|
||||||
* @module RelativePosition
|
* @module Cursors
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
find,
|
getItem,
|
||||||
exists,
|
|
||||||
getItemType,
|
getItemType,
|
||||||
createID,
|
createID,
|
||||||
writeID,
|
writeID,
|
||||||
readID,
|
readID,
|
||||||
compareIDs,
|
compareIDs,
|
||||||
|
getState,
|
||||||
findRootTypeKey,
|
findRootTypeKey,
|
||||||
AbstractItem,
|
AbstractItem,
|
||||||
ID, StructStore, Y, AbstractType // eslint-disable-line
|
ID, StructStore, Y, AbstractType // eslint-disable-line
|
||||||
@ -20,31 +20,29 @@ import * as decoding from 'lib0/decoding.js'
|
|||||||
import * as error from 'lib0/error.js'
|
import * as error from 'lib0/error.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A relative position that is based on the Yjs model. In contrast to an
|
* A Cursor is a relative position that is based on the Yjs model. In contrast to an
|
||||||
* absolute position (position by index), the relative position can be
|
* absolute position (position by index), the Cursor can be
|
||||||
* recomputed when remote changes are received. For example:
|
* recomputed when remote changes are received. For example:
|
||||||
*
|
*
|
||||||
* ```Insert(0, 'x')('a|bc') = 'xa|bc'``` Where | is the cursor position.
|
* ```Insert(0, 'x')('a|bc') = 'xa|bc'``` Where | is the cursor position.
|
||||||
*
|
*
|
||||||
* A relative cursor position can be obtained with the function
|
* A relative cursor position can be obtained with the function
|
||||||
* {@link getRelativePosition} and it can be transformed to an absolute position
|
|
||||||
* with {@link fromRelativePosition}.
|
|
||||||
*
|
*
|
||||||
* One of the properties must be defined.
|
* One of the properties must be defined.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* // Current cursor position is at position 10
|
* // Current cursor position is at position 10
|
||||||
* let relativePosition = getRelativePosition(yText, 10)
|
* const relativePosition = createCursorFromOffset(yText, 10)
|
||||||
* // modify yText
|
* // modify yText
|
||||||
* yText.insert(0, 'abc')
|
* yText.insert(0, 'abc')
|
||||||
* yText.delete(3, 10)
|
* yText.delete(3, 10)
|
||||||
* // Compute the cursor position
|
* // Compute the cursor position
|
||||||
* let absolutePosition = fromRelativePosition(y, relativePosition)
|
* const absolutePosition = toAbsolutePosition(y, relativePosition)
|
||||||
* absolutePosition.type // => yText
|
* absolutePosition.type === yText // => true
|
||||||
* console.log('cursor location is ' + absolutePosition.offset) // => cursor location is 3
|
* console.log('cursor location is ' + absolutePosition.offset) // => cursor location is 3
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export class RelativePosition {
|
export class Cursor {
|
||||||
/**
|
/**
|
||||||
* @param {ID|null} type
|
* @param {ID|null} type
|
||||||
* @param {string|null} tname
|
* @param {string|null} tname
|
||||||
@ -81,11 +79,11 @@ export class RelativePosition {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Object} json
|
* @param {Object} json
|
||||||
* @return {RelativePosition}
|
* @return {Cursor}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const createRelativePositionFromJSON = json => new RelativePosition(json.type == null ? null : createID(json.type.client, json.type.clock), json.tname || null, json.item == null ? null : createID(json.item.client, json.item.clock))
|
export const createCursorFromJSON = json => new Cursor(json.type == null ? null : createID(json.type.client, json.type.clock), json.tname || null, json.item == null ? null : createID(json.item.client, json.item.clock))
|
||||||
|
|
||||||
export class AbsolutePosition {
|
export class AbsolutePosition {
|
||||||
/**
|
/**
|
||||||
@ -118,7 +116,7 @@ export const createAbsolutePosition = (type, offset) => new AbsolutePosition(typ
|
|||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const createRelativePosition = (type, item) => {
|
export const createCursor = (type, item) => {
|
||||||
let typeid = null
|
let typeid = null
|
||||||
let tname = null
|
let tname = null
|
||||||
if (type._item === null) {
|
if (type._item === null) {
|
||||||
@ -126,7 +124,7 @@ export const createRelativePosition = (type, item) => {
|
|||||||
} else {
|
} else {
|
||||||
typeid = type._item.id
|
typeid = type._item.id
|
||||||
}
|
}
|
||||||
return new RelativePosition(typeid, tname, item)
|
return new Cursor(typeid, tname, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,32 +132,32 @@ export const createRelativePosition = (type, item) => {
|
|||||||
*
|
*
|
||||||
* @param {AbstractType<any>} type The base type (e.g. YText or YArray).
|
* @param {AbstractType<any>} type The base type (e.g. YText or YArray).
|
||||||
* @param {number} offset The absolute position.
|
* @param {number} offset The absolute position.
|
||||||
* @return {RelativePosition}
|
* @return {Cursor}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const createRelativePositionByOffset = (type, offset) => {
|
export const createCursorFromTypeOffset = (type, offset) => {
|
||||||
let t = type._start
|
let t = type._start
|
||||||
while (t !== null) {
|
while (t !== null) {
|
||||||
if (!t.deleted && t.countable) {
|
if (!t.deleted && t.countable) {
|
||||||
if (t.length > offset) {
|
if (t.length > offset) {
|
||||||
// case 1: found position somewhere in the linked list
|
// case 1: found position somewhere in the linked list
|
||||||
return createRelativePosition(type, createID(t.id.client, t.id.clock + offset))
|
return createCursor(type, createID(t.id.client, t.id.clock + offset))
|
||||||
}
|
}
|
||||||
offset -= t.length
|
offset -= t.length
|
||||||
}
|
}
|
||||||
t = t.right
|
t = t.right
|
||||||
}
|
}
|
||||||
return createRelativePosition(type, null)
|
return createCursor(type, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {encoding.Encoder} encoder
|
||||||
* @param {RelativePosition} rpos
|
* @param {Cursor} rpos
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const writeRelativePosition = (encoder, rpos) => {
|
export const writeCursor = (encoder, rpos) => {
|
||||||
const { type, tname, item } = rpos
|
const { type, tname, item } = rpos
|
||||||
if (item !== null) {
|
if (item !== null) {
|
||||||
encoding.writeVarUint(encoder, 0)
|
encoding.writeVarUint(encoder, 0)
|
||||||
@ -182,11 +180,11 @@ export const writeRelativePosition = (encoder, rpos) => {
|
|||||||
* @param {decoding.Decoder} decoder
|
* @param {decoding.Decoder} decoder
|
||||||
* @param {Y} y
|
* @param {Y} y
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @return {RelativePosition|null}
|
* @return {Cursor|null}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readRelativePosition = (decoder, y, store) => {
|
export const readCursor = (decoder, y, store) => {
|
||||||
let type = null
|
let type = null
|
||||||
let tname = null
|
let tname = null
|
||||||
let itemID = null
|
let itemID = null
|
||||||
@ -204,28 +202,28 @@ export const readRelativePosition = (decoder, y, store) => {
|
|||||||
type = readID(decoder)
|
type = readID(decoder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new RelativePosition(type, tname, itemID)
|
return new Cursor(type, tname, itemID)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {RelativePosition} rpos
|
* @param {Cursor} cursor
|
||||||
* @param {Y} y
|
* @param {Y} y
|
||||||
* @return {AbsolutePosition|null}
|
* @return {AbsolutePosition|null}
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const toAbsolutePosition = (rpos, y) => {
|
export const createAbsolutePositionFromCursor = (cursor, y) => {
|
||||||
const store = y.store
|
const store = y.store
|
||||||
const rightID = rpos.item
|
const rightID = cursor.item
|
||||||
const typeID = rpos.type
|
const typeID = cursor.type
|
||||||
const tname = rpos.tname
|
const tname = cursor.tname
|
||||||
let type = null
|
let type = null
|
||||||
let offset = 0
|
let offset = 0
|
||||||
if (rightID !== null) {
|
if (rightID !== null) {
|
||||||
if (!exists(store, rightID)) {
|
if (getState(store, rightID.client) <= rightID.clock) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const right = find(store, rightID)
|
const right = getItem(store, rightID)
|
||||||
if (!(right instanceof AbstractItem)) {
|
if (!(right instanceof AbstractItem)) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -255,42 +253,12 @@ export const toAbsolutePosition = (rpos, y) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms an absolute to a relative position.
|
* @param {Cursor|null} a
|
||||||
*
|
* @param {Cursor|null} b
|
||||||
* @param {AbsolutePosition} apos The absolute position.
|
|
||||||
* @param {Y} y The Yjs instance in which to query for the absolute position.
|
|
||||||
* @return {RelativePosition} The absolute position in the Yjs model
|
|
||||||
* (type + offset).
|
|
||||||
*
|
*
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const toRelativePosition = (apos, y) => {
|
export const compareCursors = (a, b) => a === b || (
|
||||||
const type = apos.type
|
|
||||||
if (type._length === apos.offset) {
|
|
||||||
return createRelativePosition(type, null)
|
|
||||||
} else {
|
|
||||||
let offset = apos.offset
|
|
||||||
let n = type._start
|
|
||||||
while (n !== null) {
|
|
||||||
if (!n.deleted && n.countable) {
|
|
||||||
if (n.length > offset) {
|
|
||||||
return createRelativePosition(type, createID(n.id.client, n.id.clock + offset))
|
|
||||||
}
|
|
||||||
offset -= n.length
|
|
||||||
}
|
|
||||||
n = n.right
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw error.unexpectedCase()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {RelativePosition|null} a
|
|
||||||
* @param {RelativePosition|null} b
|
|
||||||
*
|
|
||||||
* @function
|
|
||||||
*/
|
|
||||||
export const compareRelativePositions = (a, b) => a === b || (
|
|
||||||
a !== null && b !== null && a.tname === b.tname && (
|
a !== null && b !== null && a.tname === b.tname && (
|
||||||
(a.item !== null && b.item !== null && compareIDs(a.item, b.item)) ||
|
(a.item !== null && b.item !== null && compareIDs(a.item, b.item)) ||
|
||||||
(a.type !== null && b.type !== null && compareIDs(a.type, b.type))
|
(a.type !== null && b.type !== null && compareIDs(a.type, b.type))
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
findIndexSS,
|
findIndexSS,
|
||||||
exists,
|
|
||||||
GCRef,
|
GCRef,
|
||||||
ItemBinaryRef,
|
ItemBinaryRef,
|
||||||
ItemDeletedRef,
|
ItemDeletedRef,
|
||||||
@ -204,7 +203,7 @@ const resumeStructIntegration = (transaction, store) => {
|
|||||||
}
|
}
|
||||||
while (m.length > 0) {
|
while (m.length > 0) {
|
||||||
const missing = m[m.length - 1]
|
const missing = m[m.length - 1]
|
||||||
if (!exists(store, missing)) {
|
if (getState(store, missing.client) <= missing.clock) {
|
||||||
const client = missing.client
|
const client = missing.client
|
||||||
// get the struct reader that has the missing struct
|
// get the struct reader that has the missing struct
|
||||||
const structRefs = clientsStructRefs.get(client)
|
const structRefs = clientsStructRefs.get(client)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user