mirror of
https://github.com/yjs/yjs.git
synced 2025-12-15 19:27:45 +01:00
fixed several v2 issues
This commit is contained in:
@@ -16,7 +16,12 @@ import {
|
||||
ContentEmbed,
|
||||
getItemCleanStart,
|
||||
noAttributionsManager,
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager, // eslint-disable-line
|
||||
transact,
|
||||
ItemTextListPosition,
|
||||
insertText,
|
||||
deleteText,
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttributionItems, AbstractAttributionManager,
|
||||
YXmlElement, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as delta from 'lib0/delta'
|
||||
@@ -178,7 +183,7 @@ export const findMarker = (yarray, index) => {
|
||||
// window.lengths.push(marker.index - pindex)
|
||||
// console.log('distance', marker.index - pindex, 'len', p && p.parent.length)
|
||||
// }
|
||||
if (marker !== null && math.abs(marker.index - pindex) < /** @type {YText|YArray<any>} */ (p.parent).length / maxSearchMarker) {
|
||||
if (marker !== null && math.abs(marker.index - pindex) < /** @type {any} */ (p.parent).length / maxSearchMarker) {
|
||||
// adjust existing marker
|
||||
overwriteMarker(marker, p, pindex)
|
||||
return marker
|
||||
@@ -307,6 +312,10 @@ export class AbstractType {
|
||||
* @type {null | Array<ArraySearchMarker>}
|
||||
*/
|
||||
this._searchMarker = null
|
||||
/**
|
||||
* @type {EventDelta?}
|
||||
*/
|
||||
this._prelim = null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -337,6 +346,10 @@ export class AbstractType {
|
||||
_integrate (y, item) {
|
||||
this.doc = y
|
||||
this._item = item
|
||||
if (this._prelim) {
|
||||
this.applyDelta(this._prelim)
|
||||
this._prelim = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,6 +450,8 @@ export class AbstractType {
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @template {boolean} [Deep=false]
|
||||
*
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @param {Object} [opts]
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [opts.itemsToRender]
|
||||
@@ -445,16 +460,19 @@ export class AbstractType {
|
||||
* @param {Set<string>?} [opts.renderAttrs] - set of attrs to render. if null, render all attributes
|
||||
* @param {boolean} [opts.renderChildren] - if true, retain rendered+attributed deletes only
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [opts.deletedItems] - used for computing prevItem in attributes
|
||||
* @return {EventDelta} The Delta representation of this type.
|
||||
* @param {Set<import('../utils/types.js').YType>|Map<import('../utils/types.js').YType,any>|null} [opts.modified] - set of types that should be rendered as modified children
|
||||
* @param {Deep} [opts.deep] - render child types as delta
|
||||
* @return {Deep extends true ? ToDeepEventDelta<EventDelta> : EventDelta} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager, { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null } = {}) {
|
||||
getContent (am = noAttributionsManager, opts = {}) {
|
||||
const { itemsToRender = null, retainInserts = false, retainDeletes = false, renderAttrs = null, renderChildren = true, deletedItems = null, modified = null, deep = false } = opts
|
||||
/**
|
||||
* @type {EventDelta}
|
||||
*/
|
||||
const d = /** @type {any} */ (delta.create())
|
||||
typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deletedItems, itemsToRender)
|
||||
const d = /** @type {any} */ (delta.create(this.nodeName || null))
|
||||
typeMapGetDelta(d, /** @type {any} */ (this), renderAttrs, am, deep, modified, deletedItems, itemsToRender)
|
||||
if (renderChildren) {
|
||||
/**
|
||||
* @type {delta.FormattingAttributes}
|
||||
@@ -545,15 +563,21 @@ export class AbstractType {
|
||||
usingCurrentAttributes = true
|
||||
if (c.deleted ? retainDeletes : retainInserts) {
|
||||
d.retain(c.content.getLength(), null, attribution ?? {})
|
||||
} else if (deep && c.content.constructor === ContentType) {
|
||||
d.insert([/** @type {any} */ (c.content).type.getContent(am, opts)], null, attribution)
|
||||
} else {
|
||||
d.insert(c.content.getContent(), null, attribution)
|
||||
}
|
||||
} else if (renderDelete) {
|
||||
d.delete(1)
|
||||
} else if (retainContent) {
|
||||
d.usedAttributes = changedAttributes
|
||||
usingChangedAttributes = true
|
||||
d.retain(1)
|
||||
if (c.content.constructor === ContentType && modified?.has(/** @type {ContentType} */ (c.content).type)) {
|
||||
d.modify(/** @type {any} */ (c.content).type.getContent(am, opts))
|
||||
} else {
|
||||
d.usedAttributes = changedAttributes
|
||||
usingChangedAttributes = true
|
||||
d.retain(1)
|
||||
}
|
||||
}
|
||||
break
|
||||
case ContentFormat: {
|
||||
@@ -663,23 +687,52 @@ export class AbstractType {
|
||||
* @return {ToDeepEventDelta<EventDelta>}
|
||||
*/
|
||||
getContentDeep (am = noAttributionsManager) {
|
||||
const d = this.getContent(am)
|
||||
d.children.forEach(op => {
|
||||
if (op instanceof delta.InsertOp) {
|
||||
op.insert = /** @type {any} */ (op.insert.map(ins =>
|
||||
ins instanceof AbstractType
|
||||
// @ts-ignore
|
||||
? ins.getContentDeep(am)
|
||||
: ins)
|
||||
)
|
||||
}
|
||||
})
|
||||
d.attrs.forEach((op) => {
|
||||
if (delta.$insertOp.check(op) && op.value instanceof AbstractType) {
|
||||
op.value = op.value.getContentDeep(am)
|
||||
}
|
||||
})
|
||||
return /** @type {any} */ (d.done())
|
||||
return /** @type {any} */ (this.getContent(am, { deep: true }))
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a {@link Delta} on this shared type.
|
||||
*
|
||||
* @param {delta.Delta<any,any,any,any,any>} d The changes to apply on this element.
|
||||
* @param {AbstractAttributionManager} am
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
applyDelta (d, am = noAttributionsManager) {
|
||||
if (this.doc == null)
|
||||
(this._prelim || (this._prelim = /** @type {any} */ (delta.create()))).apply(d)
|
||||
else {
|
||||
// @todo this was moved here from ytext. Make this more generic
|
||||
transact(this.doc, transaction => {
|
||||
const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am)
|
||||
for (const op of d.children) {
|
||||
if (delta.$textOp.check(op)) {
|
||||
insertText(transaction, this, currPos, op.insert, op.format || {})
|
||||
} else if (delta.$insertOp.check(op)) {
|
||||
for (let i = 0; i < op.insert.length; i++) {
|
||||
let ins = op.insert[i]
|
||||
if (delta.$deltaAny.check(ins)) {
|
||||
if (ins.name != null) {
|
||||
const t = new YXmlElement(ins.name)
|
||||
t.applyDelta(ins)
|
||||
ins = t
|
||||
} else {
|
||||
error.unexpectedCase()
|
||||
}
|
||||
}
|
||||
insertText(transaction, this, currPos, ins, op.format || {})
|
||||
}
|
||||
} else if (delta.$retainOp.check(op)) {
|
||||
currPos.formatText(transaction, this, op.retain, op.format || {})
|
||||
} else if (delta.$deleteOp.check(op)) {
|
||||
deleteText(transaction, currPos, op.delete)
|
||||
} else if (delta.$modifyOp.check(op)) {
|
||||
/** @type {ContentType} */ (currPos.right?.content).type.applyDelta(op.modify)
|
||||
currPos.formatText(transaction, this, 1, op.format || {})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,13 +1283,16 @@ export const typeMapGetAll = (parent) => {
|
||||
* @param {YType_} parent
|
||||
* @param {Set<string>?} attrsToRender
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @param {boolean} deep
|
||||
* @param {Set<import('../utils/types.js').YType>|Map<import('../utils/types.js').YType,any>|null} [modified] - set of types that should be rendered as modified children
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [deletedItems]
|
||||
* @param {import('../utils/IdSet.js').IdSet?} [itemsToRender]
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, itemsToRender) => {
|
||||
export const typeMapGetDelta = (d, parent, attrsToRender, am, deep, modified, deletedItems, itemsToRender) => {
|
||||
// @todo support modified ops!
|
||||
/**
|
||||
* @param {Item} item
|
||||
* @param {string} key
|
||||
@@ -1248,8 +1304,8 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, item
|
||||
const cs = []
|
||||
am.readContent(cs, item.id.client, item.id.clock, item.deleted, item.content, 1)
|
||||
const { deleted, attrs, content } = cs[cs.length - 1]
|
||||
const c = array.last(content.getContent())
|
||||
const attribution = createAttributionFromAttributionItems(attrs, deleted)
|
||||
let c = array.last(content.getContent())
|
||||
if (deleted) {
|
||||
if (itemsToRender == null || itemsToRender.hasId(item.lastId)) {
|
||||
d.unset(key, attribution, c)
|
||||
@@ -1262,6 +1318,9 @@ export const typeMapGetDelta = (d, parent, attrsToRender, am, deletedItems, item
|
||||
// nop
|
||||
}
|
||||
const prevValue = (prevContentItem !== item && itemsToRender?.hasId(prevContentItem.lastId)) ? array.last(prevContentItem.content.getContent()) : undefined
|
||||
if (deep && c instanceof AbstractType) {
|
||||
c = c.getContent(am)
|
||||
}
|
||||
d.set(key, c, attribution, prevValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ const insertAttributes = (transaction, parent, currPos, attributes) => {
|
||||
* @private
|
||||
* @function
|
||||
**/
|
||||
const insertText = (transaction, parent, currPos, text, attributes) => {
|
||||
export const insertText = (transaction, parent, currPos, text, attributes) => {
|
||||
currPos.currentAttributes.forEach((_val, key) => {
|
||||
if (attributes[key] === undefined) {
|
||||
attributes[key] = null
|
||||
@@ -546,7 +546,7 @@ export const cleanupYTextAfterTransaction = transaction => {
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
const deleteText = (transaction, currPos, length) => {
|
||||
export const deleteText = (transaction, currPos, length) => {
|
||||
const startLength = length
|
||||
const startAttrs = map.copy(currPos.currentAttributes)
|
||||
const start = currPos.right
|
||||
@@ -745,37 +745,6 @@ export class YText extends AbstractType {
|
||||
return this.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a {@link Delta} on this shared YText type.
|
||||
*
|
||||
* @param {delta.TextDelta<Embeds>} d The changes to apply on this element.
|
||||
* @param {AbstractAttributionManager} am
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
applyDelta (d, am = noAttributionsManager) {
|
||||
if (this.doc !== null) {
|
||||
transact(this.doc, transaction => {
|
||||
const currPos = new ItemTextListPosition(null, this._start, 0, new Map(), am)
|
||||
for (const op of d.children) {
|
||||
if (delta.$textOp.check(op)) {
|
||||
insertText(transaction, this, currPos, op.insert, op.format || {})
|
||||
} else if (delta.$insertOp.check(op)) {
|
||||
for (let i = 0; i < op.insert.length; i++) {
|
||||
insertText(transaction, this, currPos, op.insert[i], op.format || {})
|
||||
}
|
||||
} else if (delta.$retainOp.check(op)) {
|
||||
currPos.formatText(transaction, this, op.retain, op.format || {})
|
||||
} else if (delta.$deleteOp.check(op)) {
|
||||
deleteText(transaction, currPos, op.delete)
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.applyDelta(d))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert text at a given index.
|
||||
*
|
||||
|
||||
@@ -59,6 +59,7 @@ export class YXmlFragment extends AbstractType {
|
||||
constructor () {
|
||||
super()
|
||||
/**
|
||||
* @todo remove _prelimContent
|
||||
* @type {Array<any>|null}
|
||||
*/
|
||||
this._prelimContent = []
|
||||
|
||||
@@ -41,14 +41,14 @@ export class YEvent {
|
||||
* @type {Transaction}
|
||||
*/
|
||||
this.transaction = transaction
|
||||
/**
|
||||
* @type {null | Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
|
||||
*/
|
||||
this._keys = null
|
||||
/**
|
||||
* @type {(Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any,any>)|null}
|
||||
*/
|
||||
this._delta = null
|
||||
/**
|
||||
* @type {(Target extends AbstractType<infer D,any> ? import('../internals.js').ToDeepEventDelta<D> : delta.Delta<any,any,any,any,any>)|null}
|
||||
*/
|
||||
this._deltaDeep = null
|
||||
/**
|
||||
* @type {Array<string|number>|null}
|
||||
*/
|
||||
@@ -102,63 +102,6 @@ export class YEvent {
|
||||
return this.transaction.deleteSet.hasId(struct.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Map<string, { action: 'add' | 'update' | 'delete', oldValue: any }>}
|
||||
*/
|
||||
get keys () {
|
||||
if (this._keys === null) {
|
||||
if (this.transaction.doc._transactionCleanups.length === 0) {
|
||||
throw error.create(errorComputeChanges)
|
||||
}
|
||||
const keys = new Map()
|
||||
const target = this.target
|
||||
// @ts-ignore
|
||||
const changed = /** @type Set<string|null> */ (this.transaction.changed.get(target))
|
||||
changed.forEach(key => {
|
||||
if (key !== null) {
|
||||
const item = /** @type {Item} */ (target._map.get(key))
|
||||
/**
|
||||
* @type {'delete' | 'add' | 'update'}
|
||||
*/
|
||||
let action
|
||||
let oldValue
|
||||
if (this.adds(item)) {
|
||||
let prev = item.left
|
||||
while (prev !== null && this.adds(prev)) {
|
||||
prev = prev.left
|
||||
}
|
||||
if (this.deletes(item)) {
|
||||
if (prev !== null && this.deletes(prev)) {
|
||||
action = 'delete'
|
||||
oldValue = array.last(prev.content.getContent())
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (prev !== null && this.deletes(prev)) {
|
||||
action = 'update'
|
||||
oldValue = array.last(prev.content.getContent())
|
||||
} else {
|
||||
action = 'add'
|
||||
oldValue = undefined
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (this.deletes(item)) {
|
||||
action = 'delete'
|
||||
oldValue = array.last(/** @type {Item} */ item.content.getContent())
|
||||
} else {
|
||||
return // nop
|
||||
}
|
||||
}
|
||||
keys.set(key, { action, oldValue })
|
||||
}
|
||||
})
|
||||
this._keys = keys
|
||||
}
|
||||
return this._keys
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a struct is added by this event.
|
||||
*
|
||||
@@ -172,14 +115,18 @@ export class YEvent {
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {boolean} [Deep=false]
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any>} The Delta representation of this type.
|
||||
* @param {object} [opts]
|
||||
* @param {Deep} [opts.deep]
|
||||
* @return {Target extends AbstractType<infer D,any> ? (Deep extends true ? import('../internals.js').ToDeepEventDelta<D> : D) : delta.Delta<any,any,any,any>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getDelta (am = noAttributionsManager) {
|
||||
getDelta (am = noAttributionsManager, { deep } = {}) {
|
||||
const itemsToRender = mergeIdSets([diffIdSet(this.transaction.insertSet, this.transaction.deleteSet), diffIdSet(this.transaction.deleteSet, this.transaction.insertSet)])
|
||||
return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: this.childListChanged, deletedItems: this.transaction.deleteSet }))
|
||||
const modified = deep ? this.transaction.changedParentTypes : null
|
||||
return /** @type {any} */ (this.target.getContent(am, { itemsToRender, retainDeletes: true, renderAttrs: this.keysChanged, renderChildren: deep || this.childListChanged, deletedItems: this.transaction.deleteSet, deep: !!deep, modified }))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -192,6 +139,17 @@ export class YEvent {
|
||||
get delta () {
|
||||
return /** @type {any} */ (this._delta ?? (this._delta = this.getDelta()))
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the changes in the delta format.
|
||||
* A {@link https://quilljs.com/docs/delta/|Quill Delta}) that represents the changes on the document.
|
||||
*
|
||||
* @type {Target extends AbstractType<infer D,any> ? D : delta.Delta<any,any,any,any,any>} The Delta representation of this type.
|
||||
* @public
|
||||
*/
|
||||
get deltaDeep () {
|
||||
return /** @type {any} */ (this._deltaDeep ?? (this._deltaDeep = this.getDelta(noAttributionsManager, { deep: true })))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user