Cscott has uploaded a new change for review. https://gerrit.wikimedia.org/r/81928
Change subject: Collapse metadata on any removal. ...................................................................... Collapse metadata on any removal. In ve.dm.Document.getMetadataReplace(), we used to only merge metadata if the amount removed is larger than the amount inserted. But this could end up putting metadata in odd positions, for example if you have Foo[[Category:Bar]]Baz and you delete 'ooBa' and replace it with {image}xxx{/image}, then the category ends up inside the image. We should always merge metadata when a segment is deleted, so that it appears outside any new structure added. Bug: 53444 Bug: 53445 Change-Id: I51d55fb370b473273f9cf152fdd0f356377d4109 --- M modules/ve/dm/ve.dm.Document.js M modules/ve/dm/ve.dm.Transaction.js M modules/ve/dm/ve.dm.TransactionProcessor.js M modules/ve/test/dm/ve.dm.Document.test.js M modules/ve/test/dm/ve.dm.Transaction.test.js M modules/ve/test/dm/ve.dm.TransactionProcessor.test.js 6 files changed, 134 insertions(+), 23 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/VisualEditor refs/changes/28/81928/1 diff --git a/modules/ve/dm/ve.dm.Document.js b/modules/ve/dm/ve.dm.Document.js index fa282ac..fddaee2 100644 --- a/modules/ve/dm/ve.dm.Document.js +++ b/modules/ve/dm/ve.dm.Document.js @@ -343,23 +343,29 @@ * @returns {Object} Metadata replace operation to keep data & metadata in sync */ ve.dm.Document.prototype.getMetadataReplace = function ( offset, remove, insert ) { - var removeMetadata, insertMetadata, replace = {}; - if ( remove > insert.length ) { - // if we are removing more than we are inserting we need to collapse the excess metadata - removeMetadata = this.getMetadata( new ve.Range( offset + insert.length, offset + remove + 1 ) ); + var removeMetadata, insertMetadata, replace = {}, extend; + if ( remove > 0 ) { + // if we are removing anything we need to collapse the metadata + if ( insert.length > 0 ) { + // if there's at least one element inserted, collapse the metadata + // onto the first cursor position. + extend = 0; + } else { + // Make the collapsed region is 1 larger than the deleted region, so we + // can put the collapsed metadata on the element immediately + // following the deleted region. + extend = 1; + } + removeMetadata = this.getMetadata( new ve.Range( offset, offset + remove + extend ) ); // check removeMetadata is non-empty if ( !ve.compare( removeMetadata, new Array( removeMetadata.length ) ) ) { insertMetadata = ve.dm.MetaLinearData.static.merge( removeMetadata ); - replace.retain = insert.length; + if ( extend === 0 ) { + ve.batchSplice( insertMetadata, 1, 0, new Array( insert.length - 1 ) ); + } replace.remove = removeMetadata; replace.insert = insertMetadata; } - } - // if insert.length === remove metadata can just stay where it is - if ( insert.length > remove ) { - // if we are inserting more than we are removing then we need to pad out with undefineds - replace.retain = remove; - replace.insert = new Array( insert.length - remove ); } return replace; }; diff --git a/modules/ve/dm/ve.dm.Transaction.js b/modules/ve/dm/ve.dm.Transaction.js index f5b7e3a..38674b4 100644 --- a/modules/ve/dm/ve.dm.Transaction.js +++ b/modules/ve/dm/ve.dm.Transaction.js @@ -888,7 +888,6 @@ lastOp = end >= 0 ? this.operations[end] : null, remove = doc.getData( new ve.Range( offset, offset + removeLength ) ), metadataReplace = doc.getMetadataReplace( offset, removeLength, insert ), - retainMetadata = metadataReplace.retain, removeMetadata = metadataReplace.remove, insertMetadata = metadataReplace.insert; @@ -904,7 +903,6 @@ 'insert': insert }; if ( removeMetadata !== undefined ) { - op.retainMetadata = retainMetadata; op.removeMetadata = removeMetadata; op.insertMetadata = insertMetadata; } diff --git a/modules/ve/dm/ve.dm.TransactionProcessor.js b/modules/ve/dm/ve.dm.TransactionProcessor.js index e24807b..959c404 100644 --- a/modules/ve/dm/ve.dm.TransactionProcessor.js +++ b/modules/ve/dm/ve.dm.TransactionProcessor.js @@ -332,11 +332,9 @@ this.document.data.batchSplice( this.cursor, remove.length, insert ); // Keep the meta linear model in sync if ( removeMetadata !== undefined ) { - this.document.metadata.batchSplice( this.cursor + retainMetadata, removeMetadata.length, insertMetadata ); - } else if ( insert.length > remove.length ) { - this.document.metadata.batchSplice( this.cursor + remove.length, 0, new Array( insert.length - remove.length ) ); - } else if ( insert.length < remove.length ) { - this.document.metadata.batchSplice( this.cursor + insert.length, remove.length - insert.length, [] ); + this.document.metadata.batchSplice( this.cursor, removeMetadata.length, insertMetadata ); + } else { + this.document.metadata.batchSplice( this.cursor, remove.length, new Array( insert.length ) ); } this.applyAnnotations( this.cursor + insert.length ); // Get the node containing the replaced content diff --git a/modules/ve/test/dm/ve.dm.Document.test.js b/modules/ve/test/dm/ve.dm.Document.test.js index bcacb2d..1a387f7 100644 --- a/modules/ve/test/dm/ve.dm.Document.test.js +++ b/modules/ve/test/dm/ve.dm.Document.test.js @@ -118,7 +118,6 @@ replace = doc.getMetadataReplace( 10, 1, [] ); expectedReplace = { - 'retain': 0, 'remove': doc.getMetadata().slice( 10, 12 ), 'insert': ve.dm.MetaLinearData.static.merge( doc.getMetadata().slice( 10, 12 ) ) }; @@ -126,7 +125,6 @@ replace = doc.getMetadataReplace( 5, 2, [] ); expectedReplace = { - 'retain': 0, 'remove': doc.getMetadata().slice( 5, 8 ), 'insert': ve.dm.MetaLinearData.static.merge( doc.getMetadata().slice( 5, 8 ) ) }; @@ -134,7 +132,6 @@ replace = doc.getMetadataReplace( 1, 8, [] ); expectedReplace = { - 'retain': 0, 'remove': doc.getMetadata().slice( 1, 10 ), 'insert': ve.dm.MetaLinearData.static.merge( doc.getMetadata().slice( 1, 10 ) ) }; diff --git a/modules/ve/test/dm/ve.dm.Transaction.test.js b/modules/ve/test/dm/ve.dm.Transaction.test.js index 4a5c31a..776b521 100644 --- a/modules/ve/test/dm/ve.dm.Transaction.test.js +++ b/modules/ve/test/dm/ve.dm.Transaction.test.js @@ -577,7 +577,6 @@ 'type': 'replace', 'remove': ['B', 'a'], 'insert': [], - 'retainMetadata': 0, 'removeMetadata': metaDoc.getMetadata().slice( 7, 10 ), 'insertMetadata': ve.dm.MetaLinearData.static.merge( metaDoc.getMetadata().slice( 7, 10 ) ) }, diff --git a/modules/ve/test/dm/ve.dm.TransactionProcessor.test.js b/modules/ve/test/dm/ve.dm.TransactionProcessor.test.js index 886f1b4..726e1d4 100644 --- a/modules/ve/test/dm/ve.dm.TransactionProcessor.test.js +++ b/modules/ve/test/dm/ve.dm.TransactionProcessor.test.js @@ -334,7 +334,120 @@ ['pushReplace', 0, 5, [ { 'type': 'table' }, { 'type': '/table' } ]] ], 'expected': function ( data ) { - data.splice( 0, 2, { 'type': 'table' }, { 'type': '/table' } ); + data.splice( 0, 2 ); + data.splice( 2, 3, { 'type': 'table' }, { 'type': '/table' } ); + } + }, + 'structural replacement starting at an offset with metadata': { + 'data': [ + { + 'type': 'alienMeta', + 'attributes': { + 'domElements': $( '<!-- foo -->' ).toArray() + } + }, + { 'type': '/alienMeta' }, + { 'type': 'paragraph' }, + 'F', + { + 'type': 'alienMeta', + 'attributes': { + 'style': 'comment', + 'text': ' inline ' + } + }, + { 'type': '/alienMeta' }, + 'o', 'o', + { 'type': '/paragraph' } + ], + 'calls': [ + ['pushReplace', 0, 5, [ { 'type': 'table' }, { 'type': '/table' } ]] + ], + 'expected': function ( data ) { + // metadata is merged. + data.splice( 2, 2 ); + data.splice( 4, 3, { 'type': 'table' }, { 'type': '/table' } ); + } + }, + 'structural replacement ending at an offset with metadata': { + 'data': [ + { + 'type': 'alienMeta', + 'attributes': { + 'domElements': $( '<!-- foo -->' ).toArray() + } + }, + { 'type': '/alienMeta' }, + { 'type': 'paragraph' }, + 'F', + { + 'type': 'alienMeta', + 'attributes': { + 'style': 'comment', + 'text': ' inline ' + } + }, + { 'type': '/alienMeta' }, + 'o', 'o', + { 'type': '/paragraph' }, + { + 'type': 'alienMeta', + 'attributes': { + 'domElements': $( '<!-- bar -->' ).toArray() + } + }, + { 'type': '/alienMeta' }, + { 'type': 'paragraph' }, + 'B', 'a', 'r', + { 'type': '/paragraph' } + ], + 'calls': [ + ['pushReplace', 0, 5, [ { 'type': 'table' }, { 'type': '/table' } ]] + ], + 'expected': function ( data ) { + // metadata is merged. + data.splice( 2, 2 ); + data.splice( 4, 3, { 'type': 'table' }, { 'type': '/table' } ); + } + }, + 'structural deletion ending at an offset with metadata': { + 'data': [ + { + 'type': 'alienMeta', + 'attributes': { + 'domElements': $( '<!-- foo -->' ).toArray() + } + }, + { 'type': '/alienMeta' }, + { 'type': 'paragraph' }, + 'F', + { + 'type': 'alienMeta', + 'attributes': { + 'style': 'comment', + 'text': ' inline ' + } + }, + { 'type': '/alienMeta' }, + 'o', 'o', + { 'type': '/paragraph' }, + { + 'type': 'alienMeta', + 'attributes': { + 'domElements': $( '<!-- bar -->' ).toArray() + } + }, + { 'type': '/alienMeta' }, + { 'type': 'paragraph' }, + 'B', 'a', 'r', + { 'type': '/paragraph' } + ], + 'calls': [ + ['pushReplace', 0, 5, [] ] + ], + 'expected': function ( data ) { + // metadata is merged. + data.splice( 2, 2 ); data.splice( 4, 3 ); } } -- To view, visit https://gerrit.wikimedia.org/r/81928 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I51d55fb370b473273f9cf152fdd0f356377d4109 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/VisualEditor Gerrit-Branch: master Gerrit-Owner: Cscott <canan...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits