Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
658c520b93 | ||
|
|
2576d4efca | ||
|
|
58b754950e | ||
|
|
ea7ad07f34 | ||
|
|
1c999b250e | ||
|
|
e9189365ee | ||
|
|
6b7b3136e0 | ||
|
|
da052bdb0a |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.5.45",
|
||||
"version": "13.5.47",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "yjs",
|
||||
"version": "13.5.45",
|
||||
"version": "13.5.47",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.49"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.5.45",
|
||||
"version": "13.5.47",
|
||||
"description": "Shared Editing Library",
|
||||
"main": "./dist/yjs.cjs",
|
||||
"module": "./dist/yjs.mjs",
|
||||
|
||||
@@ -382,12 +382,17 @@ const cleanupFormattingGap = (transaction, start, curr, startAttributes, currAtt
|
||||
switch (content.constructor) {
|
||||
case ContentFormat: {
|
||||
const { key, value } = /** @type {ContentFormat} */ (content)
|
||||
if ((endAttributes.get(key) || null) !== value || (startAttributes.get(key) || null) === value) {
|
||||
const startAttrValue = startAttributes.get(key) || null
|
||||
if ((endAttributes.get(key) || null) !== value || startAttrValue === value) {
|
||||
// Either this format is overwritten or it is not necessary because the attribute already existed.
|
||||
start.delete(transaction)
|
||||
cleanups++
|
||||
if (!reachedEndOfCurr && (currAttributes.get(key) || null) === value && (startAttributes.get(key) || null) !== value) {
|
||||
currAttributes.delete(key)
|
||||
if (startAttrValue === null) {
|
||||
currAttributes.delete(key)
|
||||
} else {
|
||||
currAttributes.set(key, startAttrValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
} from '../internals.js'
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
import * as array from 'lib0/array'
|
||||
|
||||
/**
|
||||
* Define the elements to which a set of CSS queries apply.
|
||||
@@ -237,7 +238,7 @@ export class YXmlFragment extends AbstractType {
|
||||
querySelectorAll (query) {
|
||||
query = query.toUpperCase()
|
||||
// @ts-ignore
|
||||
return Array.from(new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query))
|
||||
return array.from(new YXmlTreeWalker(this, element => element.nodeName && element.nodeName.toUpperCase() === query))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -219,17 +219,21 @@ export const createDeleteSetFromStructStore = ss => {
|
||||
*/
|
||||
export const writeDeleteSet = (encoder, ds) => {
|
||||
encoding.writeVarUint(encoder.restEncoder, ds.clients.size)
|
||||
ds.clients.forEach((dsitems, client) => {
|
||||
encoder.resetDsCurVal()
|
||||
encoding.writeVarUint(encoder.restEncoder, client)
|
||||
const len = dsitems.length
|
||||
encoding.writeVarUint(encoder.restEncoder, len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const item = dsitems[i]
|
||||
encoder.writeDsClock(item.clock)
|
||||
encoder.writeDsLen(item.len)
|
||||
}
|
||||
})
|
||||
|
||||
// Ensure that the delete set is written in a deterministic order
|
||||
array.from(ds.clients.entries())
|
||||
.sort((a, b) => b[0] - a[0])
|
||||
.forEach(([client, dsitems]) => {
|
||||
encoder.resetDsCurVal()
|
||||
encoding.writeVarUint(encoder.restEncoder, client)
|
||||
const len = dsitems.length
|
||||
encoding.writeVarUint(encoder.restEncoder, len)
|
||||
for (let i = 0; i < len; i++) {
|
||||
const item = dsitems[i]
|
||||
encoder.writeDsClock(item.clock)
|
||||
encoder.writeDsLen(item.len)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,7 +147,7 @@ export class Doc extends Observable {
|
||||
}
|
||||
|
||||
getSubdocGuids () {
|
||||
return new Set(Array.from(this.subdocs).map(doc => doc.guid))
|
||||
return new Set(array.from(this.subdocs).map(doc => doc.guid))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,6 +45,7 @@ import * as decoding from 'lib0/decoding'
|
||||
import * as binary from 'lib0/binary'
|
||||
import * as map from 'lib0/map'
|
||||
import * as math from 'lib0/math'
|
||||
import * as array from 'lib0/array'
|
||||
|
||||
/**
|
||||
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
||||
@@ -96,7 +97,7 @@ export const writeClientsStructs = (encoder, store, _sm) => {
|
||||
encoding.writeVarUint(encoder.restEncoder, sm.size)
|
||||
// Write items with higher client ids first
|
||||
// This heavily improves the conflict algorithm.
|
||||
Array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||
array.from(sm.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||
// @ts-ignore
|
||||
writeStructs(encoder, store.clients.get(client), client, clock)
|
||||
})
|
||||
@@ -231,7 +232,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => {
|
||||
*/
|
||||
const stack = []
|
||||
// sort them so that we take the higher id first, in case of conflicts the lower id will probably not conflict with the id from the higher user.
|
||||
let clientsStructRefsIds = Array.from(clientsStructRefs.keys()).sort((a, b) => a - b)
|
||||
let clientsStructRefsIds = array.from(clientsStructRefs.keys()).sort((a, b) => a - b)
|
||||
if (clientsStructRefsIds.length === 0) {
|
||||
return null
|
||||
}
|
||||
@@ -601,7 +602,7 @@ export const decodeStateVector = decodedState => readStateVector(new DSDecoderV1
|
||||
*/
|
||||
export const writeStateVector = (encoder, sv) => {
|
||||
encoding.writeVarUint(encoder.restEncoder, sv.size)
|
||||
Array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||
array.from(sv.entries()).sort((a, b) => b[0] - a[0]).forEach(([client, clock]) => {
|
||||
encoding.writeVarUint(encoder.restEncoder, client) // @todo use a special client decoder that is based on mapping
|
||||
encoding.writeVarUint(encoder.restEncoder, clock)
|
||||
})
|
||||
|
||||
@@ -5,6 +5,413 @@ import * as math from 'lib0/math'
|
||||
|
||||
const { init, compare } = Y
|
||||
|
||||
/**
|
||||
* https://github.com/yjs/yjs/issues/474
|
||||
* @todo Remove debug: 127.0.0.1:8080/test.html?filter=\[88/
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testDeltaBug = _tc => {
|
||||
const initialDelta = [{
|
||||
attributes: {
|
||||
'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'table-col': {
|
||||
width: '150'
|
||||
}
|
||||
},
|
||||
insert: '\n\n\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-apba4k'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-apba4k',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-a8qf0r'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-a8qf0r',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-oi9ikb'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-oi9ikb',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-dt6ks2'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-dt6ks2',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-qah2ay'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-qah2ay',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-468a69b5-9332-450b-9107-381d593de249',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-fpcz5a'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-fpcz5a',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-zrhylp'
|
||||
},
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-zrhylp',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-s1q9nt'
|
||||
},
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-s1q9nt',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-20b0j9'
|
||||
},
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-20b0j9',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
insert: 'Content after table'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce'
|
||||
},
|
||||
insert: '\n'
|
||||
}
|
||||
]
|
||||
const ydoc1 = new Y.Doc()
|
||||
const ytext = ydoc1.getText()
|
||||
ytext.applyDelta(initialDelta)
|
||||
const addingDash = [
|
||||
{
|
||||
retain: 12
|
||||
},
|
||||
{
|
||||
insert: '-'
|
||||
}
|
||||
]
|
||||
ytext.applyDelta(addingDash)
|
||||
const addingSpace = [
|
||||
{
|
||||
retain: 13
|
||||
},
|
||||
{
|
||||
insert: ' '
|
||||
}
|
||||
]
|
||||
ytext.applyDelta(addingSpace)
|
||||
const addingList = [
|
||||
{
|
||||
retain: 12
|
||||
},
|
||||
{
|
||||
delete: 2
|
||||
},
|
||||
{
|
||||
retain: 1,
|
||||
attributes: {
|
||||
// Clear table line attribute
|
||||
'table-cell-line': null,
|
||||
// Add list attribute in place of table-cell-line
|
||||
list: {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-20b0j9',
|
||||
list: 'bullet'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
ytext.applyDelta(addingList)
|
||||
const result = ytext.toDelta()
|
||||
const expectedResult = [
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'table-col': {
|
||||
width: '150'
|
||||
}
|
||||
},
|
||||
insert: '\n\n\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-9144be72-e528-4f91-b0b2-82d20408e9ea',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-apba4k'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-apba4k',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-639adacb-1516-43ed-b272-937c55669a1c',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-a8qf0r'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-a8qf0r',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-6302ca4a-73a3-4c25-8c1e-b542f048f1c6',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-oi9ikb'
|
||||
},
|
||||
row: 'row-6kv2ls',
|
||||
cell: 'cell-oi9ikb',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-ceeddd05-330e-4f86-8017-4a3a060c4627',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-dt6ks2'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-dt6ks2',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-37b19322-cb57-4e6f-8fad-0d1401cae53f',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-qah2ay'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-qah2ay',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-468a69b5-9332-450b-9107-381d593de249',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-fpcz5a'
|
||||
},
|
||||
row: 'row-d1sv2g',
|
||||
cell: 'cell-fpcz5a',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-26b1d252-9b2e-4808-9b29-04e76696aa3c',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-zrhylp'
|
||||
},
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-zrhylp',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-6af97ba7-8cf9-497a-9365-7075b938837b',
|
||||
'table-cell-line': {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-s1q9nt'
|
||||
},
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-s1q9nt',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
insert: '\n',
|
||||
// This attibutes has only list and no table-cell-line
|
||||
attributes: {
|
||||
list: {
|
||||
rowspan: '1',
|
||||
colspan: '1',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-20b0j9',
|
||||
list: 'bullet'
|
||||
},
|
||||
'block-id': 'block-107e273e-86bc-44fd-b0d7-41ab55aca484',
|
||||
row: 'row-pflz90',
|
||||
cell: 'cell-20b0j9',
|
||||
rowspan: '1',
|
||||
colspan: '1'
|
||||
}
|
||||
},
|
||||
// No table-cell-line below here
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-38161f9c-6f6d-44c5-b086-54cc6490f1e3'
|
||||
},
|
||||
insert: '\n'
|
||||
},
|
||||
{
|
||||
insert: 'Content after table'
|
||||
},
|
||||
{
|
||||
attributes: {
|
||||
'block-id': 'block-15630542-ef45-412d-9415-88f0052238ce'
|
||||
},
|
||||
insert: '\n'
|
||||
}
|
||||
]
|
||||
t.compare(result, expectedResult)
|
||||
}
|
||||
|
||||
/**
|
||||
* In this test we are mainly interested in the cleanup behavior and whether the resulting delta makes sense.
|
||||
* It is fine if the resulting delta is not minimal. But applying the delta to a rich-text editor should result in a
|
||||
|
||||
Reference in New Issue
Block a user