Constructor
new ve.dm.TransactionSquasher(transaction)
#
Squasher to create one transaction from multiple transactions applicable in turn
The squashed transaction has the same effect on a document as applying the original transactions in turn, but it may cause rebase conflicts where the original sequence of transactions would not have.
Suppose we have linmod arrays A, B, C, D. Note that (x,A->B,m) then (x,C->D,n) becomes (x,A.concat(C*)->D.concat(B*),min(m,n)) Where len = min(B.length, C.length) and C*=C.slice(len) and B*=B.slice(len). I.e. "A->B then C->D" becomes "remove A, remove C*, insert D, insert B*". Examples: 1234Aa5678 (4,Aa->Bb,4) 1234Bb5678 (4,B->D,5) 1234Db5678 --> (4,Aa->Db,4) 1234Aa5678 (4,Aa->Bb,4) 1234Bb5678 (4,Bb5->Dd,3) 1234Dd678 --> (4,Aa5->Dd,3)
The same sort of thing happens if the starts are not the same, e.g. helloworld (2,ll>LL,1,wo>WO,3) heLLoWOrld (3,LoW>HI,4) heLHIrld --> (2,llowo>LHI,3) hiwed (1,i>ello,1,e>orl,1) helloworld (2,llowor>a,2) heald --> (1,iwe>eal,1)
However, removal followed by reinsertion cannot be stripped out entirely, because then the squashed transaction, considered as a partial function between documents, would have a larger preimage than the composition of the original transactions. This can probably break things like associativity). Example:
hello! (1,ello>iworld,1) hiworld! (1,i>ello,6) helloworld! --> (1,ello>elloworld,1)
For annotations in the follow-up transaction, two forms of processing are needed: annotating previously-inserted content, and adding the annotations operation into the transaction for retained ranges.
Parameters:
Name | Type | Description |
---|---|---|
transaction |
ve.dm.Transaction | Base transaction to clone then squash others onto |
Squasher to create one transaction from multiple transactions applicable in turn
The squashed transaction has the same effect on a document as applying the original transactions in turn, but it may cause rebase conflicts where the original sequence of transactions would not have.
Properties
attributeOperations
#
Properties:
Name | Type | Description |
---|---|---|
attributeOperations |
Object | During squashIn, live references to attribute operations at current offset, keyed by attribute name, or null if at an open element |
globalOffset
#
Properties:
Name | Type | Description |
---|---|---|
globalOffset |
number | During squashIn, global offset over all operations |
index
#
Properties:
Name | Type | Description |
---|---|---|
index |
number | During squashIn, index of current op within operations |
offset
#
During squashIn, post-transaction offset within the current op
Properties:
Name | Type | Description |
---|---|---|
offset |
number | During squashIn, post-tx linmod offset within current op. "Post transaction" means that for replacements, the offset is within the insert block. The reason we care about post-transaction offsets is that the match the pre-transaction offsets of the next transaction. |
operations
#
Properties:
transaction
#
Properties:
Name | Type | Description |
---|---|---|
transaction |
ve.dm.Transaction | Transaction being squashed together |
Methods
changeElement(openElement, key, from, to)private
#
Change an attribute in an open element
Parameters:
Name | Type | Description |
---|---|---|
openElement |
Object | The open element |
key |
string | The attribute name |
from |
any | Old value, or undefined if the attribute is being created |
to |
any | New value, or undefined if the attribute is being removed |
getTransaction() → {ve.dm.Transaction}
#
normalizePosition()private
#
Normalize .index, .offset and .op so we're not at the end of a replace/retain
processAttribute(key, from, to)private
#
Process the setting of an attribute
The case from === to is possible. An identity attribute change still proves there is an open element at this position, so cannot be stripped
Parameters:
Name | Type | Description |
---|---|---|
key |
string | The attribute key |
from |
any | The old value |
to |
any | The new value |
Process the setting of an attribute
The case from === to is possible.
processInsert(items) → {number}private
#
Process the insertion of some items, stopping part-way if convenient
If some of the insertion is undoing a removal in this.transaction, then the "cancelled" content effectively becomes part of an identity replacement: replace 'foo' with 'foo'. (The content cannot be stripped out entirely from the squashed transaction, because then the squashed transaction, considered as a partial function between documents, would have a larger preimage than the composition of the original transactions. This can probably break things like associativity).
Parameters:
Returns:
The length of the initial slice of items that was inserted
- Type
- number
Process the insertion of some items, stopping part-way if convenient
If some of the insertion is undoing a removal in this.transaction, then the "cancelled" content effectively becomes part of an identity replacement: replace 'foo' with 'foo'.
processRemove(items) → {number}private
#
Process the removal of some items, stopping part-way if convenient
If some of the removal is undoing an insertion in this.transaction, then the "cancelled" content is stripped out entirely from the squashed transaction.
Parameters:
Name | Type | Description |
---|---|---|
items |
Array.<Object> | Items to remove some of; can be modified in place (annotated) |
Returns:
The length of the initial slice of items that was removed
- Type
- number
Process the removal of some items, stopping part-way if convenient
If some of the removal is undoing an insertion in this.transaction, then the "cancelled" content is stripped out entirely from the squashed transaction.
processRetain(maxLength) → {number}private
#
Process the retention of content, stopping part-way if convenient
Parameters:
Name | Type | Description |
---|---|---|
maxLength |
number | The maximum amount of content to retain |
Returns:
The amount of content retained
- Type
- number
readAttributes()private
#
Read the open element at the current offset (if any)
Sets this.openElement to the open element (or null) Sets this.attribute to an object containing attribute key-values (or {})
Read the open element at the current offset (if any)
Sets this.openElement to the open element (or null) Sets this.attribute to an object containing attribute key-values (or {})
splitIfInterior()private
#
If in the interior of a retain operation, split it here without moving.
For retain, the length is split at the current offset (throws an error if at the end) For all other operations, throws an error if not at the start
Afterwards, this.offset is guaranteed to be 0.
squashIn(tx)
#
Modify our Transaction in-place to incorporate a follow-up transaction
Applying the modified transaction has the same effect as applying the original transaction then the follow-up, but it may cause rebase conflicts where the original pair of transactions would not have.
Parameters:
Name | Type | Description |
---|---|---|
tx |
ve.dm.Transaction | Follow-up transaction (that can apply immediately after this) |
Modify our Transaction in-place to incorporate a follow-up transaction
Applying the modified transaction has the same effect as applying the original transaction then the follow-up, but it may cause rebase conflicts where the original pair of transactions would not have.
tryUnsplit()private
#
If this operation and the previous one are retains, join them
equalItems(item1, item2) → {boolean}static
#
Test whether two linmod items have equal values
Parameters:
Name | Type | Description |
---|---|---|
item1 |
any | A data item |
item2 |
any | Another data item |
Returns:
Whether the items have equal values
- Type
- boolean
squash(transactions) → {ve.dm.Transaction}static
#
Squash an array of consecutive transactions into a single transaction
Parameters:
Name | Type | Description |
---|---|---|
transactions |
Array.<ve.dm.Transaction> | Non-empty array of consecutive transactions |
Returns:
Single transaction with the same content as the transaction array
- Type
- ve.dm.Transaction