fix Y.Text formatting issue - closes #606
This commit is contained in:
parent
7a8ca6eaa5
commit
1cb52dc863
13
README.md
13
README.md
@ -34,8 +34,17 @@ on Yjs. [ - By contributing financially to the open-source Yjs project, you can receive professional support directly from the author. This includes the opportunity for weekly video calls to discuss your specific challenges.
|
* [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) -
|
||||||
* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in consulting and developing real-time collaborative editing solutions for visual apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and various data visualization types. Their expertise empowers developers to build engaging and interactive visual experiences leveraging the power of Yjs. See their work in action at [Visual Collaboration Showcase](https://yjs-diagram.synergy.codes/).
|
By contributing financially to the open-source Yjs project, you can receive
|
||||||
|
professional support directly from the author. This includes the opportunity for
|
||||||
|
weekly video calls to discuss your specific challenges.
|
||||||
|
* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in
|
||||||
|
consulting and developing real-time collaborative editing solutions for visual
|
||||||
|
apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and
|
||||||
|
various data visualization types. Their expertise empowers developers to build
|
||||||
|
engaging and interactive visual experiences leveraging the power of Yjs. See
|
||||||
|
their work in action at [Visual Collaboration
|
||||||
|
Showcase](https://yjs-diagram.synergy.codes/).
|
||||||
|
|
||||||
## Who is using Yjs
|
## Who is using Yjs
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
export * from './utils/AbstractConnector.js'
|
export * from './utils/AbstractConnector.js'
|
||||||
export * from './utils/DeleteSet.js'
|
export * from './utils/DeleteSet.js'
|
||||||
export * from './utils/Doc.js'
|
export * from './utils/Doc.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
UpdateEncoderV1, UpdateEncoderV2, ID, Transaction // eslint-disable-line
|
UpdateEncoderV1, UpdateEncoderV2, ID, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
addToDeleteSet,
|
addToDeleteSet,
|
||||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
Doc, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item // eslint-disable-line
|
Doc, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YText, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line
|
YText, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
readYArray,
|
readYArray,
|
||||||
readYMap,
|
readYMap,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractStruct,
|
AbstractStruct,
|
||||||
addStruct,
|
addStruct,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
GC,
|
GC,
|
||||||
getState,
|
getState,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractStruct,
|
AbstractStruct,
|
||||||
UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line
|
UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
removeEventHandlerListener,
|
removeEventHandlerListener,
|
||||||
callEventHandlerListeners,
|
callEventHandlerListeners,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @module YMap
|
* @module YMap
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @module YText
|
* @module YText
|
||||||
*/
|
*/
|
||||||
@ -118,14 +117,15 @@ const findNextPosition = (transaction, pos, count) => {
|
|||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {number} index
|
* @param {number} index
|
||||||
|
* @param {boolean} useSearchMarker
|
||||||
* @return {ItemTextListPosition}
|
* @return {ItemTextListPosition}
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
const findPosition = (transaction, parent, index) => {
|
const findPosition = (transaction, parent, index, useSearchMarker) => {
|
||||||
const currentAttributes = new Map()
|
const currentAttributes = new Map()
|
||||||
const marker = findMarker(parent, index)
|
const marker = useSearchMarker ? findMarker(parent, index) : null
|
||||||
if (marker) {
|
if (marker) {
|
||||||
const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes)
|
const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes)
|
||||||
return findNextPosition(transaction, pos, index - marker.index)
|
return findNextPosition(transaction, pos, index - marker.index)
|
||||||
@ -1120,7 +1120,7 @@ export class YText extends AbstractType {
|
|||||||
const y = this.doc
|
const y = this.doc
|
||||||
if (y !== null) {
|
if (y !== null) {
|
||||||
transact(y, transaction => {
|
transact(y, transaction => {
|
||||||
const pos = findPosition(transaction, this, index)
|
const pos = findPosition(transaction, this, index, !attributes)
|
||||||
if (!attributes) {
|
if (!attributes) {
|
||||||
attributes = {}
|
attributes = {}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -1138,20 +1138,20 @@ export class YText extends AbstractType {
|
|||||||
*
|
*
|
||||||
* @param {number} index The index to insert the embed at.
|
* @param {number} index The index to insert the embed at.
|
||||||
* @param {Object | AbstractType<any>} embed The Object that represents the embed.
|
* @param {Object | AbstractType<any>} embed The Object that represents the embed.
|
||||||
* @param {TextAttributes} attributes Attribute information to apply on the
|
* @param {TextAttributes} [attributes] Attribute information to apply on the
|
||||||
* embed
|
* embed
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
insertEmbed (index, embed, attributes = {}) {
|
insertEmbed (index, embed, attributes) {
|
||||||
const y = this.doc
|
const y = this.doc
|
||||||
if (y !== null) {
|
if (y !== null) {
|
||||||
transact(y, transaction => {
|
transact(y, transaction => {
|
||||||
const pos = findPosition(transaction, this, index)
|
const pos = findPosition(transaction, this, index, !attributes)
|
||||||
insertText(transaction, this, pos, embed, attributes)
|
insertText(transaction, this, pos, embed, attributes || {})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes))
|
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes || {}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1170,7 +1170,7 @@ export class YText extends AbstractType {
|
|||||||
const y = this.doc
|
const y = this.doc
|
||||||
if (y !== null) {
|
if (y !== null) {
|
||||||
transact(y, transaction => {
|
transact(y, transaction => {
|
||||||
deleteText(transaction, findPosition(transaction, this, index), length)
|
deleteText(transaction, findPosition(transaction, this, index, true), length)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
||||||
@ -1194,7 +1194,7 @@ export class YText extends AbstractType {
|
|||||||
const y = this.doc
|
const y = this.doc
|
||||||
if (y !== null) {
|
if (y !== null) {
|
||||||
transact(y, transaction => {
|
transact(y, transaction => {
|
||||||
const pos = findPosition(transaction, this, index)
|
const pos = findPosition(transaction, this, index, false)
|
||||||
if (pos.right === null) {
|
if (pos.right === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YEvent,
|
YEvent,
|
||||||
YXmlText, YXmlElement, YXmlFragment, Transaction // eslint-disable-line
|
YXmlText, YXmlElement, YXmlFragment, Transaction // eslint-disable-line
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YMap,
|
YMap,
|
||||||
YXmlHookRefID,
|
YXmlHookRefID,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YText,
|
YText,
|
||||||
YXmlTextRefID,
|
YXmlTextRefID,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { Observable } from 'lib0/observable'
|
import { Observable } from 'lib0/observable'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
findIndexSS,
|
findIndexSS,
|
||||||
getState,
|
getState,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { AbstractType } from '../internals.js' // eslint-disable-line
|
import { AbstractType } from '../internals.js' // eslint-disable-line
|
||||||
|
|
||||||
import * as decoding from 'lib0/decoding'
|
import * as decoding from 'lib0/decoding'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
YArray,
|
YArray,
|
||||||
YMap,
|
YMap,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
writeID,
|
writeID,
|
||||||
readID,
|
readID,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
isDeleted,
|
isDeleted,
|
||||||
createDeleteSetFromStructStore,
|
createDeleteSetFromStructStore,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
GC,
|
GC,
|
||||||
splitItem,
|
splitItem,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
getState,
|
getState,
|
||||||
writeStructsFromTransaction,
|
writeStructsFromTransaction,
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import * as error from 'lib0/error'
|
import * as error from 'lib0/error'
|
||||||
import * as encoding from 'lib0/encoding'
|
import * as encoding from 'lib0/encoding'
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
isDeleted,
|
isDeleted,
|
||||||
Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line
|
Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @module encoding
|
* @module encoding
|
||||||
*/
|
*/
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import { AbstractType, Item } from '../internals.js' // eslint-disable-line
|
import { AbstractType, Item } from '../internals.js' // eslint-disable-line
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
AbstractType // eslint-disable-line
|
AbstractType // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import * as binary from 'lib0/binary'
|
import * as binary from 'lib0/binary'
|
||||||
import * as decoding from 'lib0/decoding'
|
import * as decoding from 'lib0/decoding'
|
||||||
import * as encoding from 'lib0/encoding'
|
import * as encoding from 'lib0/encoding'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Testing if encoding/decoding compatibility and integration compatiblity is given.
|
* Testing if encoding/decoding compatibility and integration compatiblity is given.
|
||||||
* We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches.
|
* We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches.
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
import * as prng from 'lib0/prng'
|
import * as prng from 'lib0/prng'
|
||||||
import * as encoding from 'lib0/encoding'
|
import * as encoding from 'lib0/encoding'
|
||||||
|
@ -3,6 +3,46 @@ import { init } from './testHelper.js' // eslint-disable-line
|
|||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
|
export const testInconsistentFormat = () => {
|
||||||
|
/**
|
||||||
|
* @param {Y.Doc} ydoc
|
||||||
|
*/
|
||||||
|
const testYjsMerge = ydoc => {
|
||||||
|
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
|
||||||
|
content.format(0, 6, { bold: null })
|
||||||
|
content.format(6, 4, { type: 'text' })
|
||||||
|
t.compare(content.toDelta(), [
|
||||||
|
{
|
||||||
|
attributes: { type: 'text' },
|
||||||
|
insert: 'Merge Test'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attributes: { type: 'text', italic: true },
|
||||||
|
insert: ' After'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
const initializeYDoc = () => {
|
||||||
|
const yDoc = new Y.Doc({ gc: false })
|
||||||
|
|
||||||
|
const content = /** @type {Y.XmlText} */ (yDoc.get('text', Y.XmlText))
|
||||||
|
content.insert(0, ' After', { type: 'text', italic: true })
|
||||||
|
content.insert(0, 'Test', { type: 'text' })
|
||||||
|
content.insert(0, 'Merge ', { type: 'text', bold: true })
|
||||||
|
return yDoc
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const yDoc = initializeYDoc()
|
||||||
|
testYjsMerge(yDoc)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const initialYDoc = initializeYDoc()
|
||||||
|
const yDoc = new Y.Doc({ gc: false })
|
||||||
|
Y.applyUpdate(yDoc, Y.encodeStateAsUpdate(initialYDoc))
|
||||||
|
testYjsMerge(yDoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user