 db881fa0ad
			
		
	
	db881fa0ad
	
	
	
		
			
			Building on existing support for asynchronous diff processing, the rendering stage is now also asynchronous. The `emitGroup` method of gr-diff-builder (which constructs a DOM fragment and attaches it to the document) is now called whenever the processor emits a new group, rather than waiting for all groups to be available and looping over them. Adds support for canceling the processor, and removes a behavior where the diff was being re-rendered needlessly, causing visible flicker. Updates a test that broke when rendering became asynchronous. Change-Id: I37fcd65efc8c901b85fc93e91611c022edc10dc4
		
			
				
	
	
		
			265 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!--
 | |
| Copyright (C) 2016 The Android Open Source Project
 | |
| 
 | |
| Licensed under the Apache License, Version 2.0 (the "License");
 | |
| you may not use this file except in compliance with the License.
 | |
| You may obtain a copy of the License at
 | |
| 
 | |
| http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| Unless required by applicable law or agreed to in writing, software
 | |
| distributed under the License is distributed on an "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| See the License for the specific language governing permissions and
 | |
| limitations under the License.
 | |
| -->
 | |
| <link rel="import" href="../../../bower_components/polymer/polymer.html">
 | |
| <link rel="import" href="../gr-diff-processor/gr-diff-processor.html">
 | |
| 
 | |
| <dom-module id="gr-diff-builder">
 | |
|   <template>
 | |
|     <div class="contentWrapper">
 | |
|       <content></content>
 | |
|     </div>
 | |
|     <gr-diff-processor
 | |
|         id="processor"
 | |
|         groups="{{_groups}}"></gr-diff-processor>
 | |
|   </template>
 | |
|   <script src="../gr-diff/gr-diff-line.js"></script>
 | |
|   <script src="../gr-diff/gr-diff-group.js"></script>
 | |
|   <script src="gr-diff-builder.js"></script>
 | |
|   <script src="gr-diff-builder-side-by-side.js"></script>
 | |
|   <script src="gr-diff-builder-unified.js"></script>
 | |
|   <script src="gr-diff-builder-image.js"></script>
 | |
|   <script>
 | |
|     (function() {
 | |
|       'use strict';
 | |
| 
 | |
|       var DiffViewMode = {
 | |
|         SIDE_BY_SIDE: 'SIDE_BY_SIDE',
 | |
|         UNIFIED: 'UNIFIED_DIFF',
 | |
|       };
 | |
| 
 | |
|       Polymer({
 | |
|         is: 'gr-diff-builder',
 | |
| 
 | |
|         /**
 | |
|          * Fired when the diff is rendered.
 | |
|          *
 | |
|          * @event render
 | |
|          */
 | |
| 
 | |
|         properties: {
 | |
|           viewMode: String,
 | |
|           isImageDiff: Boolean,
 | |
|           baseImage: Object,
 | |
|           revisionImage: Object,
 | |
|           _builder: Object,
 | |
|           _groups: Array,
 | |
|         },
 | |
| 
 | |
|         get diffElement() {
 | |
|           return this.queryEffectiveChildren('#diffTable');
 | |
|         },
 | |
| 
 | |
|         observers: [
 | |
|           '_groupsChanged(_groups.splices)',
 | |
|         ],
 | |
| 
 | |
|         render: function(diff, comments, prefs) {
 | |
|           // Stop the processor (if it's running).
 | |
|           this.$.processor.cancel();
 | |
| 
 | |
|           this._builder = this._getDiffBuilder(diff, comments, prefs);
 | |
| 
 | |
|           this.$.processor.context = prefs.context;
 | |
|           this.$.processor.keyLocations = this._getCommentLocations(comments);
 | |
| 
 | |
|           this._clearDiffContent();
 | |
| 
 | |
|           this.$.processor.process(diff.content).then(function() {
 | |
|             if (this.isImageDiff) {
 | |
|               this._builder.renderDiffImages();
 | |
|             }
 | |
|             this.fire('render');
 | |
|           }.bind(this));
 | |
|         },
 | |
| 
 | |
|         getLineElByChild: function(node) {
 | |
|           while (node) {
 | |
|             if (node instanceof Element) {
 | |
|               if (node.classList.contains('lineNum')) {
 | |
|                 return node;
 | |
|               }
 | |
|               if (node.classList.contains('section')) {
 | |
|                 return null;
 | |
|               }
 | |
|             }
 | |
|             node = node.previousSibling || node.parentElement;
 | |
|           }
 | |
|           return null;
 | |
|         },
 | |
| 
 | |
|         renderLineRange: function(startLine, endLine, opt_side) {
 | |
|           var groups =
 | |
|               this._builder.getGroupsByLineRange(startLine, endLine, opt_side);
 | |
|           groups.forEach(function(group) {
 | |
|             var newElement = this._builder.buildSectionElement(group);
 | |
|             var oldElement = group.element;
 | |
| 
 | |
|             // Transfer comment threads from existing section to new one.
 | |
|             var threads = Polymer.dom(newElement).querySelectorAll(
 | |
|                 'gr-diff-comment-thread');
 | |
|             threads.forEach(function(threadEl) {
 | |
|               var lineEl = this.getLineElByChild(threadEl, oldElement);
 | |
|               if (!lineEl) { // New comment thread.
 | |
|                 return;
 | |
|               }
 | |
|               var side = this.getSideByLineEl(lineEl);
 | |
|               var line = lineEl.getAttribute('data-value');
 | |
|               var oldThreadEl =
 | |
|                   this.getCommentThreadByLine(line, side, oldElement);
 | |
|               threadEl.parentNode.replaceChild(oldThreadEl, threadEl);
 | |
|             }, this);
 | |
| 
 | |
|             // Replace old group elements with new ones.
 | |
|             group.element.parentNode.replaceChild(newElement, group.element);
 | |
|             group.element = newElement;
 | |
|           }, this);
 | |
| 
 | |
|           this.async(function() {
 | |
|             this.fire('render');
 | |
|           }, 1);
 | |
|         },
 | |
| 
 | |
|         getContentByLine: function(lineNumber, opt_side, opt_root) {
 | |
|           var root = Polymer.dom(opt_root || this.diffElement);
 | |
|           var sideSelector = !!opt_side ? ('.' + opt_side) : '';
 | |
|           return root.querySelector('td.lineNum[data-value="' + lineNumber +
 | |
|               '"]' + sideSelector + ' ~ td.content');
 | |
|         },
 | |
| 
 | |
|         getContentByLineEl: function(lineEl) {
 | |
|           var root = Polymer.dom(lineEl.parentElement);
 | |
|           var side = this.getSideByLineEl(lineEl);
 | |
|           var line = lineEl.getAttribute('data-value');
 | |
|           return this.getContentByLine(line, side, root);
 | |
|         },
 | |
| 
 | |
|         getLineElByNumber: function(lineNumber, opt_side) {
 | |
|           var sideSelector = !!opt_side ? ('.' + opt_side) : '';
 | |
|           return this.diffElement.querySelector(
 | |
|               '.lineNum[data-value="' + lineNumber + '"]' + sideSelector);
 | |
|         },
 | |
| 
 | |
|         getContentsByLineRange: function(startLine, endLine, opt_side) {
 | |
|           var groups =
 | |
|               this._builder.getGroupsByLineRange(startLine, endLine, opt_side);
 | |
|           // In each group, search Element for lines in range.
 | |
|           return groups.reduce((function(acc, group) {
 | |
|             for (var line = startLine; line <= endLine; line++) {
 | |
|               var content =
 | |
|                   this.getContentByLine(line, opt_side, group.element);
 | |
|               if (content) {
 | |
|                 acc.push(content);
 | |
|               }
 | |
|             }
 | |
|             return acc;
 | |
|           }).bind(this), []);
 | |
|         },
 | |
| 
 | |
|         getCommentThreadByLine: function(lineNumber, opt_side, opt_root) {
 | |
|           var root = Polymer.dom(opt_root || this.diffElement);
 | |
|           var sideSelector = !!opt_side ? ('.' + opt_side) : '';
 | |
|           var content = this.getContentByLine(lineNumber, opt_side, opt_root);
 | |
|           return this.getCommentThreadByContentEl(content);
 | |
|         },
 | |
| 
 | |
|         getCommentThreadByContentEl: function(contentEl) {
 | |
|           return contentEl.querySelector('gr-diff-comment-thread');
 | |
|         },
 | |
| 
 | |
|         getSideByLineEl: function(lineEl) {
 | |
|           return lineEl.classList.contains(GrDiffBuilder.Side.RIGHT) ?
 | |
|               GrDiffBuilder.Side.RIGHT : GrDiffBuilder.Side.LEFT;
 | |
|         },
 | |
| 
 | |
|         createCommentThread: function(changeNum, patchNum, path, side,
 | |
|             projectConfig) {
 | |
|           return this._builder.createCommentThread(changeNum, patchNum, path,
 | |
|               side, projectConfig);
 | |
|         },
 | |
| 
 | |
|         emitGroup: function(group, sectionEl) {
 | |
|           this._builder.emitGroup(group, sectionEl);
 | |
|         },
 | |
| 
 | |
|         showContext: function(newGroups, sectionEl) {
 | |
|           var groups = this._builder.groups;
 | |
|           // TODO(viktard): Polyfill findIndex for IE10.
 | |
|           var contextIndex = groups.findIndex(function(group) {
 | |
|             return group.element == sectionEl;
 | |
|           });
 | |
|           groups.splice.apply(groups, [contextIndex, 1].concat(newGroups));
 | |
| 
 | |
|           newGroups.forEach(function(newGroup) {
 | |
|             this._builder.emitGroup(newGroup, sectionEl);
 | |
|           }, this);
 | |
|           sectionEl.parentNode.removeChild(sectionEl);
 | |
| 
 | |
|           this.async(function() {
 | |
|             this.fire('render');
 | |
|           }, 1);
 | |
|         },
 | |
| 
 | |
|         _getDiffBuilder: function(diff, comments, prefs) {
 | |
|           if (this.isImageDiff) {
 | |
|             return new GrDiffBuilderImage(diff, comments, prefs,
 | |
|                 this.diffElement, this.baseImage, this.revisionImage);
 | |
|           } else if (this.viewMode === DiffViewMode.SIDE_BY_SIDE) {
 | |
|             return new GrDiffBuilderSideBySide(
 | |
|                 diff, comments, prefs, this.diffElement);
 | |
|           } else if (this.viewMode === DiffViewMode.UNIFIED) {
 | |
|             return new GrDiffBuilderUnified(
 | |
|                 diff, comments, prefs, this.diffElement);
 | |
|           }
 | |
|           throw Error('Unsupported diff view mode: ' + this.viewMode);
 | |
|         },
 | |
| 
 | |
|         _clearDiffContent: function() {
 | |
|           this.diffElement.innerHTML = null;
 | |
|         },
 | |
| 
 | |
|         _getCommentLocations: function(comments) {
 | |
|           var result = {
 | |
|             left: {},
 | |
|             right: {},
 | |
|           };
 | |
|           for (var side in comments) {
 | |
|             if (side !== GrDiffBuilder.Side.LEFT &&
 | |
|                 side !== GrDiffBuilder.Side.RIGHT) {
 | |
|               continue;
 | |
|             }
 | |
|             comments[side].forEach(function(c) {
 | |
|               result[side][c.line || GrDiffLine.FILE] = true;
 | |
|             });
 | |
|           }
 | |
|           return result;
 | |
|         },
 | |
| 
 | |
|         _groupsChanged: function(changeRecord) {
 | |
|           if (!changeRecord) { return; }
 | |
|           changeRecord.indexSplices.forEach(function(splice) {
 | |
|             var group;
 | |
|             for (var i = 0; i < splice.addedCount; i++) {
 | |
|               group = splice.object[splice.index + i];
 | |
|               this._builder.groups.push(group);
 | |
|               this._builder.emitGroup(group);
 | |
|             }
 | |
|           }, this);
 | |
|         },
 | |
|       });
 | |
|     })();
 | |
|   </script>
 | |
| </dom-module>
 |