Esanders has uploaded a new change for review.
https://gerrit.wikimedia.org/r/61048
Change subject: Allow fixUpInsertion to move offsets when inserting at start/end
......................................................................
Allow fixUpInsertion to move offsets when inserting at start/end
First commit, probably needs some more test cases.
Change-Id: Ic51dd03725c11f1f7e279929534ee3afea14d662
---
M modules/ve/dm/ve.dm.Document.js
M modules/ve/dm/ve.dm.Transaction.js
M modules/ve/test/dm/ve.dm.Transaction.test.js
3 files changed, 82 insertions(+), 29 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor
refs/changes/48/61048/1
diff --git a/modules/ve/dm/ve.dm.Document.js b/modules/ve/dm/ve.dm.Document.js
index 1136c9d..8f2ceb5 100644
--- a/modules/ve/dm/ve.dm.Document.js
+++ b/modules/ve/dm/ve.dm.Document.js
@@ -487,6 +487,9 @@
// and popped off when balanced out by an opening in data
closingStack = [],
+ firstChildStack = [],
+ lastChildStack = [],
+
// *** State persisting across iterations of the outer loop ***
// The node (from the document) we're currently in. When in a
node that was opened
// in data, this is set to its first ancestor that is already
in the document
@@ -602,6 +605,16 @@
parentNode = this.getNodeFromOffset( offset );
parentType = parentNode.getType();
inTextNode = false;
+
+ i = offset;
+ while( this.data.isOpenElementData( --i ) ) {
+ firstChildStack.push( this.data.getData( i ) );
+ }
+ i = offset - 1;
+ while( this.data.isCloseElementData( ++i ) ) {
+ lastChildStack.push( this.data.getData( i ) );
+ }
+
for ( i = 0; i < data.length; i++ ) {
if ( inTextNode && data[i].type !== undefined ) {
parentType = openingStack.length > 0 ?
@@ -657,6 +670,10 @@
);
if ( !childrenOK ) {
// We can't insert this into this parent
+ if ( firstChildStack.length ) {
+ return this.fixupInsertion(
data, offset - 1 );
+ }
+
// Close the parent and try one level up
closings.push( { 'type': '/' +
parentType } );
if ( openingStack.length > 0 ) {
@@ -711,6 +728,10 @@
}
}
+ if ( closingStack.length > 0 && lastChildStack.length > 0 ) {
+ return this.fixupInsertion( data, offset + 1 );
+ }
+
if ( inTextNode ) {
parentType = openingStack.length > 0 ?
openingStack[openingStack.length - 1].type :
parentNode.getType();
@@ -731,7 +752,10 @@
writeElement( popped.getClonedElement(), i );
}
- return newData;
+ return {
+ offset: offset,
+ data: newData
+ };
};
/**
diff --git a/modules/ve/dm/ve.dm.Transaction.js
b/modules/ve/dm/ve.dm.Transaction.js
index b2a9a7d..d8ce506 100644
--- a/modules/ve/dm/ve.dm.Transaction.js
+++ b/modules/ve/dm/ve.dm.Transaction.js
@@ -36,11 +36,11 @@
// Fix up the insertion
insertion = doc.fixupInsertion( insertion, offset );
// Retain up to insertion point, if needed
- tx.pushRetain( offset );
+ tx.pushRetain( insertion.offset );
// Insert data
- tx.pushReplace( doc, offset, 0, insertion );
+ tx.pushReplace( doc, offset, 0, insertion.data );
// Retain to end of document, if needed (for completeness)
- tx.pushRetain( data.length - offset );
+ tx.pushRetain( data.length - insertion.offset );
return tx;
};
diff --git a/modules/ve/test/dm/ve.dm.Transaction.test.js
b/modules/ve/test/dm/ve.dm.Transaction.test.js
index 82769f4..8147e36 100644
--- a/modules/ve/test/dm/ve.dm.Transaction.test.js
+++ b/modules/ve/test/dm/ve.dm.Transaction.test.js
@@ -48,6 +48,9 @@
doc2 = new ve.dm.Document(
ve.dm.example.preprocessAnnotations( [ { 'type':
'paragraph' }, { 'type': '/paragraph' } ] )
),
+ doc3 = new ve.dm.Document(
+ ve.dm.example.preprocessAnnotations( [ { 'type':
'paragraph' }, 'F', 'o', 'o', { 'type': '/paragraph' } ] )
+ ),
cases = {
'paragraph before first element': {
'args': [doc, 0, [{ 'type': 'paragraph' }, '1',
{ 'type': '/paragraph' }]],
@@ -95,16 +98,16 @@
{ 'type': 'retain', 'length': 59 }
]
},
- 'paragraph inside a list closes and reopens list': {
+ 'paragraph inside a list moves in front of list': {
'args': [doc, 13, [{ 'type': 'paragraph' },
'F', 'O', 'O', { 'type': '/paragraph' }]],
'ops': [
- { 'type': 'retain', 'length': 13 },
+ { 'type': 'retain', 'length': 12 },
{
'type': 'replace',
'remove': [],
- 'insert': [{'type': '/list' },
{ 'type': 'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type':
'list', 'attributes': { 'style': 'bullet' } }]
+ 'insert': [{ 'type':
'paragraph' } , 'F', 'O', 'O', { 'type': '/paragraph' }]
},
- { 'type': 'retain', 'length': 48 }
+ { 'type': 'retain', 'length': 49 }
]
},
'tableCell inside the document is wrapped in a table,
tableSection and tableRow': {
@@ -119,16 +122,16 @@
{ 'type': 'retain', 'length': 18 }
]
},
- 'tableCell inside a paragraph is wrapped in a table,
tableSection and tableRow and closes and reopens the paragraph': {
+ 'tableCell inside a paragraph is wrapped in a table,
tableSection and tableRow and moves outside of paragraph': {
'args': [doc, 52, [{ 'type': 'tableCell',
'attributes': { 'style': 'data' } }, { 'type': 'paragraph' }, 'F', 'O', 'O', {
'type': '/paragraph' }, { 'type': '/tableCell' }]],
'ops': [
- { 'type': 'retain', 'length': 52 },
+ { 'type': 'retain', 'length': 53 },
{
'type': 'replace',
'remove': [],
- 'insert': [{ 'type':
'/paragraph' }, { 'type': 'table' }, { 'type': 'tableSection', 'attributes': {
'style': 'body' } }, { 'type': 'tableRow' }, { 'type': 'tableCell',
'attributes': { 'style': 'data' } }, { 'type': 'paragraph' }, 'F', 'O', 'O', {
'type': '/paragraph' }, { 'type': '/tableCell' }, { 'type': '/tableRow' }, {
'type': '/tableSection' }, { 'type': '/table' }, { 'type': 'paragraph' }]
+ 'insert': [{ 'type': 'table' },
{ 'type': 'tableSection', 'attributes': { 'style': 'body' } }, { 'type':
'tableRow' }, { 'type': 'tableCell', 'attributes': { 'style': 'data' } }, {
'type': 'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type':
'/tableCell' }, { 'type': '/tableRow' }, { 'type': '/tableSection' }, { 'type':
'/table' }]
},
- { 'type': 'retain', 'length': 9 }
+ { 'type': 'retain', 'length': 8 }
]
},
'text at a structural location in the document is
wrapped in a paragraph': {
@@ -166,52 +169,56 @@
{ 'type': 'retain', 'length': 59 }
]
},
- 'text inside a tableSection is wrapped in a paragraph
and closes and reopens the tableSection, tableRow and table': {
+ 'text inside a tableSection moves all the way to the
end of the table and is wrapped in a paragraph': {
'args': [doc, 34, ['F', 'O', 'O']],
'ops': [
- { 'type': 'retain', 'length': 34 },
+ { 'type': 'retain', 'length': 37 },
{
'type': 'replace',
'remove': [],
- 'insert': [{ 'type':
'/tableRow' }, { 'type': '/tableSection' }, { 'type': '/table' }, { 'type':
'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'table' }, {
'type': 'tableSection', 'attributes': { 'style': 'body' } }, { 'type':
'tableRow' } ]
+ 'insert': [{ 'type':
'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }]
},
- { 'type': 'retain', 'length': 27 }
+ { 'type': 'retain', 'length': 24 }
]
},
- 'insert two complete paragraphs into a paragraph': {
+ 'insert two complete paragraphs into start of paragraph
moves insertion point left': {
'args': [doc, 10, [{ 'type': 'paragraph' },
'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'paragraph' }, 'B', 'A',
'R', { 'type': '/paragraph' }]],
'ops': [
- { 'type': 'retain', 'length': 10 },
+ { 'type': 'retain', 'length': 9 },
{
'type': 'replace',
'remove': [],
- 'insert': [{ 'type':
'/paragraph' }, { 'type': 'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph'
}, { 'type': 'paragraph' }, 'B', 'A', 'R', { 'type': '/paragraph' }, { 'type':
'paragraph' }]
+ 'insert': [{ 'type':
'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'paragraph'
}, 'B', 'A', 'R', { 'type': '/paragraph' }]
},
- { 'type': 'retain', 'length': 51 }
+ { 'type': 'retain', 'length': 52 }
]
},
- 'insert text, close paragraph and open heading into
paragraph': {
+ 'insert text, close paragraph and open heading into end
of paragraph moves insertion point right': {
'args': [doc, 57, ['F', 'O', 'O', { 'type':
'/paragraph' }, { 'type': 'heading', 'attributes': { 'level': 1 } }, 'B', 'A',
'R']],
'ops': [
- { 'type': 'retain', 'length': 57 },
+ { 'type': 'retain', 'length': 58 },
{
'type': 'replace',
'remove': [],
- 'insert': ['F', 'O', 'O', {
'type': '/paragraph' }, { 'type': 'heading', 'attributes': { 'level': 1 } },
'B', 'A', 'R', { 'type': '/heading' }, { 'type': 'paragraph' }]
+ 'insert': [{ 'type':
'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'heading',
'attributes': { 'level': 1 } }, 'B', 'A', 'R', { 'type': '/heading' }]
},
- { 'type': 'retain', 'length': 4 }
+ { 'type': 'retain', 'length': 3 }
]
},
- 'insert paragraph and incomplete heading into
paragraph': {
- 'args': [doc, 10, [{ 'type': 'paragraph' },
'F', 'O', 'O', { 'type': '/paragraph' }, { 'type': 'heading', 'attributes': {
'level': 1 } }, 'B', 'A', 'R']],
+ 'insert heading and incomplete paragraph into heading':
{
+ 'args': [doc, 2, [{ 'type': 'heading',
'attributes': { 'level': 1 } }, 'F', 'O', 'O', { 'type': '/heading' }, {
'type': 'paragraph' }, 'B', 'A', 'R']],
'ops': [
- { 'type': 'retain', 'length': 10 },
+ { 'type': 'retain', 'length': 2 },
{
'type': 'replace',
'remove': [],
- 'insert': [{ 'type':
'/paragraph' }, { 'type': 'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph'
}, { 'type': 'heading', 'attributes': { 'level': 1 } }, 'B', 'A', 'R', {
'type': '/heading' }, { 'type': 'paragraph' }]
+ 'insert': [
+ { 'type': '/heading' },
{ 'type': 'heading', 'attributes': { 'level': 1 } }, 'F', 'O', 'O', { 'type':
'/heading' },
+ { 'type': 'paragraph'
}, 'B', 'A', 'R', { 'type': '/paragraph' },
+ { 'type': 'heading',
'attributes': { 'level': 1 } }
+ ]
},
- { 'type': 'retain', 'length': 51 }
+ { 'type': 'retain', 'length': 59 }
]
},
'inserting two paragraphs into a document with just an
empty paragraph': {
@@ -237,6 +244,28 @@
},
{ 'type': 'retain', 'length': 1 }
]
+ },
+ 'inserting one paragraph into empty paragraph moves
insertion before': {
+ 'args': [doc2, 1, [{ 'type': 'paragraph' },
'F', 'O', 'O', { 'type': '/paragraph' }]],
+ 'ops': [
+ {
+ 'type': 'replace',
+ 'remove': [],
+ 'insert': [{ 'type':
'paragraph' }, 'F', 'O', 'O', { 'type': '/paragraph' }]
+ },
+ { 'type': 'retain', 'length': 2 }
+ ]
+ },
+ 'inserting paragraph and end of paragraph moves
insertion point forward': {
+ 'args': [doc3, 4, [{ 'type': 'paragraph' },
'B', 'A', 'R', { 'type': '/paragraph' }]],
+ 'ops': [
+ { 'type': 'retain', 'length': 5 },
+ {
+ 'type': 'replace',
+ 'remove': [],
+ 'insert': [{ 'type':
'paragraph' }, 'B', 'A', 'R', { 'type': '/paragraph' }]
+ }
+ ]
}
// TODO test cases for unclosed openings
// TODO test cases for (currently failing) unopened
closings use case
--
To view, visit https://gerrit.wikimedia.org/r/61048
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ic51dd03725c11f1f7e279929534ee3afea14d662
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/VisualEditor
Gerrit-Branch: master
Gerrit-Owner: Esanders <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits