Tchanders has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/355716 )
Change subject: Visual Diff: add internal list diff ...................................................................... Visual Diff: add internal list diff Display changes to references in the visual diff. Bug: T162819 Change-Id: If603a1861b32c1241396608ea1bd1927ebba9049 --- M src/dm/ve.dm.VisualDiff.js M src/ui/elements/ve.ui.DiffElement.js 2 files changed, 173 insertions(+), 12 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/VisualEditor/VisualEditor refs/changes/16/355716/1 diff --git a/src/dm/ve.dm.VisualDiff.js b/src/dm/ve.dm.VisualDiff.js index 750d112..6782614 100644 --- a/src/dm/ve.dm.VisualDiff.js +++ b/src/dm/ve.dm.VisualDiff.js @@ -21,16 +21,19 @@ this.oldDoc = oldDoc.cloneFromRange(); this.newDoc = newDoc.cloneFromRange(); - this.oldDocNode = oldDoc.getDocumentNode(); - this.newDocNode = newDoc.getDocumentNode(); + this.oldDocNode = this.oldDoc.getDocumentNode(); + this.newDocNode = this.newDoc.getDocumentNode(); this.oldDocChildren = this.oldDocNode.children; this.newDocChildren = this.newDocNode.children; + this.oldDocInternalListNode = this.oldDocChildren[ this.oldDocChildren.length - 1 ]; + this.newDocInternalListNode = this.newDocChildren[ this.newDocChildren.length - 1 ]; this.treeDiffer = treeDiffer; // eslint-disable-next-line camelcase,new-cap this.linearDiffer = new ve.DiffMatchPatch( this.oldDoc.getStore(), this.newDoc.getStore() ); this.endTime = new Date().getTime() + ( timeout || 1000 ); this.diff = this.getDiff(); + this.diff.internalListDiff = this.getInternalListDiffInfo(); }; /** @@ -364,6 +367,7 @@ if ( keepLength < 0.5 * diffLength ) { return false; } + return { treeDiff: treeDiff, diffInfo: diffInfo, @@ -372,3 +376,81 @@ }; }; + +/* + * Get the diff between the old document's internal list and the new document's + * internal list. The diff is grouped by list group, and each node in each list + * group is marked as removed, inserted, the same, or changed (in which case the + * linear diff is given). + * + * @return {Object} Internal list diff object + */ +ve.dm.VisualDiff.prototype.getInternalListDiffInfo = function () { + var i, ilen, diff, + group, groupIndexOrder, nodeIndex, + oldDocInternalListNodes = this.oldDoc.internalList.nodes, + newDocInternalListNodes = this.newDoc.internalList.nodes, + retainedInternalListItems = {}, + removedInternalListItems = {}, + internalListDiffInfo = {}; + + // Find all nodes referenced by the new document's internal list + for ( group in newDocInternalListNodes ) { + groupIndexOrder = newDocInternalListNodes[ group ].indexOrder; + internalListDiffInfo[ group ] = {}; + for ( i = 0, ilen = groupIndexOrder.length; i < ilen; i++ ) { + nodeIndex = groupIndexOrder[ i ]; + retainedInternalListItems[ nodeIndex ] = group; + internalListDiffInfo[ group ] = { + changes: false + }; + } + } + + // Find all nodes referenced by the old document's internal list + for ( group in oldDocInternalListNodes ) { + groupIndexOrder = oldDocInternalListNodes[ group ].indexOrder; + for ( i = 0, ilen = groupIndexOrder.length; i < ilen; i++ ) { + nodeIndex = groupIndexOrder[ i ]; + if ( retainedInternalListItems[ nodeIndex ] ) { + continue; + } + removedInternalListItems[ nodeIndex ] = group; + // TODO: Work out what should happen if the whole list group was removed + internalListDiffInfo[ group ] = { + changes: false + }; + } + } + + for ( i = 0, ilen = this.newDocInternalListNode.children.length; i < ilen; i++ ) { + nodeIndex = i; + group = retainedInternalListItems[ nodeIndex ] || removedInternalListItems[ nodeIndex ]; + if ( i > this.oldDocInternalListNode.children.length - 1 ) { + // Item was inserted + internalListDiffInfo[ group ][ nodeIndex ] = 1; + internalListDiffInfo[ group ].changes = true; + } else if ( i in removedInternalListItems ) { + // Item was removed + internalListDiffInfo[ group ][ nodeIndex ] = -1; + internalListDiffInfo[ group ].changes = true; + } else { + // Item corresponds to the item in the old document's internal list with + // the same index. They could be the same or changed. + diff = this.getDocChildDiff( + this.oldDocInternalListNode.children[ i ], + this.newDocInternalListNode.children[ i ] + ); + if ( diff.diffInfo.length > 0 ) { + // Item was changed from an old item + internalListDiffInfo[ group ][ nodeIndex ] = diff; + internalListDiffInfo[ group ].changes = true; + } else { + // Item is unchanged from an old item + internalListDiffInfo[ group ][ nodeIndex ] = 0; + } + } + } + + return internalListDiffInfo; +}; diff --git a/src/ui/elements/ve.ui.DiffElement.js b/src/ui/elements/ve.ui.DiffElement.js index 67e3961..f88e087 100644 --- a/src/ui/elements/ve.ui.DiffElement.js +++ b/src/ui/elements/ve.ui.DiffElement.js @@ -25,14 +25,19 @@ // Documents this.oldDoc = visualDiff.oldDoc; this.newDoc = visualDiff.newDoc; - this.oldDocChildren = this.oldDoc.getDocumentNode().children; - this.newDocChildren = this.newDoc.getDocumentNode().children; + this.oldDocChildren = visualDiff.oldDocChildren; + this.newDocChildren = visualDiff.newDocChildren; + + // Internal list + this.newDocInternalListNode = visualDiff.newDocInternalListNode; + this.oldDocInternalListNode = visualDiff.oldDocInternalListNode; // Diff this.oldToNew = diff.docChildrenOldToNew; this.newToOld = diff.docChildrenNewToOld; this.insert = diff.docChildrenInsert; this.remove = diff.docChildrenRemove; + this.internalListDiff = diff.internalListDiff; this.$overlays = $( '<div>' ).addClass( 've-ui-diffElement-overlays' ); this.$content = $( '<div>' ).addClass( 've-ui-diffElement-content' ); @@ -184,6 +189,9 @@ */ ve.ui.DiffElement.prototype.renderDiff = function () { var i, j, k, ilen, jlen, klen, nodes, move, elements, spacerNode, noChanges, + group, headingNode, listNode, groupName, nodeIndex, listItemNode, + internalListGroup, annotatedData, documentSlice, nodeData, body, + referenceDiffDiv, anyInternalListChanges, documentNode = this.$document[ 0 ], anyChanges = false, spacer = false, @@ -267,8 +275,80 @@ } } else if ( !spacer ) { spacer = true; - documentNode.appendChild( spacerNode.cloneNode( true ) ); + documentNode.appendChild( + documentNode.ownerDocument.adoptNode( spacerNode.cloneNode( true ) ) + ); } + } + + // Render the internal list diff, i.e. all reflists with changed nodes. + // TODO: It would be nice if the reflists could be rendered in place in the document; however, + // they could be hard to find if they are within a template, so for now they are just shown at + // the end of the diff. + referenceDiffDiv = document.createElement( 'div' ); + for ( group in this.internalListDiff ) { + + internalListGroup = this.internalListDiff[ group ]; + if ( !internalListGroup.changes ) { + continue; + } + delete internalListGroup.changes; + + // There are changes, so display this list + anyInternalListChanges = true; + headingNode = document.createElement( 'h2' ); + headingNode.setAttribute( 'data-diff-action', 'none' ); + listNode = document.createElement( 'ol' ); + groupName = group.split( '/' )[ 1 ] || 'References'; + headingNode.appendChild( document.createTextNode( groupName ) ); + referenceDiffDiv.appendChild( headingNode ); + referenceDiffDiv.appendChild( listNode ); + + for ( nodeIndex in internalListGroup ) { + listItemNode = document.createElement( 'li' ); + if ( internalListGroup[ nodeIndex ] === 1 ) { + elements = this.getNodeElements( this.newDocInternalListNode.children[ nodeIndex ].children[ 0 ], 'insert' ); + listItemNode.appendChild( + listItemNode.ownerDocument.adoptNode( elements[ 0 ] ) + ); + } else if ( internalListGroup[ nodeIndex ] === -1 ) { + elements = this.getNodeElements( this.oldDocInternalListNode.children[ nodeIndex ].children[ 0 ], 'remove' ); + listItemNode.appendChild( + listItemNode.ownerDocument.adoptNode( elements[ 0 ] ) + ); + } else if ( internalListGroup[ nodeIndex ] === 0 ) { + elements = this.getNodeElements( this.newDocInternalListNode.children[ nodeIndex ].children[ 0 ], 'none' ); + listItemNode.appendChild( + listItemNode.ownerDocument.adoptNode( elements[ 0 ] ) + ); + } else { + + annotatedData = this.annotateNode( internalListGroup[ nodeIndex ].diffInfo[ 0 ].linearDiff ); + elements = document.createElement( 'div' ); + elements.setAttribute( 'class', 've-ui-diffElement-doc-child-change' ); + documentSlice = this.newDoc.cloneFromRange( { from: 0, to: 0 } ); + documentSlice.getStore().merge( this.newDoc.getStore() ); + nodeData = documentSlice.data.data; + ve.batchSplice( nodeData, 0, 0, annotatedData ); + body = ve.dm.converter.getDomFromModel( documentSlice, true ).body; + while ( body.childNodes.length ) { + elements.appendChild( + elements.ownerDocument.adoptNode( body.childNodes[ 0 ] ) + ); + } + + listItemNode.appendChild( + listItemNode.ownerDocument.adoptNode( elements ) + ); + + } + + listNode.appendChild( listItemNode ); + + } + } + if ( anyInternalListChanges ) { + documentNode.appendChild( referenceDiffDiv ); } ve.resolveAttributes( documentNode, this.newDoc.getHtmlDocument(), ve.dm.Converter.static.computedAttributes ); @@ -295,10 +375,10 @@ * or moved * @param {string} action 'remove', 'insert' or, if moved, 'none' * @param {string} [move] 'up' or 'down' if the node has moved - * @return {HTMLElement[]} HTML to display the action/move + * @return {HTMLElement[]} Elements (not owned by window.document) */ ve.ui.DiffElement.prototype.getNodeElements = function ( node, action, move ) { - var nodeData, body, element, + var nodeData, doc, body, element, nodeDoc = action === 'remove' ? this.oldDoc : this.newDoc, documentSlice = nodeDoc.cloneFromRange( node.getOuterRange() ); @@ -312,15 +392,14 @@ // Doc is always the new doc when inserting into the store documentSlice.getStore().merge( this.newDoc.getStore() ); // forClipboard is true, so that we can render otherwise invisible nodes - body = ve.dm.converter.getDomFromModel( documentSlice, true ).body; + doc = ve.dm.converter.getDomFromModel( documentSlice, true ); + body = doc.body; if ( action !== 'none' ) { - element = document.createElement( 'div' ); + element = doc.createElement( 'div' ); element.setAttribute( 'class', 've-ui-diffElement-doc-child-change' ); while ( body.childNodes.length ) { - element.appendChild( - element.ownerDocument.adoptNode( body.childNodes[ 0 ] ) - ); + element.appendChild( body.childNodes[ 0 ] ); } return [ element ]; } -- To view, visit https://gerrit.wikimedia.org/r/355716 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: If603a1861b32c1241396608ea1bd1927ebba9049 Gerrit-PatchSet: 1 Gerrit-Project: VisualEditor/VisualEditor Gerrit-Branch: master Gerrit-Owner: Tchanders <thalia.e.c...@googlemail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits