simplify exposed APi

This commit is contained in:
Kevin Jahns 2019-04-12 20:04:07 +02:00
parent 4582832a71
commit 07a6a0044b
6 changed files with 46 additions and 99 deletions

View File

@ -1,7 +1,7 @@
# ![Yjs](https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png) # ![Yjs](https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png)
> 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**.

View File

@ -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'

View File

@ -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'

View File

@ -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
* *

View File

@ -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))

View File

@ -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)