Compare commits
11 Commits
v13.0.0-99
...
v13.0.0-10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4c919d9ec | ||
|
|
aeb23dbaa9 | ||
|
|
6d4f0c0cdd | ||
|
|
303138f309 | ||
|
|
ad373a3dce | ||
|
|
2150fa58f2 | ||
|
|
ece4841b5c | ||
|
|
8103220c05 | ||
|
|
66d500f08d | ||
|
|
5f8e7c7ba7 | ||
|
|
7b8eee6b25 |
@@ -55,7 +55,7 @@ are implemented in separate modules.
|
|||||||
| Name | Cursors | Binding | Demo |
|
| Name | Cursors | Binding | Demo |
|
||||||
|---|:-:|---|---|
|
|---|:-:|---|---|
|
||||||
| [ProseMirror](https://prosemirror.net/) | ✔ | [y-prosemirror](http://github.com/yjs/y-prosemirror) | [demo](https://yjs-demos.now.sh/prosemirror/) |
|
| [ProseMirror](https://prosemirror.net/) | ✔ | [y-prosemirror](http://github.com/yjs/y-prosemirror) | [demo](https://yjs-demos.now.sh/prosemirror/) |
|
||||||
| [Quill](https://quilljs.com/) | | [y-quill](http://github.com/yjs/y-quill) | [demo](https://yjs-demos.now.sh/quill/) |
|
| [Quill](https://quilljs.com/) | ✔ | [y-quill](http://github.com/yjs/y-quill) | [demo](https://yjs-demos.now.sh/quill/) |
|
||||||
| [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](http://github.com/yjs/y-codemirror) | [demo](https://yjs-demos.now.sh/codemirror/) |
|
| [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](http://github.com/yjs/y-codemirror) | [demo](https://yjs-demos.now.sh/codemirror/) |
|
||||||
| [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](http://github.com/yjs/y-monaco) | [demo](https://yjs-demos.now.sh/monaco/) |
|
| [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](http://github.com/yjs/y-monaco) | [demo](https://yjs-demos.now.sh/monaco/) |
|
||||||
| [Ace](https://ace.c9.io/) | | [y-ace](http://github.com/yjs/y-ace) | [demo](https://yjs-demos.now.sh/ace/) |
|
| [Ace](https://ace.c9.io/) | | [y-ace](http://github.com/yjs/y-ace) | [demo](https://yjs-demos.now.sh/ace/) |
|
||||||
@@ -235,7 +235,8 @@ or any of its children.
|
|||||||
Copies the <code>[key,value]</code> pairs of this YMap to a new Object.It
|
Copies the <code>[key,value]</code> pairs of this YMap to a new Object.It
|
||||||
transforms all child types to JSON using their <code>toJSON</code> method.
|
transforms all child types to JSON using their <code>toJSON</code> method.
|
||||||
</dd>
|
</dd>
|
||||||
<b><code>forEach(function(key:string,value:object|boolean|Array|string|number|Uint8Array|Y.Type))</code></b>
|
<b><code>forEach(function(value:object|boolean|Array|string|number|Uint8Array|Y.Type,
|
||||||
|
key:string, map: Y.Map))</code></b>
|
||||||
<dd>
|
<dd>
|
||||||
Execute the provided function once for every key-value pair.
|
Execute the provided function once for every key-value pair.
|
||||||
</dd>
|
</dd>
|
||||||
@@ -343,7 +344,7 @@ or any of its children.
|
|||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary><b>YXmlFragment</b></summary>
|
<summary><b>Y.XmlFragment</b></summary>
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
A container that holds an Array of Y.XmlElements.
|
A container that holds an Array of Y.XmlElements.
|
||||||
|
|||||||
2
package-lock.json
generated
2
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.0.0-99",
|
"version": "13.0.0-101",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.0.0-99",
|
"version": "13.0.0-101",
|
||||||
"description": "Shared Editing Library",
|
"description": "Shared Editing Library",
|
||||||
"main": "./dist/yjs.js",
|
"main": "./dist/yjs.js",
|
||||||
"module": "./dist/yjs.mjs",
|
"module": "./dist/yjs.mjs",
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import * as set from 'lib0/set.js'
|
|||||||
import * as binary from 'lib0/binary.js'
|
import * as binary from 'lib0/binary.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @todo This should return several items
|
||||||
|
*
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
* @param {ID} id
|
* @param {ID} id
|
||||||
* @return {{item:Item, diff:number}}
|
* @return {{item:Item, diff:number}}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
createID,
|
createID,
|
||||||
followRedone,
|
followRedone,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
|
getState,
|
||||||
Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line
|
Transaction, Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
@@ -49,35 +50,53 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|||||||
transact(doc, transaction => {
|
transact(doc, transaction => {
|
||||||
while (stack.length > 0 && result === null) {
|
while (stack.length > 0 && result === null) {
|
||||||
const store = doc.store
|
const store = doc.store
|
||||||
|
const clientID = doc.clientID
|
||||||
const stackItem = /** @type {StackItem} */ (stack.pop())
|
const stackItem = /** @type {StackItem} */ (stack.pop())
|
||||||
|
const stackStartClock = stackItem.start
|
||||||
|
const stackEndClock = stackItem.start + stackItem.len
|
||||||
const itemsToRedo = new Set()
|
const itemsToRedo = new Set()
|
||||||
|
// @todo iterateStructs should not need the structs parameter
|
||||||
|
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(clientID))
|
||||||
let performedChange = false
|
let performedChange = false
|
||||||
|
if (stackStartClock !== stackEndClock) {
|
||||||
|
// make sure structs don't overlap with the range of created operations [stackItem.start, stackItem.start + stackItem.end)
|
||||||
|
getItemCleanStart(transaction, createID(clientID, stackStartClock))
|
||||||
|
if (stackEndClock < getState(doc.store, clientID)) {
|
||||||
|
getItemCleanStart(transaction, createID(clientID, stackEndClock))
|
||||||
|
}
|
||||||
|
}
|
||||||
iterateDeletedStructs(transaction, stackItem.ds, struct => {
|
iterateDeletedStructs(transaction, stackItem.ds, struct => {
|
||||||
if (struct instanceof Item && scope.some(type => isParentOf(type, struct))) {
|
if (
|
||||||
|
struct instanceof Item &&
|
||||||
|
scope.some(type => isParentOf(type, struct)) &&
|
||||||
|
// Never redo structs in [stackItem.start, stackItem.start + stackItem.end) because they were created and deleted in the same capture interval.
|
||||||
|
!(struct.id.client === clientID && struct.id.clock >= stackStartClock && struct.id.clock < stackEndClock)
|
||||||
|
) {
|
||||||
itemsToRedo.add(struct)
|
itemsToRedo.add(struct)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
itemsToRedo.forEach(item => {
|
itemsToRedo.forEach(struct => {
|
||||||
performedChange = redoItem(transaction, item, itemsToRedo) !== null || performedChange
|
performedChange = redoItem(transaction, struct, itemsToRedo) !== null || performedChange
|
||||||
})
|
})
|
||||||
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(doc.clientID))
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<Item>}
|
* @type {Array<Item>}
|
||||||
*/
|
*/
|
||||||
const itemsToDelete = []
|
const itemsToDelete = []
|
||||||
iterateStructs(transaction, structs, stackItem.start, stackItem.len, struct => {
|
iterateStructs(transaction, structs, stackStartClock, stackItem.len, struct => {
|
||||||
if (struct instanceof Item && !struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) {
|
if (struct instanceof Item) {
|
||||||
if (struct.redone !== null) {
|
if (struct.redone !== null) {
|
||||||
let { item, diff } = followRedone(store, struct.id)
|
let { item, diff } = followRedone(store, struct.id)
|
||||||
if (diff > 0) {
|
if (diff > 0) {
|
||||||
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff))
|
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff))
|
||||||
}
|
}
|
||||||
if (item.length > stackItem.len) {
|
if (item.length > stackItem.len) {
|
||||||
getItemCleanStart(transaction, createID(item.id.client, item.id.clock + stackItem.len))
|
getItemCleanStart(transaction, createID(item.id.client, stackEndClock))
|
||||||
}
|
}
|
||||||
struct = item
|
struct = item
|
||||||
}
|
}
|
||||||
itemsToDelete.push(struct)
|
if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) {
|
||||||
|
itemsToDelete.push(struct)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// We want to delete in reverse order so that children are deleted before
|
// We want to delete in reverse order so that children are deleted before
|
||||||
@@ -111,9 +130,9 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|||||||
/**
|
/**
|
||||||
* Fires 'stack-item-added' event when a stack item was added to either the undo- or
|
* Fires 'stack-item-added' event when a stack item was added to either the undo- or
|
||||||
* the redo-stack. You may store additional stack information via the
|
* the redo-stack. You may store additional stack information via the
|
||||||
* metadata property on `event.stackItem.metadata` (it is a `Map` of metadata properties).
|
* metadata property on `event.stackItem.meta` (it is a `Map` of metadata properties).
|
||||||
* Fires 'stack-item-popped' event when a stack item was popped from either the
|
* Fires 'stack-item-popped' event when a stack item was popped from either the
|
||||||
* undo- or the redo-stack. You may restore the saved stack information from `event.stackItem.metadata`.
|
* undo- or the redo-stack. You may restore the saved stack information from `event.stackItem.meta`.
|
||||||
*
|
*
|
||||||
* @extends {Observable<'stack-item-added'|'stack-item-popped'>}
|
* @extends {Observable<'stack-item-added'|'stack-item-popped'>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export class YEvent {
|
|||||||
/**
|
/**
|
||||||
* Check if a struct is deleted by this event.
|
* Check if a struct is deleted by this event.
|
||||||
*
|
*
|
||||||
|
* In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
|
||||||
|
*
|
||||||
* @param {AbstractStruct} struct
|
* @param {AbstractStruct} struct
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
@@ -66,6 +68,8 @@ export class YEvent {
|
|||||||
/**
|
/**
|
||||||
* Check if a struct is added by this event.
|
* Check if a struct is added by this event.
|
||||||
*
|
*
|
||||||
|
* In contrast to change.deleted, this method also returns true if the struct was added and then deleted.
|
||||||
|
*
|
||||||
* @param {AbstractStruct} struct
|
* @param {AbstractStruct} struct
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +110,7 @@ export class YEvent {
|
|||||||
}
|
}
|
||||||
for (let item = target._start; item !== null; item = item.right) {
|
for (let item = target._start; item !== null; item = item.right) {
|
||||||
if (item.deleted) {
|
if (item.deleted) {
|
||||||
if (this.deletes(item)) {
|
if (this.deletes(item) && !this.adds(item)) {
|
||||||
if (lastOp === null || lastOp.delete === undefined) {
|
if (lastOp === null || lastOp.delete === undefined) {
|
||||||
packOp()
|
packOp()
|
||||||
lastOp = { delete: 0 }
|
lastOp = { delete: 0 }
|
||||||
|
|||||||
@@ -13,6 +13,23 @@ import * as t from 'lib0/testing.js'
|
|||||||
export const testUndoText = tc => {
|
export const testUndoText = tc => {
|
||||||
const { testConnector, text0, text1 } = init(tc, { users: 3 })
|
const { testConnector, text0, text1 } = init(tc, { users: 3 })
|
||||||
const undoManager = new UndoManager(text0)
|
const undoManager = new UndoManager(text0)
|
||||||
|
|
||||||
|
// items that are added & deleted in the same transaction won't be undo
|
||||||
|
text0.insert(0, 'test')
|
||||||
|
text0.delete(0, 4)
|
||||||
|
undoManager.undo()
|
||||||
|
t.assert(text0.toString() === '')
|
||||||
|
|
||||||
|
// follow redone items
|
||||||
|
text0.insert(0, 'a')
|
||||||
|
undoManager.stopCapturing()
|
||||||
|
text0.delete(0, 1)
|
||||||
|
undoManager.stopCapturing()
|
||||||
|
undoManager.undo()
|
||||||
|
t.assert(text0.toString() === 'a')
|
||||||
|
undoManager.undo()
|
||||||
|
t.assert(text0.toString() === '')
|
||||||
|
|
||||||
text0.insert(0, 'abc')
|
text0.insert(0, 'abc')
|
||||||
text1.insert(0, 'xyz')
|
text1.insert(0, 'xyz')
|
||||||
testConnector.syncAll()
|
testConnector.syncAll()
|
||||||
@@ -65,6 +82,15 @@ export const testUndoMap = tc => {
|
|||||||
t.assert(map0.get('a') === 44)
|
t.assert(map0.get('a') === 44)
|
||||||
undoManager.redo()
|
undoManager.redo()
|
||||||
t.assert(map0.get('a') === 44)
|
t.assert(map0.get('a') === 44)
|
||||||
|
|
||||||
|
// test setting value multiple times
|
||||||
|
map0.set('b', 'initial')
|
||||||
|
undoManager.stopCapturing()
|
||||||
|
map0.set('b', 'val1')
|
||||||
|
map0.set('b', 'val2')
|
||||||
|
undoManager.stopCapturing()
|
||||||
|
undoManager.undo()
|
||||||
|
t.assert(map0.get('b') === 'initial')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user