Move gr-diff-new to gr-diff Change-Id: Ifaad016f806c31f3df43143b3238b757faa18b20
diff --git a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html index a47c50f..9a75fa9 100644 --- a/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html +++ b/polygerrit-ui/app/elements/core/gr-keyboard-shortcuts-dialog/gr-keyboard-shortcuts-dialog.html
@@ -194,6 +194,10 @@ <td><span class="key">a</span></td> <td>Review and publish comments</td> </tr> + <tr> + <td><span class="key">,</span></td> + <td>Show diff preferences</td> + </tr> </tbody> </table> </main>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html index 39477bc..d293060 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
@@ -44,7 +44,6 @@ draft="[[comment.__draft]]" show-actions="[[_showActions]]" project-config="[[projectConfig]]" - on-height-change="_handleCommentHeightChange" on-reply="_handleCommentReply" on-comment-discard="_handleCommentDiscard" on-done="_handleCommentDone"></gr-diff-comment>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js index c7bbff2..b827d26 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
@@ -18,12 +18,6 @@ is: 'gr-diff-comment-thread', /** - * Fired when the height of the thread changes. - * - * @event height-change - */ - - /** * Fired when the thread should be discarded. * * @event thread-discard @@ -44,11 +38,6 @@ }, _showActions: Boolean, - _boundWindowResizeHandler: { - type: Function, - value: function() { return this._handleWindowResize.bind(this); } - }, - _lastHeight: Number, _orderedComments: Array, }, @@ -64,12 +53,6 @@ this._getLoggedIn().then(function(loggedIn) { this._showActions = loggedIn; }.bind(this)); - - window.addEventListener('resize', this._boundWindowResizeHandler); - }, - - detached: function() { - window.removeEventListener('resize', this._boundWindowResizeHandler); }, addDraft: function(opt_lineNum) { @@ -88,10 +71,6 @@ return this.$.restAPI.getLoggedIn(); }, - _handleWindowResize: function(e) { - this._heightChanged(); - }, - _commentsChanged: function(changeRecord) { this._orderedComments = this._sortedComments(this.comments); }, @@ -133,11 +112,6 @@ } }, - _handleCommentHeightChange: function(e) { - e.stopPropagation(); - this._heightChanged(); - }, - _handleCommentReply: function(e) { var comment = e.detail.comment; var quoteStr; @@ -153,7 +127,6 @@ this.async(function() { var commentEl = this._commentElWithDraftID(reply.__draftID); commentEl.editing = true; - this.async(this._heightChanged.bind(this), 1); }.bind(this), 1); }, @@ -166,7 +139,6 @@ this.async(function() { var commentEl = this._commentElWithDraftID(reply.__draftID); commentEl.save(); - this.async(this._heightChanged.bind(this), 1); }.bind(this), 1); }, @@ -215,15 +187,6 @@ this.fire('thread-discard'); return; } - this.async(this._heightChanged.bind(this), 1); - }, - - _heightChanged: function() { - var height = this.$.container.offsetHeight; - if (height == this._lastHeight) { return; } - - this.fire('height-change', {height: height}, {bubbles: false}); - this._lastHeight = height; }, _indexOf: function(comment, arr) {
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html index ca6815b..a6a8c4d 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
@@ -129,7 +129,6 @@ disabled="{{disabled}}" rows="4" bind-value="{{_editDraft}}" - on-keyup="_handleTextareaKeyup" on-keydown="_handleTextareaKeydown"></iron-autogrow-textarea> <gr-linked-text class="message" pre
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js index dcdb881..81449b8 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
@@ -18,12 +18,6 @@ is: 'gr-diff-comment', /** - * Fired when the height of the comment changes. - * - * @event height-change - */ - - /** * Fired when the Reply action is triggered. * * @event reply @@ -75,10 +69,6 @@ this.editing = this._editDraft.length == 0; }, - attached: function() { - this._heightChanged(); - }, - save: function() { this.comment.message = this._editDraft; this.disabled = true; @@ -101,13 +91,6 @@ }.bind(this)); }, - _heightChanged: function() { - this.async(function() { - this.fire('height-change', {height: this.offsetHeight}, - {bubbles: false}); - }.bind(this)); - }, - _draftChanged: function(draft) { this.$.container.classList.toggle('draft', draft); }, @@ -126,7 +109,6 @@ if (this.comment && this.comment.id) { this.$$('.cancel').hidden = !editing; } - this._heightChanged(); }, _computeLinkToComment: function(comment) { @@ -137,12 +119,6 @@ return draft == null || draft.trim() == ''; }, - _handleTextareaKeyup: function(e) { - // TODO(andybons): This isn't always true, but I can't currently think - // of a better metric. - this._heightChanged(); - }, - _handleTextareaKeydown: function(e) { if (e.keyCode == 27) { // 'esc' this._handleCancel(e);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html deleted file mode 100644 index 972dc2d..0000000 --- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.html +++ /dev/null
@@ -1,97 +0,0 @@ -<!-- -Copyright (C) 2015 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-comment-thread/gr-diff-comment-thread.html"> - -<dom-module id="gr-diff-side"> - <template> - <style> - :host, - .container { - display: flex; - flex: 0 0 auto; - } - .lineNum:before, - .code:before { - /* To ensure the height is non-zero in these elements, a - zero-width space is set as its content. The character - itself doesn't matter. Just that there is something - there. */ - content: '\200B'; - } - .lineNum { - background-color: #eee; - color: #666; - padding: 0 .75em; - text-align: right; - } - .canComment .lineNum { - cursor: pointer; - text-decoration: underline; - } - .canComment .lineNum:hover { - background-color: #ccc; - } - .lightHighlight { - background-color: var(--light-highlight-color); - } - hl, - .darkHighlight { - background-color: var(--dark-highlight-color); - } - .br:after { - /* Line feed */ - content: '\A'; - } - .tab { - display: inline-block; - } - .tab.withIndicator:before { - color: #C62828; - /* >> character */ - content: '\00BB'; - } - .numbers, - .content { - white-space: pre; - } - .numbers .filler { - background-color: #eee; - } - .contextControl { - background-color: #fef; - } - .contextControl a:link, - .contextControl a:visited { - display: block; - text-decoration: none; - } - .numbers .contextControl { - padding: 0 .75em; - text-align: right; - } - .content .contextControl { - text-align: center; - } - </style> - <div class$="[[_computeContainerClass(canComment)]]"> - <div class="numbers" id="numbers"></div> - <div class="content" id="content"></div> - </div> - </template> - <script src="gr-diff-side.js"></script> -</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js deleted file mode 100644 index b97d7b7..0000000 --- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side.js +++ /dev/null
@@ -1,612 +0,0 @@ -// 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. -(function() { - 'use strict'; - - var CharCode = { - LESS_THAN: '<'.charCodeAt(0), - GREATER_THAN: '>'.charCodeAt(0), - AMPERSAND: '&'.charCodeAt(0), - SEMICOLON: ';'.charCodeAt(0), - }; - - var TAB_REGEX = /\t/g; - - Polymer({ - is: 'gr-diff-side', - - /** - * Fired when an expand context control is clicked. - * - * @event expand-context - */ - - /** - * Fired when a thread's height is changed. - * - * @event thread-height-change - */ - - /** - * Fired when a draft should be added. - * - * @event add-draft - */ - - /** - * Fired when a thread is removed. - * - * @event remove-thread - */ - - properties: { - canComment: { - type: Boolean, - value: false, - }, - content: { - type: Array, - notify: true, - observer: '_contentChanged', - }, - prefs: { - type: Object, - value: function() { return {}; }, - }, - changeNum: String, - patchNum: String, - path: String, - projectConfig: { - type: Object, - observer: '_projectConfigChanged', - }, - - _lineFeedHTML: { - type: String, - value: '<span class="style-scope gr-diff-side br"></span>', - readOnly: true, - }, - _highlightStartTag: { - type: String, - value: '<hl class="style-scope gr-diff-side">', - readOnly: true, - }, - _highlightEndTag: { - type: String, - value: '</hl>', - readOnly: true, - }, - _diffChunkLineNums: { - type: Array, - value: function() { return []; }, - }, - _commentThreadLineNums: { - type: Array, - value: function() { return []; }, - }, - _focusedLineNum: { - type: Number, - value: 1, - }, - }, - - listeners: { - 'tap': '_tapHandler', - }, - - observers: [ - '_prefsChanged(prefs.*)', - ], - - rowInserted: function(index) { - this.renderLineIndexRange(index, index); - this._updateDOMIndices(); - this._updateJumpIndices(); - }, - - rowRemoved: function(index) { - var removedEls = Polymer.dom(this.root).querySelectorAll( - '[data-index="' + index + '"]'); - for (var i = 0; i < removedEls.length; i++) { - removedEls[i].parentNode.removeChild(removedEls[i]); - } - this._updateDOMIndices(); - this._updateJumpIndices(); - }, - - rowUpdated: function(index) { - var removedEls = Polymer.dom(this.root).querySelectorAll( - '[data-index="' + index + '"]'); - for (var i = 0; i < removedEls.length; i++) { - removedEls[i].parentNode.removeChild(removedEls[i]); - } - this.renderLineIndexRange(index, index); - }, - - scrollToLine: function(lineNum) { - if (isNaN(lineNum) || lineNum < 1) { return; } - - var el = this.$$('.numbers .lineNum[data-line-num="' + lineNum + '"]'); - if (!el) { return; } - - // Calculate where the line is relative to the window. - var top = el.offsetTop; - for (var offsetParent = el.offsetParent; - offsetParent; - offsetParent = offsetParent.offsetParent) { - top += offsetParent.offsetTop; - } - - // Scroll the element to the middle of the window. Dividing by a third - // instead of half the inner height feels a bit better otherwise the - // element appears to be below the center of the window even when it - // isn't. - window.scrollTo(0, top - (window.innerHeight / 3) - el.offsetHeight); - }, - - scrollToNextDiffChunk: function() { - this._scrollToNextChunkOrThread(this._diffChunkLineNums); - }, - - scrollToPreviousDiffChunk: function() { - this._scrollToPreviousChunkOrThread(this._diffChunkLineNums); - }, - - scrollToNextCommentThread: function() { - this._scrollToNextChunkOrThread(this._commentThreadLineNums); - }, - - scrollToPreviousCommentThread: function() { - this._scrollToPreviousChunkOrThread(this._commentThreadLineNums); - }, - - renderLineIndexRange: function(startIndex, endIndex) { - this._render(this.content, startIndex, endIndex); - }, - - hideElementsWithIndex: function(index) { - var els = Polymer.dom(this.root).querySelectorAll( - '[data-index="' + index + '"]'); - for (var i = 0; i < els.length; i++) { - els[i].setAttribute('hidden', true); - } - }, - - getRowHeight: function(index) { - var row = this.content[index]; - // Filler elements should not be taken into account when determining - // height calculations. - if (row.type == 'FILLER') { - return 0; - } - if (row.height != null) { - return row.height; - } - - var selector = '[data-index="' + index + '"]'; - var els = Polymer.dom(this.root).querySelectorAll(selector); - if (els.length != 2) { - throw Error('Rows should only consist of two elements'); - } - return Math.max(els[0].offsetHeight, els[1].offsetHeight); - }, - - getRowNaturalHeight: function(index) { - var contentEl = this.$$('.content [data-index="' + index + '"]'); - return contentEl.naturalHeight || contentEl.offsetHeight; - }, - - setRowNaturalHeight: function(index) { - var lineEl = this.$$('.numbers [data-index="' + index + '"]'); - var contentEl = this.$$('.content [data-index="' + index + '"]'); - contentEl.style.height = null; - var height = contentEl.offsetHeight; - lineEl.style.height = height + 'px'; - this.content[index].height = height; - return height; - }, - - setRowHeight: function(index, height) { - var selector = '[data-index="' + index + '"]'; - var els = Polymer.dom(this.root).querySelectorAll(selector); - for (var i = 0; i < els.length; i++) { - els[i].style.height = height + 'px'; - } - this.content[index].height = height; - }, - - _scrollToNextChunkOrThread: function(lineNums) { - for (var i = 0; i < lineNums.length; i++) { - if (lineNums[i] > this._focusedLineNum) { - this._focusedLineNum = lineNums[i]; - this.scrollToLine(this._focusedLineNum); - return; - } - } - }, - - _scrollToPreviousChunkOrThread: function(lineNums) { - for (var i = lineNums.length - 1; i >= 0; i--) { - if (this._focusedLineNum > lineNums[i]) { - this._focusedLineNum = lineNums[i]; - this.scrollToLine(this._focusedLineNum); - return; - } - } - }, - - _updateJumpIndices: function() { - this._commentThreadLineNums = []; - this._diffChunkLineNums = []; - var inHighlight = false; - for (var i = 0; i < this.content.length; i++) { - switch (this.content[i].type) { - case 'COMMENT_THREAD': - this._commentThreadLineNums.push( - this.content[i].comments[0].line); - break; - case 'CODE': - // Only grab the first line of the highlighted chunk. - if (!inHighlight && this.content[i].highlight) { - this._diffChunkLineNums.push(this.content[i].lineNum); - inHighlight = true; - } else if (!this.content[i].highlight) { - inHighlight = false; - } - break; - } - } - }, - - _updateDOMIndices: function() { - // There is no way to select elements with a data-index greater than a - // given value. For now, just update all DOM elements. - var lineEls = Polymer.dom(this.root).querySelectorAll( - '.numbers [data-index]'); - var contentEls = Polymer.dom(this.root).querySelectorAll( - '.content [data-index]'); - if (lineEls.length != contentEls.length) { - throw Error( - 'There must be the same number of line and content elements'); - } - var index = 0; - for (var i = 0; i < this.content.length; i++) { - if (this.content[i].hidden) { continue; } - - lineEls[index].setAttribute('data-index', i); - contentEls[index].setAttribute('data-index', i); - index++; - } - }, - - _prefsChanged: function(changeRecord) { - var prefs = changeRecord.base; - this.$.content.style.width = prefs.line_length + 'ch'; - }, - - _projectConfigChanged: function(projectConfig) { - var threadEls = - Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread'); - for (var i = 0; i < threadEls.length; i++) { - threadEls[i].projectConfig = projectConfig; - } - }, - - _contentChanged: function(diff) { - this._clearChildren(this.$.numbers); - this._clearChildren(this.$.content); - this._render(diff, 0, diff.length - 1); - this._updateJumpIndices(); - }, - - _computeContainerClass: function(canComment) { - return 'container' + (canComment ? ' canComment' : ''); - }, - - _tapHandler: function(e) { - var lineEl = Polymer.dom(e).rootTarget; - if (!this.canComment || !lineEl.classList.contains('lineNum')) { - return; - } - - e.preventDefault(); - var index = parseInt(lineEl.getAttribute('data-index'), 10); - var line = parseInt(lineEl.getAttribute('data-line-num'), 10); - this.fire('add-draft', { - index: index, - line: line - }, {bubbles: false}); - }, - - _clearChildren: function(el) { - while (el.firstChild) { - el.removeChild(el.firstChild); - } - }, - - _handleContextControlClick: function(context, e) { - e.preventDefault(); - this.fire('expand-context', {context: context}, {bubbles: false}); - }, - - _render: function(diff, startIndex, endIndex) { - var beforeLineEl; - var beforeContentEl; - if (endIndex != diff.length - 1) { - beforeLineEl = this.$$('.numbers [data-index="' + endIndex + '"]'); - beforeContentEl = this.$$('.content [data-index="' + endIndex + '"]'); - if (!beforeLineEl && !beforeContentEl) { - // `endIndex` may be present within the model, but not in the DOM. - // Insert it before its successive element. - beforeLineEl = this.$$( - '.numbers [data-index="' + (endIndex + 1) + '"]'); - beforeContentEl = this.$$( - '.content [data-index="' + (endIndex + 1) + '"]'); - } - } - - for (var i = startIndex; i <= endIndex; i++) { - if (diff[i].hidden) { continue; } - - switch (diff[i].type) { - case 'CODE': - this._renderCode(diff[i], i, beforeLineEl, beforeContentEl); - break; - case 'FILLER': - this._renderFiller(diff[i], i, beforeLineEl, beforeContentEl); - break; - case 'CONTEXT_CONTROL': - this._renderContextControl(diff[i], i, beforeLineEl, - beforeContentEl); - break; - case 'COMMENT_THREAD': - this._renderCommentThread(diff[i], i, beforeLineEl, - beforeContentEl); - break; - } - } - }, - - _handleCommentThreadHeightChange: function(e) { - var threadEl = Polymer.dom(e).rootTarget; - var index = parseInt(threadEl.getAttribute('data-index'), 10); - this.content[index].height = e.detail.height; - var lineEl = this.$$('.numbers [data-index="' + index + '"]'); - lineEl.style.height = e.detail.height + 'px'; - this.fire('thread-height-change', { - index: index, - height: e.detail.height, - }, {bubbles: false}); - }, - - _handleCommentThreadDiscard: function(e) { - var threadEl = Polymer.dom(e).rootTarget; - var index = parseInt(threadEl.getAttribute('data-index'), 10); - this.fire('remove-thread', {index: index}, {bubbles: false}); - }, - - _renderCommentThread: function(thread, index, beforeLineEl, - beforeContentEl) { - var lineEl = this._createElement('div', 'commentThread'); - lineEl.classList.add('filler'); - lineEl.setAttribute('data-index', index); - var threadEl = document.createElement('gr-diff-comment-thread'); - threadEl.addEventListener('height-change', - this._handleCommentThreadHeightChange.bind(this)); - threadEl.addEventListener('thread-discard', - this._handleCommentThreadDiscard.bind(this)); - threadEl.setAttribute('data-index', index); - threadEl.changeNum = this.changeNum; - threadEl.patchNum = thread.patchNum || this.patchNum; - threadEl.path = this.path; - threadEl.comments = thread.comments; - threadEl.projectConfig = this.projectConfig; - - this.$.numbers.insertBefore(lineEl, beforeLineEl); - this.$.content.insertBefore(threadEl, beforeContentEl); - }, - - _renderContextControl: function(control, index, beforeLineEl, - beforeContentEl) { - var lineEl = this._createElement('div', 'contextControl'); - lineEl.setAttribute('data-index', index); - lineEl.textContent = '@@'; - var contentEl = this._createElement('div', 'contextControl'); - contentEl.setAttribute('data-index', index); - var a = this._createElement('a'); - a.href = '#'; - a.textContent = 'Show ' + control.numLines + ' common ' + - (control.numLines == 1 ? 'line' : 'lines') + '...'; - a.addEventListener('click', - this._handleContextControlClick.bind(this, control)); - contentEl.appendChild(a); - - this.$.numbers.insertBefore(lineEl, beforeLineEl); - this.$.content.insertBefore(contentEl, beforeContentEl); - }, - - _renderFiller: function(filler, index, beforeLineEl, beforeContentEl) { - var lineFillerEl = this._createElement('div', 'filler'); - lineFillerEl.setAttribute('data-index', index); - var fillerEl = this._createElement('div', 'filler'); - fillerEl.setAttribute('data-index', index); - var numLines = filler.numLines || 1; - - lineFillerEl.textContent = '\n'.repeat(numLines); - for (var i = 0; i < numLines; i++) { - var newlineEl = this._createElement('span', 'br'); - fillerEl.appendChild(newlineEl); - } - - this.$.numbers.insertBefore(lineFillerEl, beforeLineEl); - this.$.content.insertBefore(fillerEl, beforeContentEl); - }, - - _renderCode: function(code, index, beforeLineEl, beforeContentEl) { - var lineNumEl = this._createElement('div', 'lineNum'); - lineNumEl.setAttribute('data-line-num', code.lineNum); - lineNumEl.setAttribute('data-index', index); - var numLines = code.numLines || 1; - lineNumEl.textContent = code.lineNum + '\n'.repeat(numLines); - - var contentEl = this._createElement('div', 'code'); - contentEl.setAttribute('data-line-num', code.lineNum); - contentEl.setAttribute('data-index', index); - - if (code.highlight) { - contentEl.classList.add(code.intraline.length > 0 ? - 'lightHighlight' : 'darkHighlight'); - } - - var html = util.escapeHTML(code.content); - if (code.highlight && code.intraline.length > 0) { - html = this._addIntralineHighlights(code.content, html, - code.intraline); - } - if (numLines > 1) { - html = this._addNewLines(code.content, html, numLines); - } - html = this._addTabWrappers(code.content, html); - - // If the html is equivalent to the text then it didn't get highlighted - // or escaped. Use textContent which is faster than innerHTML. - if (code.content == html) { - contentEl.textContent = code.content; - } else { - contentEl.innerHTML = html; - } - - this.$.numbers.insertBefore(lineNumEl, beforeLineEl); - this.$.content.insertBefore(contentEl, beforeContentEl); - }, - - // Advance `index` by the appropriate number of characters that would - // represent one source code character and return that index. For - // example, for source code '<span>' the escaped html string is - // '<span>'. Advancing from index 0 on the prior html string would - // return 4, since < maps to one source code character ('<'). - _advanceChar: function(html, index) { - // Any tags don't count as characters - while (index < html.length && - html.charCodeAt(index) == CharCode.LESS_THAN) { - while (index < html.length && - html.charCodeAt(index) != CharCode.GREATER_THAN) { - index++; - } - index++; // skip the ">" itself - } - // An HTML entity (e.g., <) counts as one character. - if (index < html.length && - html.charCodeAt(index) == CharCode.AMPERSAND) { - while (index < html.length && - html.charCodeAt(index) != CharCode.SEMICOLON) { - index++; - } - } - return index + 1; - }, - - _addIntralineHighlights: function(content, html, highlights) { - var startTag = this._highlightStartTag; - var endTag = this._highlightEndTag; - - for (var i = 0; i < highlights.length; i++) { - var hl = highlights[i]; - - var htmlStartIndex = 0; - for (var j = 0; j < hl.startIndex; j++) { - htmlStartIndex = this._advanceChar(html, htmlStartIndex); - } - - var htmlEndIndex = 0; - if (hl.endIndex != null) { - for (var j = 0; j < hl.endIndex; j++) { - htmlEndIndex = this._advanceChar(html, htmlEndIndex); - } - } else { - // If endIndex isn't present, continue to the end of the line. - htmlEndIndex = html.length; - } - // The start and end indices could be the same if a highlight is meant - // to start at the end of a line and continue onto the next one. - // Ignore it. - if (htmlStartIndex != htmlEndIndex) { - html = html.slice(0, htmlStartIndex) + startTag + - html.slice(htmlStartIndex, htmlEndIndex) + endTag + - html.slice(htmlEndIndex); - } - } - return html; - }, - - _addNewLines: function(content, html, numLines) { - var htmlIndex = 0; - var indices = []; - var numChars = 0; - for (var i = 0; i < content.length; i++) { - if (numChars > 0 && numChars % this.prefs.line_length == 0) { - indices.push(htmlIndex); - } - htmlIndex = this._advanceChar(html, htmlIndex); - if (content[i] == '\t') { - numChars += this.prefs.tab_size; - } else { - numChars++; - } - } - var result = html; - var linesLeft = numLines; - // Since the result string is being altered in place, start from the end - // of the string so that the insertion indices are not affected as the - // result string changes. - for (var i = indices.length - 1; i >= 0; i--) { - result = result.slice(0, indices[i]) + this._lineFeedHTML + - result.slice(indices[i]); - linesLeft--; - } - // numLines is the total number of lines this code block should take up. - // Fill in the remaining ones. - for (var i = 0; i < linesLeft; i++) { - result += this._lineFeedHTML; - } - return result; - }, - - _addTabWrappers: function(content, html) { - // TODO(andybons): CSS tab-size is not supported in IE. - // Force this to be a number to prevent arbitrary injection. - var tabSize = +this.prefs.tab_size; - var htmlStr = '<span class="style-scope gr-diff-side tab ' + - (this.prefs.show_tabs ? 'withIndicator" ' : '" ') + - 'style="tab-size:' + tabSize + ';' + - '-moz-tab-size:' + tabSize + ';">\t</span>'; - return html.replace(TAB_REGEX, htmlStr); - }, - - _createElement: function(tagName, className) { - var el = document.createElement(tagName); - // When Shady DOM is being used, these classes are added to account for - // Polymer's polyfill behavior. In order to guarantee sufficient - // specificity within the CSS rules, these are added to every element. - // Since the Polymer DOM utility functions (which would do this - // automatically) are not being used for performance reasons, this is - // done manually. - el.classList.add('style-scope', 'gr-diff-side'); - if (!!className) { - el.classList.add(className); - } - return el; - }, - }); -})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html b/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html deleted file mode 100644 index dbae6cb..0000000 --- a/polygerrit-ui/app/elements/diff/gr-diff-side/gr-diff-side_test.html +++ /dev/null
@@ -1,300 +0,0 @@ -<!DOCTYPE html> -<!-- -Copyright (C) 2015 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. ---> - -<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> -<title>gr-diff-side</title> - -<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script> -<script src="../../../bower_components/web-component-tester/browser.js"></script> -<script src="../../../scripts/util.js"></script> - -<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html"> -<link rel="import" href="gr-diff-side.html"> - -<test-fixture id="basic"> - <template> - <gr-diff-side></gr-diff-side> - </template> -</test-fixture> - -<script> - suite('gr-diff-side tests', function() { - var element; - - function isVisibleInWindow(el) { - var rect = el.getBoundingClientRect(); - return rect.top >= 0 && rect.left >= 0 && - rect.bottom <= window.innerHeight && rect.right <= window.innerWidth; - } - - setup(function() { - element = fixture('basic'); - }); - - test('comments', function() { - assert.isFalse(element.$$('.container').classList.contains('canComment')); - element.canComment = true; - assert.isTrue(element.$$('.container').classList.contains('canComment')); - // TODO(andybons): Check for comment creation events firing/not firing - // when implemented. - }); - - test('scroll to line', function() { - var content = []; - for (var i = 0; i < 300; i++) { - content.push({ - type: 'CODE', - content: 'All work and no play makes Jack a dull boy', - numLines: 1, - lineNum: i + 1, - highlight: false, - intraline: [], - }); - } - element.content = content; - - window.scrollTo(0, 0); - element.scrollToLine(-12849); - assert.equal(window.scrollY, 0); - element.scrollToLine('sup'); - assert.equal(window.scrollY, 0); - var lineEl = element.$$('.numbers .lineNum[data-line-num="150"]'); - assert.ok(lineEl); - element.scrollToLine(150); - assert.isAbove(window.scrollY, 0); - assert.isTrue(isVisibleInWindow(lineEl), 'element should be visible'); - }); - - test('intraline highlights', function() { - var content = ' <gr-linked-text content="' + - '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>'; - var html = util.escapeHTML(content); - var highlights = [ - {startIndex: 0, endIndex: 33}, - {startIndex: 75}, - ]; - assert.equal( - content.slice(highlights[0].startIndex, highlights[0].endIndex), - ' <gr-linked-text content="'); - assert.equal(content.slice(highlights[1].startIndex), - '"></gr-linked-text>'); - var result = element._addIntralineHighlights(content, html, highlights); - var expected = element._highlightStartTag + - ' <gr-linked-text content="' + - element._highlightEndTag + - '[[_computeCurrentRevisionMessage(change)]]' + - element._highlightStartTag + - '"></gr-linked-text>' + - element._highlightEndTag; - assert.equal(result, expected); - }); - - test('newlines', function() { - element.prefs = { - line_length: 80, - tab_size: 4, - }; - - element.content = [{ - type: 'CODE', - content: 'A'.repeat(50), - numLines: 1, - lineNum: 1, - highlight: false, - intraline: [], - }]; - - var lineEl = element.$$('.numbers .lineNum[data-line-num="1"]'); - assert.ok(lineEl); - var contentEl = element.$$('.content .code[data-line-num="1"]'); - assert.ok(contentEl); - assert.equal(contentEl.innerHTML, 'A'.repeat(50)); - - element.content = [{ - type: 'CODE', - content: 'A'.repeat(100), - numLines: 2, - lineNum: 1, - highlight: false, - intraline: [], - }]; - - lineEl = element.$$('.numbers .lineNum[data-line-num="1"]'); - assert.ok(lineEl); - contentEl = element.$$('.content .code[data-line-num="1"]'); - assert.ok(contentEl); - assert.equal(contentEl.innerHTML, - 'A'.repeat(80) + element._lineFeedHTML + - 'A'.repeat(20) + element._lineFeedHTML); - }); - - test('tabs', function(done) { - element.prefs = { - line_length: 80, - tab_size: 4, - show_tabs: true, - }; - - element.content = [{ - type: 'CODE', - content: 'A'.repeat(50) + '\t' + 'A'.repeat(50), - numLines: 2, - lineNum: 1, - highlight: false, - intraline: [], - }]; - - var lineEl = element.$$('.numbers .lineNum[data-line-num="1"]'); - assert.ok(lineEl); - var contentEl = element.$$('.content .code[data-line-num="1"]'); - assert.ok(contentEl); - var spanEl = contentEl.childNodes[1]; - assert.equal(spanEl.tagName, 'SPAN'); - assert.isTrue(spanEl.classList.contains( - 'style-scope', 'gr-diff-side', 'tab', 'withIndicator')); - - element.prefs.show_tabs = false; - element.content = [{ - type: 'CODE', - content: 'A'.repeat(50) + '\t' + 'A'.repeat(50), - numLines: 2, - lineNum: 1, - highlight: false, - intraline: [], - }]; - contentEl = element.$$('.content .code[data-line-num="1"]'); - assert.ok(contentEl); - spanEl = contentEl.childNodes[1]; - assert.equal(spanEl.tagName, 'SPAN'); - assert.isTrue(spanEl.classList.contains( - 'style-scope', 'gr-diff-side', 'tab')); - - var alertStub = sinon.stub(window, 'alert'); - element.prefs.tab_size = - '"><img src="/" onerror="alert(1);"><span class="'; - element.content = [{ - type: 'CODE', - content: '\t', - numLines: 1, - lineNum: 1, - highlight: false, - intraline: [], - }]; - flush(function() { - assert.isFalse(alertStub.called); - alertStub.restore(); - done(); - }); - }); - - test('diff context', function() { - var content = [ - {type: 'CODE', hidden: true, content: '<!DOCTYPE html>'}, - {type: 'CODE', hidden: true, content: '<meta charset="utf-8">'}, - {type: 'CODE', hidden: true, content: '<title>My great page</title>'}, - {type: 'CODE', hidden: true, content: '<style>'}, - {type: 'CODE', hidden: true, content: ' *,'}, - {type: 'CODE', hidden: true, content: ' *:before,'}, - {type: 'CODE', hidden: true, content: ' *:after {'}, - {type: 'CODE', hidden: true, content: ' box-sizing: border-box;'}, - {type: 'CONTEXT_CONTROL', numLines: 8, start: 0, end: 8}, - {type: 'CODE', hidden: false, content: ' }'}, - ]; - element.content = content; - - // Only the context elements and the following code line elements should - // be present in the DOM. - var contextEls = - Polymer.dom(element.root).querySelectorAll('.contextControl'); - assert.equal(contextEls.length, 2); - var codeLineEls = - Polymer.dom(element.root).querySelectorAll('.lineNum, .code'); - assert.equal(codeLineEls.length, 2); - - for (var i = 0; i <= 8; i++) { - element.content[i].hidden = false; - } - element.renderLineIndexRange(0, 8); - element.hideElementsWithIndex(8); - - contextEls = - Polymer.dom(element.root).querySelectorAll('.contextControl'); - for (var i = 0; i < contextEls.length; i++) { - assert.isTrue(contextEls[i].hasAttribute('hidden')); - } - - codeLineEls = - Polymer.dom(element.root).querySelectorAll('.lineNum, .code'); - - // Nine lines should now be present in the DOM. - assert.equal(codeLineEls.length, 9 * 2); - }); - - test('tap line to add a draft', function() { - var numAddDraftEvents = 0; - sinon.stub(element, 'fire', function(eventName) { - if (eventName == 'add-draft') { - numAddDraftEvents++; - } - }); - element.content = [{type: 'CODE', content: '<!DOCTYPE html>'}]; - element.canComment = false; - flushAsynchronousOperations(); - - var lineEl = element.$$('.lineNum'); - assert.ok(lineEl); - MockInteractions.tap(lineEl); - assert.equal(numAddDraftEvents, 0); - - element.canComment = true; - MockInteractions.tap(lineEl); - assert.equal(numAddDraftEvents, 1); - }); - - test('jump to diff chunk/thread', function() { - element.content = [ - {type: 'CODE', content: '', intraline: [], lineNum: 1, highlight: true}, - {type: 'CODE', content: '', intraline: [], lineNum: 2, highlight: true}, - {type: 'CODE', content: '', intraline: [], lineNum: 3 }, - {type: 'CODE', content: '', intraline: [], lineNum: 4 }, - {type: 'COMMENT_THREAD', comments: [ { line: 4 }]}, - {type: 'CODE', content: '', intraline: [], lineNum: 5 }, - {type: 'CODE', content: '', intraline: [], lineNum: 6, highlight: true}, - {type: 'CODE', content: '', intraline: [], lineNum: 7, highlight: true}, - {type: 'CODE', content: '', intraline: [], lineNum: 8 }, - {type: 'COMMENT_THREAD', comments: [ { line: 8 }]}, - {type: 'CODE', content: '', intraline: [], lineNum: 9 }, - {type: 'CODE', content: '', intraline: [], lineNum: 10, - highlight: true}, - ]; - - var scrollToLineStub = sinon.stub(element, 'scrollToLine'); - element.scrollToNextDiffChunk(); - assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(6)); - element.scrollToPreviousDiffChunk(); - assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(1)); - element.scrollToNextCommentThread(); - assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(4)); - element.scrollToNextCommentThread(); - assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(8)); - element.scrollToPreviousDiffChunk(); - assert.isTrue(scrollToLineStub.lastCall.calledWithExactly(6)); - - scrollToLineStub.restore(); - }); - }); -</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html index 0dc18ae..e7ec202 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html +++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -162,7 +162,6 @@ </h3> <gr-diff id="diff" change-num="[[_changeNum]]" - prefs="{{prefs}}" patch-range="[[_patchRange]]" path="[[_path]]" project-config="[[_projectConfig]]"
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js index 557c213..190a286 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js +++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -26,10 +26,6 @@ */ properties: { - prefs: { - type: Object, - notify: true, - }, /** * URL params passed from the router. */
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-side-by-side.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-side-by-side.js similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-side-by-side.js rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-side-by-side.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-unified.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-unified.js similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder-unified.js rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder-unified.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js similarity index 98% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js index e49a0a3..338845e 100644 --- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder.js +++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder.js
@@ -32,7 +32,7 @@ GrDiffBuilder.TAB_REGEX = /\t/g; GrDiffBuilder.LINE_FEED_HTML = - '<span class="style-scope gr-new-diff br"></span>'; + '<span class="style-scope gr-diff br"></span>'; GrDiffBuilder.GroupType = { ADDED: 'b', @@ -509,7 +509,7 @@ GrDiffBuilder.prototype._addIntralineHighlights = function(content, html, highlights) { - var START_TAG = '<hl class="style-scope gr-new-diff">'; + var START_TAG = '<hl class="style-scope gr-diff">'; var END_TAG = '</hl>'; for (var i = 0; i < highlights.length; i++) { @@ -549,7 +549,7 @@ throw Error('Invalid tab size from preferences.'); } - var str = '<span class="style-scope gr-new-diff tab '; + var str = '<span class="style-scope gr-diff tab '; if (showTabs) { str += 'withIndicator'; } @@ -569,7 +569,7 @@ // Since the Polymer DOM utility functions (which would do this // automatically) are not being used for performance reasons, this is // done manually. - el.classList.add('style-scope', 'gr-new-diff'); + el.classList.add('style-scope', 'gr-diff'); if (!!className) { el.classList.add(className); }
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder_test.html similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-builder_test.html rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-builder_test.html
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group.js rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group.js
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-group_test.html rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-line.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js similarity index 100% rename from polygerrit-ui/app/elements/diff/gr-new-diff/gr-diff-line.js rename to polygerrit-ui/app/elements/diff/gr-diff/gr-diff-line.js
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html index a6d92a2..e0306ad 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html +++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.html
@@ -20,13 +20,19 @@ <link rel="import" href="../../shared/gr-request/gr-request.html"> <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> +<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html"> <link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html"> -<link rel="import" href="../gr-diff-side/gr-diff-side.html"> <link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html"> <dom-module id="gr-diff"> <template> <style> + :host { + --light-remove-highlight-color: #fee; + --dark-remove-highlight-color: #ffd4d4; + --light-add-highlight-color: #efe; + --dark-add-highlight-color: #d4ffd4; + } .loading { padding: 0 var(--default-horizontal-margin) 1em; color: #666; @@ -45,16 +51,99 @@ display: flex; font: 12px var(--monospace-font-family); overflow-x: auto; + will-change: transform; } - gr-diff-side:first-of-type { - --light-highlight-color: #fee; - --dark-highlight-color: #ffd4d4; - } - gr-diff-side:last-of-type { - --light-highlight-color: #efe; - --dark-highlight-color: #d4ffd4; + table { + border-collapse: collapse; border-right: 1px solid #ddd; } + .section { + background-color: #eee; + } + .blank, + .content { + background-color: #fff; + } + .lineNum, + .content { + vertical-align: top; + white-space: pre; + } + .contextLineNum:before, + .lineNum:before { + display: inline-block; + color: #666; + content: attr(data-value); + padding: 0 .75em; + text-align: right; + width: 100%; + } + .canComment .lineNum[data-value] { + cursor: pointer; + } + .canComment .lineNum[data-value]:before { + text-decoration: underline; + } + .canComment .lineNum[data-value]:hover:before { + background-color: #ccc; + } + .canComment .lineNum[data-value="FILE"]:before { + content: 'File'; + } + .content { + overflow: hidden; + min-width: var(--content-width, 80ch); + } + .content.left { + -webkit-user-select: var(--left-user-select, text); + -moz-user-select: var(--left-user-select, text); + -ms-user-select: var(--left-user-select, text); + user-select: var(--left-user-select, text); + } + .content.right { + -webkit-user-select: var(--right-user-select, text); + -moz-user-select: var(--right-user-select, text); + -ms-user-select: var(--right-user-select, text); + user-select: var(--right-user-select, text); + } + .content.add hl, + .content.add.darkHighlight { + background-color: var(--dark-add-highlight-color); + } + .content.add.lightHighlight { + background-color: var(--light-add-highlight-color); + } + .content.remove hl, + .content.remove.darkHighlight { + background-color: var(--dark-remove-highlight-color); + } + .content.remove.lightHighlight { + background-color: var(--light-remove-highlight-color); + } + .contextControl { + color: #849; + background-color: #fef; + } + .contextControl gr-button { + display: block; + font-family: var(--monospace-font-family); + text-decoration: none; + } + .contextControl td:not(.lineNum) { + text-align: center; + } + .br:after { + /* Line feed */ + content: '\A'; + } + .tab { + display: inline-block; + } + .tab.withIndicator:before { + color: #C62828; + /* >> character */ + content: '\00BB'; + } </style> <div class="loading" hidden$="[[!_loading]]">Loading...</div> <div hidden$="[[_loading]]" hidden> @@ -67,44 +156,29 @@ <gr-button link class="prefsButton" on-tap="_handlePrefsTap" - hidden$="[[!prefs]]" + hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]" hidden>Diff View Preferences</gr-button> </div> <gr-overlay id="prefsOverlay" with-backdrop> <gr-diff-preferences - prefs="{{prefs}}" + prefs="{{_prefs}}" on-save="_handlePrefsSave" on-cancel="_handlePrefsCancel"></gr-diff-preferences> </gr-overlay> - <div class="diffContainer"> - <gr-diff-side id="leftDiff" - change-num="[[changeNum]]" - patch-num="[[patchRange.basePatchNum]]" - path="[[path]]" - content="{{_diff.leftSide}}" - prefs="[[prefs]]" - can-comment="[[_loggedIn]]" - project-config="[[projectConfig]]" - on-expand-context="_handleExpandContext" - on-thread-height-change="_handleThreadHeightChange" - on-add-draft="_handleAddDraft" - on-remove-thread="_handleRemoveThread"></gr-diff-side> - <gr-diff-side id="rightDiff" - change-num="[[changeNum]]" - patch-num="[[patchRange.patchNum]]" - path="[[path]]" - content="{{_diff.rightSide}}" - prefs="[[prefs]]" - can-comment="[[_loggedIn]]" - project-config="[[projectConfig]]" - on-expand-context="_handleExpandContext" - on-thread-height-change="_handleThreadHeightChange" - on-add-draft="_handleAddDraft" - on-remove-thread="_handleRemoveThread"></gr-diff-side> + <div class$="[[_computeContainerClass(_loggedIn, _viewMode)]]" + on-tap="_handleTap" + on-mousedown="_handleMouseDown" + on-copy="_handleCopy"> + <table id="diffTable"></table> </div> </div> <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> </template> + <script src="gr-diff-line.js"></script> + <script src="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.js"></script> </dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js index cac1f3e..12ceacb 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js +++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.js
@@ -14,6 +14,16 @@ (function() { 'use strict'; + var DiffViewMode = { + SIDE_BY_SIDE: 'SIDE_BY_SIDE', + UNIFIED: 'UNIFIED_DIFF', + }; + + var DiffSide = { + LEFT: 'left', + RIGHT: 'right', + }; + Polymer({ is: 'gr-diff', @@ -26,148 +36,379 @@ properties: { availablePatches: Array, changeNum: String, - /* - * A single object to encompass basePatchNum and patchNum is used - * so that both can be set at once without incremental observers - * firing after each property changes. - */ patchRange: Object, path: String, - prefs: { - type: Object, - notify: true, - }, - projectConfig: Object, - _prefsReady: { + projectConfig: { type: Object, - readOnly: true, - value: function() { - return new Promise(function(resolve) { - this._resolvePrefsReady = resolve; - }.bind(this)); - }, + observer: '_projectConfigChanged', }, - _baseComments: Array, - _comments: Array, - _drafts: Array, - _baseDrafts: Array, - /** - * Base (left side) comments and drafts grouped by line number. - * Only used for initial rendering. - */ - _groupedBaseComments: { - type: Object, - value: function() { return {}; }, - }, - /** - * Comments and drafts (right side) grouped by line number. - * Only used for initial rendering. - */ - _groupedComments: { - type: Object, - value: function() { return {}; }, - }, - _diffResponse: Object, - _diff: { - type: Object, - value: function() { return {}; }, - }, + _loggedIn: { type: Boolean, value: false, }, - _initialRenderComplete: { - type: Boolean, - value: false, - }, _loading: { type: Boolean, value: true, }, - _savedPrefs: Object, - - _diffPreferencesPromise: Object, // Used for testing. + _viewMode: { + type: String, + value: DiffViewMode.SIDE_BY_SIDE, + }, + _diff: Object, + _diffBuilder: Object, + _prefs: Object, + _selectionSide: { + type: String, + observer: '_selectionSideChanged', + }, + _comments: Object, + _focusedSection: { + type: Number, + value: -1, + }, + _focusedThread: { + type: Number, + value: -1, + }, }, observers: [ - '_prefsChanged(prefs.*)', + '_prefsChanged(_prefs.*)', ], - ready: function() { - app.accountReady.then(function() { - this._loggedIn = app.loggedIn; + attached: function() { + this._getLoggedIn().then(function(loggedIn) { + this._loggedIn = loggedIn; }.bind(this)); - }, - scrollToLine: function(lineNum) { - // TODO(andybons): Should this always be the right side? - this.$.rightDiff.scrollToLine(lineNum); - }, - - scrollToNextDiffChunk: function() { - this.$.rightDiff.scrollToNextDiffChunk(); - }, - - scrollToPreviousDiffChunk: function() { - this.$.rightDiff.scrollToPreviousDiffChunk(); - }, - - scrollToNextCommentThread: function() { - this.$.rightDiff.scrollToNextCommentThread(); - }, - - scrollToPreviousCommentThread: function() { - this.$.rightDiff.scrollToPreviousCommentThread(); + this.addEventListener('thread-discard', + this._handleThreadDiscard.bind(this)); + this.addEventListener('comment-discard', + this._handleCommentDiscard.bind(this)); }, reload: function() { + this._clearDiffContent(); this._loading = true; - // If a diff takes a considerable amount of time to render, the previous - // diff can end up showing up while the DOM is constructed. Clear the - // content on a reload to prevent this. - this._diff = { - leftSide: [], - rightSide: [], - }; - var diffLoaded = this._getDiff().then(function(diff) { - this._diffResponse = diff; + var promises = []; + + promises.push(this._getDiff().then(function(diff) { + this._diff = diff; + this._loading = false; + }.bind(this))); + + promises.push(this._getDiffCommentsAndDrafts().then(function(comments) { + this._comments = comments; + }.bind(this))); + + promises.push(this._getDiffPreferences().then(function(prefs) { + this._prefs = prefs; + }.bind(this))); + + return Promise.all(promises).then(function() { + this._render(); }.bind(this)); + }, - var promises = [ - this._prefsReady, - diffLoaded, - ]; + showDiffPreferences: function() { + this.$.prefsOverlay.open(); + }, - return app.accountReady.then(function() { - promises.push(this._getDiffComments().then(function(res) { - this._baseComments = res.baseComments; - this._comments = res.comments; - }.bind(this))); + scrollToLine: function(lineNum) { + if (isNaN(lineNum) || lineNum < 1) { return; } - if (!app.loggedIn) { - this._baseDrafts = []; - this._drafts = []; - } else { - promises.push(this._getDiffDrafts().then(function(res) { - this._baseDrafts = res.baseComments; - this._drafts = res.comments; - }.bind(this))); - } + var lineEls = Polymer.dom(this.root).querySelectorAll( + '.lineNum[data-value="' + lineNum + '"]'); - return Promise.all(promises).then(function() { - this._render(); - this._loading = false; - }.bind(this)).catch(function(err) { - this._loading = false; - alert('Oops. Something went wrong. Check the console and bug the ' + + // Always choose the right side. + var el = lineEls.length === 2 ? lineEls[1] : lineEls[0]; + this._scrollToElement(el); + }, + + scrollToNextDiffChunk: function() { + this._focusedSection = this._advanceElementWithinNodeList( + this._getDeltaSections(), this._focusedSection, 1); + }, + + scrollToPreviousDiffChunk: function() { + this._focusedSection = this._advanceElementWithinNodeList( + this._getDeltaSections(), this._focusedSection, -1); + }, + + scrollToNextCommentThread: function() { + this._focusedThread = this._advanceElementWithinNodeList( + this._getCommentThreads(), this._focusedThread, 1); + }, + + scrollToPreviousCommentThread: function() { + this._focusedThread = this._advanceElementWithinNodeList( + this._getCommentThreads(), this._focusedThread, -1); + }, + + _advanceElementWithinNodeList: function(els, curIndex, direction) { + var idx = Math.max(0, Math.min(els.length - 1, curIndex + direction)); + if (curIndex !== idx) { + this._scrollToElement(els[idx]); + return idx; + } + return curIndex; + }, + + _getCommentThreads: function() { + return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread'); + }, + + _getDeltaSections: function() { + return Polymer.dom(this.root).querySelectorAll('.section.delta'); + }, + + _scrollToElement: function(el) { + if (!el) { return; } + + // Calculate where the element is relative to the window. + var top = el.offsetTop; + for (var offsetParent = el.offsetParent; + offsetParent; + offsetParent = offsetParent.offsetParent) { + top += offsetParent.offsetTop; + } + + // Scroll the element to the middle of the window. Dividing by a third + // instead of half the inner height feels a bit better otherwise the + // element appears to be below the center of the window even when it + // isn't. + window.scrollTo(0, top - (window.innerHeight / 3) + + (el.offsetHeight / 2)); + }, + + _computeContainerClass: function(loggedIn, viewMode) { + var classes = ['diffContainer']; + switch (viewMode) { + case DiffViewMode.UNIFIED: + classes.push('unified'); + break; + case DiffViewMode.SIDE_BY_SIDE: + classes.push('sideBySide'); + break + default: + throw Error('Invalid view mode: ', viewMode); + } + if (loggedIn) { + classes.push('canComment'); + } + return classes.join(' '); + }, + + _computePrefsButtonHidden: function(prefs, loggedIn) { + return !loggedIn || !prefs; + }, + + _handlePrefsTap: function(e) { + e.preventDefault(); + this.$.prefsOverlay.open(); + }, + + _handlePrefsSave: function(e) { + e.stopPropagation(); + var el = Polymer.dom(e).rootTarget; + el.disabled = true; + this._saveDiffPreferences().then(function() { + this.$.prefsOverlay.close(); + el.disabled = false; + }.bind(this)).catch(function(err) { + el.disabled = false; + alert('Oops. Something went wrong. Check the console and bug the ' + 'PolyGerrit team for assistance.'); - throw err; - }.bind(this)); + throw err; + }); + }, + + _saveDiffPreferences: function() { + return this.$.restAPI.saveDiffPreferences(this._prefs); + }, + + _handlePrefsCancel: function(e) { + e.stopPropagation(); + this.$.prefsOverlay.close(); + }, + + _handleTap: function(e) { + var el = Polymer.dom(e).rootTarget; + + if (el.classList.contains('showContext')) { + this._showContext(e.detail.group, e.detail.section); + } else if (el.classList.contains('lineNum')) { + this._handleLineTap(el); + } + }, + + _handleLineTap: function(el) { + this._getLoggedIn().then(function(loggedIn) { + if (!loggedIn) { return; } + + var value = el.getAttribute('data-value'); + if (value === GrDiffLine.FILE) { + this._addDraft(el); + return; + } + var lineNum = parseInt(value, 10); + if (isNaN(lineNum)) { + throw Error('Invalid line number: ' + value); + } + this._addDraft(el, lineNum); }.bind(this)); }, + _addDraft: function(lineEl, opt_lineNum) { + var threadEl; + + // Does a thread already exist at this line? + var contentEl = lineEl.nextSibling; + while (contentEl && !contentEl.classList.contains('content')) { + contentEl = contentEl.nextSibling; + } + if (contentEl.childNodes.length > 0 && + contentEl.lastChild.nodeName === 'GR-DIFF-COMMENT-THREAD') { + threadEl = contentEl.lastChild; + } else { + var patchNum = this.patchRange.patchNum; + var side = 'REVISION'; + if (contentEl.classList.contains(DiffSide.LEFT) || + contentEl.classList.contains('remove')) { + if (this.patchRange.basePatchNum === 'PARENT') { + side = 'PARENT'; + } else { + patchNum = this.patchRange.basePatchNum; + } + } + threadEl = this._builder.createCommentThread(this.changeNum, patchNum, + this.path, side, this.projectConfig); + contentEl.appendChild(threadEl); + } + threadEl.addDraft(opt_lineNum); + }, + + _handleThreadDiscard: function(e) { + var el = Polymer.dom(e).rootTarget; + el.parentNode.removeChild(el); + }, + + _handleCommentDiscard: function(e) { + var comment = Polymer.dom(e).rootTarget.comment; + this._removeComment(comment); + }, + + _removeComment: function(comment) { + if (!comment.id) { return; } + this._removeCommentFromSide(comment, DiffSide.LEFT) || + this._removeCommentFromSide(comment, DiffSide.RIGHT); + }, + + _removeCommentFromSide: function(comment, side) { + var idx = -1; + for (var i = 0; i < this._comments[side].length; i++) { + if (this._comments[side][i].id === comment.id) { + idx = i; + break; + } + } + if (idx !== -1) { + this.splice('_comments.' + side, idx, 1); + return true; + } + return false; + }, + + _handleMouseDown: function(e) { + var el = Polymer.dom(e).rootTarget; + var side; + for (var node = el; node != null; node = node.parentNode) { + if (!node.classList) { continue; } + + if (node.classList.contains(DiffSide.LEFT)) { + side = DiffSide.LEFT; + break; + } else if (node.classList.contains(DiffSide.RIGHT)) { + side = DiffSide.RIGHT; + break; + } + } + this._selectionSide = side; + }, + + _selectionSideChanged: function(side) { + if (side) { + var oppositeSide = side === DiffSide.RIGHT ? + DiffSide.LEFT : DiffSide.RIGHT; + this.customStyle['--' + side + '-user-select'] = 'text'; + this.customStyle['--' + oppositeSide + '-user-select'] = 'none'; + } else { + this.customStyle['--left-user-select'] = 'text'; + this.customStyle['--right-user-select'] = 'text'; + } + this.updateStyles(); + }, + + _handleCopy: function(e) { + var text = this._getSelectedText(this._selectionSide); + e.clipboardData.setData('Text', text); + e.preventDefault(); + }, + + _getSelectedText: function(opt_side) { + var sel = window.getSelection(); + var range = sel.getRangeAt(0); + var doc = range.cloneContents(); + var selector = '.content'; + if (opt_side) { + selector += '.' + opt_side; + } + var contentEls = Polymer.dom(doc).querySelectorAll(selector); + + if (contentEls.length === 0) { + return doc.textContent; + } + + var text = ''; + for (var i = 0; i < contentEls.length; i++) { + text += contentEls[i].textContent + '\n'; + } + return text; + }, + + _showContext: function(group, sectionEl) { + this._builder.emitGroup(group, sectionEl); + sectionEl.parentNode.removeChild(sectionEl); + }, + + _prefsChanged: function(prefsChangeRecord) { + var prefs = prefsChangeRecord.base; + this.customStyle['--content-width'] = prefs.line_length + 'ch'; + this.updateStyles(); + + if (this._diff && this._comments) { + this._render(); + } + }, + + _render: function() { + this._clearDiffContent(); + this._builder = this._getDiffBuilder(this._diff, this._comments, + this._prefs); + this._builder.emitDiff(this._diff.content); + + this.async(function() { + this.fire('render', null, {bubbles: false}); + }.bind(this), 1); + }, + + _clearDiffContent: function() { + this.$.diffTable.innerHTML = null; + }, + _getDiff: function() { return this.$.restAPI.getDiff( this.changeNum, @@ -185,528 +426,95 @@ }, _getDiffDrafts: function() { - return this.$.restAPI.getDiffDrafts( - this.changeNum, - this.patchRange.basePatchNum, - this.patchRange.patchNum, - this.path); - }, - - showDiffPreferences: function() { - this.$.prefsOverlay.open(); - }, - - _prefsChanged: function(changeRecord) { - if (this._initialRenderComplete) { - this._render(); - } - this._resolvePrefsReady(changeRecord.base); - }, - - _render: function() { - this._groupCommentsAndDrafts(); - this._processContent(); - - // Allow for the initial rendering to complete before firing the event. - this.async(function() { - this.fire('render', null, {bubbles: false}); - }.bind(this), 1); - - this._initialRenderComplete = true; - }, - - _handlePrefsTap: function(e) { - e.preventDefault(); - - // TODO(andybons): This is not supported in IE. Implement a polyfill. - // NOTE: Object.assign is NOT automatically a deep copy. If prefs adds - // an object as a value, it must be marked enumerable. - this._savedPrefs = Object.assign({}, this.prefs); - this.$.prefsOverlay.open(); - }, - - _handlePrefsSave: function(e) { - e.stopPropagation(); - var el = Polymer.dom(e).rootTarget; - el.disabled = true; - app.accountReady.then(function() { - if (!this._loggedIn) { - el.disabled = false; - this.$.prefsOverlay.close(); - return; + return this._getLoggedIn().then(function(loggedIn) { + if (!loggedIn) { + return Promise.resolve({baseComments: [], comments: []}); } - this._saveDiffPreferences().then(function() { - this.$.prefsOverlay.close(); - el.disabled = false; - }.bind(this)).catch(function(err) { - el.disabled = false; - alert('Oops. Something went wrong. Check the console and bug the ' + - 'PolyGerrit team for assistance.'); - throw err; - }); + return this.$.restAPI.getDiffDrafts( + this.changeNum, + this.patchRange.basePatchNum, + this.patchRange.patchNum, + this.path); }.bind(this)); }, - _saveDiffPreferences: function() { - var xhr = document.createElement('gr-request'); - this._diffPreferencesPromise = xhr.send({ - method: 'PUT', - url: '/accounts/self/preferences.diff', - body: this.prefs, - }); - return this._diffPreferencesPromise; + _getDiffCommentsAndDrafts: function() { + var promises = []; + promises.push(this._getDiffComments()); + promises.push(this._getDiffDrafts()); + return Promise.all(promises).then(function(results) { + return Promise.resolve({ + comments: results[0], + drafts: results[1], + }); + }).then(this._normalizeDiffCommentsAndDrafts.bind(this)); }, - _handlePrefsCancel: function(e) { - e.stopPropagation(); - this.prefs = this._savedPrefs; - this.$.prefsOverlay.close(); - }, - - _handleExpandContext: function(e) { - var ctx = e.detail.context; - var contextControlIndex = -1; - for (var i = ctx.start; i <= ctx.end; i++) { - this._diff.leftSide[i].hidden = false; - this._diff.rightSide[i].hidden = false; - if (this._diff.leftSide[i].type == 'CONTEXT_CONTROL' && - this._diff.rightSide[i].type == 'CONTEXT_CONTROL') { - contextControlIndex = i; + _getDiffPreferences: function() { + return this._getLoggedIn().then(function(loggedIn) { + if (!loggedIn) { + // These defaults should match the defaults in + // gerrit-extension-api/src/main/jcg/gerrit/extensions/client/DiffPreferencesInfo.java + // NOTE: There are some settings that don't apply to PolyGerrit + // (Render mode being at least one of them). + return Promise.resolve({ + auto_hide_diff_table_header: true, + context: 10, + cursor_blink_rate: 0, + ignore_whitespace: 'IGNORE_NONE', + intraline_difference: true, + line_length: 100, + show_line_endings: true, + show_tabs: true, + show_whitespace_errors: true, + syntax_highlighting: true, + tab_size: 8, + theme: 'DEFAULT', + }); } - } - this._diff.leftSide[contextControlIndex].hidden = true; - this._diff.rightSide[contextControlIndex].hidden = true; - - this.$.leftDiff.hideElementsWithIndex(contextControlIndex); - this.$.rightDiff.hideElementsWithIndex(contextControlIndex); - - this.$.leftDiff.renderLineIndexRange(ctx.start, ctx.end); - this.$.rightDiff.renderLineIndexRange(ctx.start, ctx.end); + return this.$.restAPI.getDiffPreferences(); + }.bind(this)); }, - _handleThreadHeightChange: function(e) { - var index = e.detail.index; - var diffEl = Polymer.dom(e).rootTarget; - var otherSide = diffEl == this.$.leftDiff ? - this.$.rightDiff : this.$.leftDiff; - - var threadHeight = e.detail.height; - var otherSideHeight; - if (otherSide.content[index].type == 'COMMENT_THREAD') { - otherSideHeight = otherSide.getRowNaturalHeight(index); - } else { - otherSideHeight = otherSide.getRowHeight(index); + _normalizeDiffCommentsAndDrafts: function(results) { + function markAsDraft(d) { + d.__draft = true; + return d; } - var maxHeight = Math.max(threadHeight, otherSideHeight); - this.$.leftDiff.setRowHeight(index, maxHeight); - this.$.rightDiff.setRowHeight(index, maxHeight); - }, - - _handleAddDraft: function(e) { - var insertIndex = e.detail.index + 1; - var diffEl = Polymer.dom(e).rootTarget; - var content = diffEl.content; - if (content[insertIndex] && - content[insertIndex].type == 'COMMENT_THREAD') { - // A thread is already here. Do nothing. - return; - } - var comment = { - type: 'COMMENT_THREAD', - comments: [{ - __draft: true, - __draftID: Math.random().toString(36), - line: e.detail.line, + var baseDrafts = results.drafts.baseComments.map(markAsDraft); + var drafts = results.drafts.comments.map(markAsDraft); + return Promise.resolve({ + meta: { path: this.path, - }] - }; - if (diffEl == this.$.leftDiff && - this.patchRange.basePatchNum == 'PARENT') { - comment.comments[0].side = 'PARENT'; - comment.patchNum = this.patchRange.patchNum; - } - - if (content[insertIndex] && - content[insertIndex].type == 'FILLER') { - content[insertIndex] = comment; - diffEl.rowUpdated(insertIndex); - } else { - content.splice(insertIndex, 0, comment); - diffEl.rowInserted(insertIndex); - } - - var otherSide = diffEl == this.$.leftDiff ? - this.$.rightDiff : this.$.leftDiff; - if (otherSide.content[insertIndex] == null || - otherSide.content[insertIndex].type != 'COMMENT_THREAD') { - otherSide.content.splice(insertIndex, 0, { - type: 'FILLER', - }); - otherSide.rowInserted(insertIndex); - } - }, - - _handleRemoveThread: function(e) { - var diffEl = Polymer.dom(e).rootTarget; - var otherSide = diffEl == this.$.leftDiff ? - this.$.rightDiff : this.$.leftDiff; - var index = e.detail.index; - - if (otherSide.content[index].type == 'FILLER') { - otherSide.content.splice(index, 1); - otherSide.rowRemoved(index); - diffEl.content.splice(index, 1); - diffEl.rowRemoved(index); - } else if (otherSide.content[index].type == 'COMMENT_THREAD') { - diffEl.content[index] = {type: 'FILLER'}; - diffEl.rowUpdated(index); - var height = otherSide.setRowNaturalHeight(index); - diffEl.setRowHeight(index, height); - } else { - throw Error('A thread cannot be opposite anything but filler or ' + - 'another thread'); - } - }, - - _processContent: function() { - var leftSide = []; - var rightSide = []; - var initialLineNum = 0 + (this._diffResponse.content.skip || 0); - var ctx = { - hidingLines: false, - lastNumLinesHidden: 0, - left: { - lineNum: initialLineNum, + changeNum: this.changeNum, + patchRange: this.patchRange, + projectConfig: this.projectConfig, }, - right: { - lineNum: initialLineNum, - } - }; - var content = this._breakUpCommonChunksWithComments(ctx, - this._diffResponse.content); - var context = this.prefs.context; - if (context == -1) { - // Show the entire file. - context = Infinity; - } - for (var i = 0; i < content.length; i++) { - if (i == 0) { - ctx.skipRange = [0, context]; - } else if (i == content.length - 1) { - ctx.skipRange = [context, 0]; - } else { - ctx.skipRange = [context, context]; - } - ctx.diffChunkIndex = i; - this._addDiffChunk(ctx, content[i], leftSide, rightSide); - } - - this._diff = { - leftSide: leftSide, - rightSide: rightSide, - }; + left: results.comments.baseComments.concat(baseDrafts), + right: results.comments.comments.concat(drafts), + }); }, - // In order to show comments out of the bounds of the selected context, - // treat them as diffs within the model so that the content (and context - // surrounding it) renders correctly. - _breakUpCommonChunksWithComments: function(ctx, content) { - var result = []; - var leftLineNum = ctx.left.lineNum; - var rightLineNum = ctx.right.lineNum; - for (var i = 0; i < content.length; i++) { - if (!content[i].ab) { - result.push(content[i]); - if (content[i].a) { - leftLineNum += content[i].a.length; - } - if (content[i].b) { - rightLineNum += content[i].b.length; - } - continue; - } - var chunk = content[i].ab; - var currentChunk = {ab: []}; - for (var j = 0; j < chunk.length; j++) { - leftLineNum++; - rightLineNum++; - if (this._groupedBaseComments[leftLineNum] == null && - this._groupedComments[rightLineNum] == null) { - currentChunk.ab.push(chunk[j]); - } else { - if (currentChunk.ab && currentChunk.ab.length > 0) { - result.push(currentChunk); - currentChunk = {ab: []}; - } - // Append an annotation to indicate that this line should not be - // highlighted even though it's implied with both `a` and `b` - // defined. This is needed since there may be two lines that - // should be highlighted but are equal (blank lines, for example). - result.push({ - __noHighlight: true, - a: [chunk[j]], - b: [chunk[j]], - }); - } - } - if (currentChunk.ab != null && currentChunk.ab.length > 0) { - result.push(currentChunk); - } - } - return result; + _getLoggedIn: function() { + return this.$.restAPI.getLoggedIn(); }, - _groupCommentsAndDrafts: function() { - this._baseDrafts.forEach(function(d) { d.__draft = true; }); - this._drafts.forEach(function(d) { d.__draft = true; }); - var allLeft = this._baseComments.concat(this._baseDrafts); - var allRight = this._comments.concat(this._drafts); - - var leftByLine = {}; - var rightByLine = {}; - var mapFunc = function(byLine) { - return function(c) { - // File comments/drafts are grouped with line 1 for now. - var line = c.line || 1; - if (byLine[line] == null) { - byLine[line] = []; - } - byLine[line].push(c); - }; - }; - allLeft.forEach(mapFunc(leftByLine)); - allRight.forEach(mapFunc(rightByLine)); - - this._groupedBaseComments = leftByLine; - this._groupedComments = rightByLine; + _getDiffBuilder: function(diff, comments, prefs) { + if (this._viewMode === DiffViewMode.SIDE_BY_SIDE) { + return new GrDiffBuilderSideBySide(diff, comments, prefs, + this.$.diffTable); + } else if (this._viewMode === DiffViewMode.UNIFIED) { + return new GrDiffBuilderUnified(diff, comments, prefs, + this.$.diffTable); + } + throw Error('Unsupported diff view mode: ' + this._viewMode); }, - _addContextControl: function(ctx, leftSide, rightSide) { - var numLinesHidden = ctx.lastNumLinesHidden; - var leftStart = leftSide.length - numLinesHidden; - var leftEnd = leftSide.length; - var rightStart = rightSide.length - numLinesHidden; - var rightEnd = rightSide.length; - if (leftStart != rightStart || leftEnd != rightEnd) { - throw Error( - 'Left and right ranges for context control should be equal:' + - 'Left: [' + leftStart + ', ' + leftEnd + '] ' + - 'Right: [' + rightStart + ', ' + rightEnd + ']'); + _projectConfigChanged: function(projectConfig) { + var threadEls = this._getCommentThreads(); + for (var i = 0; i < threadEls.length; i++) { + threadEls[i].projectConfig = projectConfig; } - var obj = { - type: 'CONTEXT_CONTROL', - numLines: numLinesHidden, - start: leftStart, - end: leftEnd, - }; - // NOTE: Be careful, here. This object is meant to be immutable. If the - // object is altered within one side's array it will reflect the - // alterations in another. - leftSide.push(obj); - rightSide.push(obj); - }, - - _addCommonDiffChunk: function(ctx, chunk, leftSide, rightSide) { - for (var i = 0; i < chunk.ab.length; i++) { - var numLines = Math.ceil( - this._visibleLineLength(chunk.ab[i]) / this.prefs.line_length); - var hidden = i >= ctx.skipRange[0] && - i < chunk.ab.length - ctx.skipRange[1]; - if (ctx.hidingLines && hidden == false) { - // No longer hiding lines. Add a context control. - this._addContextControl(ctx, leftSide, rightSide); - ctx.lastNumLinesHidden = 0; - } - ctx.hidingLines = hidden; - if (hidden) { - ctx.lastNumLinesHidden++; - } - - // Blank lines within a diff content array indicate a newline. - leftSide.push({ - type: 'CODE', - hidden: hidden, - content: chunk.ab[i] || '\n', - numLines: numLines, - lineNum: ++ctx.left.lineNum, - }); - rightSide.push({ - type: 'CODE', - hidden: hidden, - content: chunk.ab[i] || '\n', - numLines: numLines, - lineNum: ++ctx.right.lineNum, - }); - - this._addCommentsIfPresent(ctx, leftSide, rightSide); - } - if (ctx.lastNumLinesHidden > 0) { - this._addContextControl(ctx, leftSide, rightSide); - } - }, - - _addDiffChunk: function(ctx, chunk, leftSide, rightSide) { - if (chunk.ab) { - this._addCommonDiffChunk(ctx, chunk, leftSide, rightSide); - return; - } - - var leftHighlights = []; - if (chunk.edit_a) { - leftHighlights = - this._normalizeIntralineHighlights(chunk.a, chunk.edit_a); - } - var rightHighlights = []; - if (chunk.edit_b) { - rightHighlights = - this._normalizeIntralineHighlights(chunk.b, chunk.edit_b); - } - - var aLen = (chunk.a && chunk.a.length) || 0; - var bLen = (chunk.b && chunk.b.length) || 0; - var maxLen = Math.max(aLen, bLen); - for (var i = 0; i < maxLen; i++) { - var hasLeftContent = chunk.a && i < chunk.a.length; - var hasRightContent = chunk.b && i < chunk.b.length; - var leftContent = hasLeftContent ? chunk.a[i] : ''; - var rightContent = hasRightContent ? chunk.b[i] : ''; - var highlight = !chunk.__noHighlight; - var maxNumLines = this._maxLinesSpanned(leftContent, rightContent); - if (hasLeftContent) { - leftSide.push({ - type: 'CODE', - content: leftContent || '\n', - numLines: maxNumLines, - lineNum: ++ctx.left.lineNum, - highlight: highlight, - intraline: highlight && leftHighlights.filter(function(hl) { - return hl.contentIndex == i; - }), - }); - } else { - leftSide.push({ - type: 'FILLER', - numLines: maxNumLines, - }); - } - if (hasRightContent) { - rightSide.push({ - type: 'CODE', - content: rightContent || '\n', - numLines: maxNumLines, - lineNum: ++ctx.right.lineNum, - highlight: highlight, - intraline: highlight && rightHighlights.filter(function(hl) { - return hl.contentIndex == i; - }), - }); - } else { - rightSide.push({ - type: 'FILLER', - numLines: maxNumLines, - }); - } - this._addCommentsIfPresent(ctx, leftSide, rightSide); - } - }, - - _addCommentsIfPresent: function(ctx, leftSide, rightSide) { - var leftComments = this._groupedBaseComments[ctx.left.lineNum]; - var rightComments = this._groupedComments[ctx.right.lineNum]; - if (leftComments) { - var thread = { - type: 'COMMENT_THREAD', - comments: leftComments, - }; - if (this.patchRange.basePatchNum == 'PARENT') { - thread.patchNum = this.patchRange.patchNum; - } - leftSide.push(thread); - } - if (rightComments) { - rightSide.push({ - type: 'COMMENT_THREAD', - comments: rightComments, - }); - } - if (leftComments && !rightComments) { - rightSide.push({type: 'FILLER'}); - } else if (!leftComments && rightComments) { - leftSide.push({type: 'FILLER'}); - } - this._groupedBaseComments[ctx.left.lineNum] = null; - this._groupedComments[ctx.right.lineNum] = null; - }, - - // The `highlights` array consists of a list of <skip length, mark length> - // pairs, where the skip length is the number of characters between the - // end of the previous edit and the start of this edit, and the mark - // length is the number of edited characters following the skip. The start - // of the edits is from the beginning of the related diff content lines. - // - // Note that the implied newline character at the end of each line is - // included in the length calculation, and thus it is possible for the - // edits to span newlines. - // - // A line highlight object consists of three fields: - // - contentIndex: The index of the diffChunk `content` field (the line - // being referred to). - // - startIndex: Where the highlight should begin. - // - endIndex: (optional) Where the highlight should end. If omitted, the - // highlight is meant to be a continuation onto the next line. - _normalizeIntralineHighlights: function(content, highlights) { - var contentIndex = 0; - var idx = 0; - var normalized = []; - for (var i = 0; i < highlights.length; i++) { - var line = content[contentIndex] + '\n'; - var hl = highlights[i]; - var j = 0; - while (j < hl[0]) { - if (idx == line.length) { - idx = 0; - line = content[++contentIndex] + '\n'; - continue; - } - idx++; - j++; - } - var lineHighlight = { - contentIndex: contentIndex, - startIndex: idx, - }; - - j = 0; - while (line && j < hl[1]) { - if (idx == line.length) { - idx = 0; - line = content[++contentIndex] + '\n'; - normalized.push(lineHighlight); - lineHighlight = { - contentIndex: contentIndex, - startIndex: idx, - }; - continue; - } - idx++; - j++; - } - lineHighlight.endIndex = idx; - normalized.push(lineHighlight); - } - return normalized; - }, - - _visibleLineLength: function(contents) { - // http://jsperf.com/performance-of-match-vs-split - var numTabs = contents.split('\t').length - 1; - return contents.length - numTabs + (this.prefs.tab_size * numTabs); - }, - - _maxLinesSpanned: function(left, right) { - return Math.max( - Math.ceil(this._visibleLineLength(left) / this.prefs.line_length), - Math.ceil(this._visibleLineLength(right) / this.prefs.line_length)); }, }); })();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html index e46bbb8..579957e 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html +++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -20,8 +20,6 @@ <script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script> <script src="../../../bower_components/web-component-tester/browser.js"></script> -<script src="../../../test/fake-app.js"></script> -<script src="../../../scripts/util.js"></script> <link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html"> <link rel="import" href="gr-diff.html"> @@ -35,458 +33,214 @@ <script> suite('gr-diff tests', function() { var element; - var server; - var getDiffStub; - var getCommentsStub; setup(function() { + stub('gr-rest-api-interface', { + getLoggedIn: function() { return Promise.resolve(false); }, + }) element = fixture('basic'); - element.changeNum = 42; - element.path = 'sieve.go'; - element.prefs = { - context: 10, - tab_size: 8, - }; - - getDiffStub = sinon.stub(element.$.restAPI, 'getDiff', function() { - return Promise.resolve({ - change_type: 'MODIFIED', - content: [ - { - ab: [ - '<!DOCTYPE html>', - '<meta charset="utf-8">', - '<title>My great page</title>', - '<style>', - ' *,', - ' *:before,', - ' *:after {', - ' box-sizing: border-box;', - ' }', - '</style>', - '<header>', - ] - }, - { - a: [ - ' Welcome ', - ' to the wooorld of tomorrow!', - ], - b: [ - ' Hello, world!', - ], - }, - { - ab: [ - '</header>', - '<body>', - 'Leela: This is the only place the ship can’t hear us, so ', - 'everyone pretend to shower.', - 'Fry: Same as every day. Got it.', - ] - }, - ] - }); - }); - - getCommentsStub = sinon.stub(element.$.restAPI, 'getDiffComments', - function() { - return Promise.resolve({ - baseComments: [ - { - author: { - _account_id: 1000000, - name: 'Andrew Bonventre', - email: 'andybons@gmail.com', - }, - id: '9af53d3f_5f2b8b82', - line: 1, - message: 'this isn’t quite right', - updated: '2015-12-10 02:50:21.627000000', - } - ], - comments: [ - { - author: { - _account_id: 1010008, - name: 'Dave Borowitz', - email: 'dborowitz@google.com', - }, - id: '001a2067_f30f3048', - line: 12, - message: 'What on earth are you thinking, here?', - updated: '2015-12-12 02:51:37.973000000', - }, - { - author: { - _account_id: 1000000, - name: 'Andrew Bonventre', - email: 'andybons@gmail.com', - }, - id: 'a0407443_30dfe8fb', - in_reply_to: '001a2067_f30f3048', - line: 12, - message: '¯\\_(ツ)_/¯', - updated: '2015-12-12 18:50:21.627000000', - }, - ], - }); - } - ); - - server = sinon.fakeServer.create(); - server.respondWith( - 'PUT', - '/accounts/self/preferences.diff', - [ - 200, - {'Content-Type': 'application/json'}, - ')]}\'\n' + - JSON.stringify({context: 25}), - ] - ); - }); - teardown(function() { - getDiffStub.restore(); - getCommentsStub.restore(); - server.restore(); - }); + test('get drafts logged out', function(done) { + element.patchRange = {basePatchNum: 0, patchNum: 0}; - test('comment rendering', function(done) { - element.prefs.context = -1; - element._loggedIn = true; - element.patchRange = { - basePatchNum: 1, - patchNum: 2, - }; - - element.reload().then(function() { - flush(function() { - var leftThreadEls = - Polymer.dom(element.$.leftDiff.root).querySelectorAll( - 'gr-diff-comment-thread'); - assert.equal(leftThreadEls.length, 1); - assert.equal(leftThreadEls[0].comments.length, 1); - - var rightThreadEls = - Polymer.dom(element.$.rightDiff.root).querySelectorAll( - 'gr-diff-comment-thread'); - assert.equal(rightThreadEls.length, 1); - assert.equal(rightThreadEls[0].comments.length, 2); - - var index = leftThreadEls[0].getAttribute('data-index'); - var leftFillerEls = - Polymer.dom(element.$.leftDiff.root).querySelectorAll( - '.commentThread.filler[data-index="' + index + '"]'); - assert.equal(leftFillerEls.length, 1); - var rightFillerEls = - Polymer.dom(element.$.rightDiff.root).querySelectorAll( - '[data-index="' + index + '"]'); - assert.equal(rightFillerEls.length, 2); - - for (var i = 0; i < rightFillerEls.length; i++) { - assert.isTrue(rightFillerEls[i].classList.contains('filler')); - } - var originalHeight = rightFillerEls[0].offsetHeight; - assert.equal(rightFillerEls[1].offsetHeight, originalHeight); - assert.equal(leftThreadEls[0].offsetHeight, originalHeight); - assert.equal(leftFillerEls[0].offsetHeight, originalHeight); - - // Create a comment on the opposite side of the first comment. - var rightLineEL = element.$.rightDiff.$$( - '.lineNum[data-index="' + (index - 1) + '"]'); - assert.ok(rightLineEL); - MockInteractions.tap(rightLineEL); - flush(function() { - var newThreadEls = - Polymer.dom(element.$.rightDiff.root).querySelectorAll( - '[data-index="' + index + '"]'); - assert.equal(newThreadEls.length, 2); - for (var i = 0; i < newThreadEls.length; i++) { - assert.isTrue( - newThreadEls[i].classList.contains('commentThread') || - newThreadEls[i].tagName == 'GR-DIFF-COMMENT-THREAD'); - } - var newHeight = newThreadEls[0].offsetHeight; - assert.equal(newThreadEls[1].offsetHeight, newHeight); - assert.equal(leftFillerEls[0].offsetHeight, newHeight); - assert.equal(leftThreadEls[0].offsetHeight, newHeight); - - // The editing mode height of the right comment will be greater than - // the non-editing mode height of the left comment. - assert.isAbove(newHeight, originalHeight); - - // Discard the right thread and ensure the left comment heights are - // back to their original values. - newThreadEls[1].addEventListener('thread-discard', function() { - rightFillerEls = - Polymer.dom(element.$.rightDiff.root).querySelectorAll( - '[data-index="' + index + '"]'); - assert.equal(rightFillerEls.length, 2); - - for (var i = 0; i < rightFillerEls.length; i++) { - assert.isTrue(rightFillerEls[i].classList.contains('filler')); - } - var originalHeight = rightFillerEls[0].offsetHeight; - assert.equal(rightFillerEls[1].offsetHeight, originalHeight); - assert.equal(leftThreadEls[0].offsetHeight, originalHeight); - assert.equal(leftFillerEls[0].offsetHeight, originalHeight); - done(); - }); - var commentEl = newThreadEls[1].$$('gr-diff-comment'); - commentEl.fire('comment-discard'); - }); - }); - }); - server.respond(); - }); - - test('intraline normalization', function() { - // The content and highlights are in the format returned by the Gerrit - // REST API. - var content = [ - ' <section class="summary">', - ' <gr-linked-text content="' + - '[[_computeCurrentRevisionMessage(change)]]"></gr-linked-text>', - ' </section>', - ]; - var highlights = [ - [31, 34], [42, 26] - ]; - var results = element._normalizeIntralineHighlights(content, highlights); - assert.deepEqual(results, [ - { - contentIndex: 0, - startIndex: 31, - }, - { - contentIndex: 1, - startIndex: 0, - endIndex: 33, - }, - { - contentIndex: 1, - startIndex: 75, - }, - { - contentIndex: 2, - startIndex: 0, - endIndex: 6, - } - ]); - - content = [ - ' this._path = value.path;', - '', - ' // When navigating away from the page, there is a possibility that the', - ' // patch number is no longer a part of the URL (say when navigating to', - ' // the top-level change info view) and therefore undefined in `params`.', - ' if (!this._patchRange.patchNum) {', - ]; - highlights = [ - [14, 17], - [11, 70], - [12, 67], - [12, 67], - [14, 29], - ]; - results = element._normalizeIntralineHighlights(content, highlights); - assert.deepEqual(results, [ - { - contentIndex: 0, - startIndex: 14, - endIndex: 31, - }, - { - contentIndex: 2, - startIndex: 8, - endIndex: 78, - }, - { - contentIndex: 3, - startIndex: 11, - endIndex: 78, - }, - { - contentIndex: 4, - startIndex: 11, - endIndex: 78, - }, - { - contentIndex: 5, - startIndex: 12, - endIndex: 41, - } - ]); - }); - - test('context', function() { - element.prefs.context = 3; - element._diffResponse = { - content: [ - { - ab: [ - '<!DOCTYPE html>', - '<meta charset="utf-8">', - '<title>My great page</title>', - '<style>', - ' *,', - ' *:before,', - ' *:after {', - ' box-sizing: border-box;', - ' }', - '</style>', - '<header>', - ] - }, - { - a: [ - ' Welcome ', - ' to the wooorld of tomorrow!', - ], - b: [ - ' Hello, world!', - ], - }, - { - ab: [ - '</header>', - '<body>', - 'Leela: This is the only place the ship can’t hear us, so ', - 'everyone pretend to shower.', - 'Fry: Same as every day. Got it.', - ] - }, - ] - }; - element._processContent(); - - // First eight lines should be hidden on both sides. - for (var i = 0; i < 8; i++) { - assert.isTrue(element._diff.leftSide[i].hidden); - assert.isTrue(element._diff.rightSide[i].hidden); - } - // A context control should be at index 8 on both sides. - var leftContext = element._diff.leftSide[8]; - var rightContext = element._diff.rightSide[8]; - assert.deepEqual(leftContext, rightContext); - assert.equal(leftContext.numLines, 8); - assert.equal(leftContext.start, 0); - assert.equal(leftContext.end, 8); - - // Line indices 9-16 should be shown. - for (var i = 9; i <= 16; i++) { - // notOk (falsy) because the `hidden` attribute may not be present. - assert.notOk(element._diff.leftSide[i].hidden); - assert.notOk(element._diff.rightSide[i].hidden); - } - - // Lines at indices 17 and 18 should be hidden. - assert.isTrue(element._diff.leftSide[17].hidden); - assert.isTrue(element._diff.rightSide[17].hidden); - assert.isTrue(element._diff.leftSide[18].hidden); - assert.isTrue(element._diff.rightSide[18].hidden); - - // Context control at index 19. - leftContext = element._diff.leftSide[19]; - rightContext = element._diff.rightSide[19]; - assert.deepEqual(leftContext, rightContext); - assert.equal(leftContext.numLines, 2); - assert.equal(leftContext.start, 17); - assert.equal(leftContext.end, 19); - }); - - test('save prefs', function(done) { - element._loggedIn = false; - - element.prefs = { - tab_size: 4, - context: 50, - }; - element.fire('save', {}, {node: element.$$('gr-diff-preferences')}); - assert.isTrue(element._diffPreferencesPromise == null); - - element._loggedIn = true; - element.fire('save', {}, {node: element.$$('gr-diff-preferences')}); - server.respond(); - - element._diffPreferencesPromise.then(function(req) { - assert.equal(req.xhr.requestBody, JSON.stringify(element.prefs)); + var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts'); + var loggedInStub = sinon.stub(element, '_getLoggedIn', + function() { return Promise.resolve(false); }); + element._getDiffDrafts().then(function(result) { + assert.deepEqual(result, {baseComments: [], comments: []}); + sinon.assert.notCalled(getDraftsStub); + loggedInStub.restore(); + getDraftsStub.restore(); done(); }); }); - test('visible line length', function() { - assert.equal(element._visibleLineLength('A'.repeat(5)), 5); - assert.equal( - element._visibleLineLength('A'.repeat(5) + '\t' + 'A'.repeat(5)), 18); + test('get drafts logged in', function(done) { + element.patchRange = {basePatchNum: 0, patchNum: 0}; + var draftsResponse = { + baseComments: [{id: 'foo'}], + comments: [{id: 'bar'}], + }; + var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts', + function() { return Promise.resolve(draftsResponse); }); + var loggedInStub = sinon.stub(element, '_getLoggedIn', + function() { return Promise.resolve(true); }); + element._getDiffDrafts().then(function(result) { + assert.deepEqual(result, draftsResponse); + loggedInStub.restore(); + getDraftsStub.restore(); + done(); + }); }); - test('break up common diff chunks', function() { - element._groupedBaseComments = { - 1: {}, + test('get comments and drafts', function(done) { + var loggedInStub = sinon.stub(element, '_getLoggedIn', + function() { return Promise.resolve(true); }); + var comments = { + baseComments: [ + {id: 'bc1'}, + {id: 'bc2'}, + ], + comments: [ + {id: 'c1'}, + {id: 'c2'}, + ], }; - element._groupedComments = { - 10: {}, + var diffCommentsStub = sinon.stub(element, '_getDiffComments', + function() { return Promise.resolve(comments); }); + + var drafts = { + baseComments: [ + {id: 'bd1'}, + {id: 'bd2'}, + ], + comments: [ + {id: 'd1'}, + {id: 'd2'}, + ], }; - var ctx = { - left: {lineNum: 0}, - right: {lineNum: 0}, + var diffDraftsStub = sinon.stub(element, '_getDiffDrafts', + function() { return Promise.resolve(drafts); }); + + element.changeNum = '42'; + element.patchRange = { + basePatchNum: 'PARENT', + patchNum: 3, }; - var content = [ - { - ab: [ - 'Copyright (C) 2015 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.', - ] - } - ]; - var result = element._breakUpCommonChunksWithComments(ctx, content); - assert.deepEqual(result, [ - { - __noHighlight: true, - a: ['Copyright (C) 2015 The Android Open Source Project'], - b: ['Copyright (C) 2015 The Android Open Source Project'], + element.path = '/path/to/foo'; + element.projectConfig = {foo: 'bar'}; + + element._getDiffCommentsAndDrafts().then(function(result) { + assert.deepEqual(result, { + meta: { + changeNum: '42', + patchRange: { + basePatchNum: 'PARENT', + patchNum: 3, + }, + path: '/path/to/foo', + projectConfig: {foo: 'bar'}, + }, + left: [ + {id: 'bc1'}, + {id: 'bc2'}, + {id: 'bd1', __draft: true}, + {id: 'bd2', __draft: true}, + ], + right: [ + {id: 'c1'}, + {id: 'c2'}, + {id: 'd1', __draft: true}, + {id: 'd2', __draft: true}, + ], + }); + + diffCommentsStub.restore(); + diffDraftsStub.restore(); + loggedInStub.restore(); + done(); + }); + }); + + test('remove comment', function() { + element._comments = { + meta: { + changeNum: '42', + patchRange: { + basePatchNum: 'PARENT', + patchNum: 3, + }, + path: '/path/to/foo', + projectConfig: {foo: 'bar'}, }, - { - ab: [ - '', - '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, ', - ] + left: [ + {id: 'bc1'}, + {id: 'bc2'}, + {id: 'bd1', __draft: true}, + {id: 'bd2', __draft: true}, + ], + right: [ + {id: 'c1'}, + {id: 'c2'}, + {id: 'd1', __draft: true}, + {id: 'd2', __draft: true}, + ], + }; + + element._removeComment({}); + // Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t seem to + // believe that one object deepEquals another even when they do :-/. + assert.equal(JSON.stringify(element._comments), JSON.stringify({ + meta: { + changeNum: '42', + patchRange: { + basePatchNum: 'PARENT', + patchNum: 3, + }, + path: '/path/to/foo', + projectConfig: {foo: 'bar'}, }, - { - __noHighlight: true, - a: ['software distributed under the License is distributed on an '], - b: ['software distributed under the License is distributed on an '] + left: [ + {id: 'bc1'}, + {id: 'bc2'}, + {id: 'bd1', __draft: true}, + {id: 'bd2', __draft: true}, + ], + right: [ + {id: 'c1'}, + {id: 'c2'}, + {id: 'd1', __draft: true}, + {id: 'd2', __draft: true}, + ], + })); + + element._removeComment({id: 'bc2'}); + assert.equal(JSON.stringify(element._comments), JSON.stringify({ + meta: { + changeNum: '42', + patchRange: { + basePatchNum: 'PARENT', + patchNum: 3, + }, + path: '/path/to/foo', + projectConfig: {foo: 'bar'}, }, - { - ab: [ - '"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.', - ] - } - ]); + left: [ + {id: 'bc1'}, + {id: 'bd1', __draft: true}, + {id: 'bd2', __draft: true}, + ], + right: [ + {id: 'c1'}, + {id: 'c2'}, + {id: 'd1', __draft: true}, + {id: 'd2', __draft: true}, + ], + })); + + element._removeComment({id: 'd2'}); + assert.deepEqual(JSON.stringify(element._comments), JSON.stringify({ + meta: { + changeNum: '42', + patchRange: { + basePatchNum: 'PARENT', + patchNum: 3, + }, + path: '/path/to/foo', + projectConfig: {foo: 'bar'}, + }, + left: [ + {id: 'bc1'}, + {id: 'bd1', __draft: true}, + {id: 'bd2', __draft: true}, + ], + right: [ + {id: 'c1'}, + {id: 'c2'}, + {id: 'd1', __draft: true}, + ], + })); }); }); - </script>
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html deleted file mode 100644 index 352092c..0000000 --- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.html +++ /dev/null
@@ -1,184 +0,0 @@ -<!-- -Copyright (C) 2015 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="../../shared/gr-button/gr-button.html"> -<link rel="import" href="../../shared/gr-overlay/gr-overlay.html"> -<link rel="import" href="../../shared/gr-request/gr-request.html"> -<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html"> - -<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html"> -<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html"> -<link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html"> - -<dom-module id="gr-new-diff"> - <template> - <style> - :host { - --light-remove-highlight-color: #fee; - --dark-remove-highlight-color: #ffd4d4; - --light-add-highlight-color: #efe; - --dark-add-highlight-color: #d4ffd4; - } - .loading { - padding: 0 var(--default-horizontal-margin) 1em; - color: #666; - } - .header { - display: flex; - justify-content: space-between; - margin: 0 var(--default-horizontal-margin) .75em; - } - .prefsButton { - text-align: right; - } - .diffContainer { - border-bottom: 1px solid #eee; - border-top: 1px solid #eee; - display: flex; - font: 12px var(--monospace-font-family); - overflow-x: auto; - will-change: transform; - } - table { - border-collapse: collapse; - border-right: 1px solid #ddd; - } - .section { - background-color: #eee; - } - .blank, - .content { - background-color: #fff; - } - .lineNum, - .content { - vertical-align: top; - white-space: pre; - } - .contextLineNum:before, - .lineNum:before { - display: inline-block; - color: #666; - content: attr(data-value); - padding: 0 .75em; - text-align: right; - width: 100%; - } - .canComment .lineNum[data-value] { - cursor: pointer; - } - .canComment .lineNum[data-value]:before { - text-decoration: underline; - } - .canComment .lineNum[data-value]:hover:before { - background-color: #ccc; - } - .canComment .lineNum[data-value="FILE"]:before { - content: 'File'; - } - .content { - overflow: hidden; - min-width: var(--content-width, 80ch); - } - .content.left { - -webkit-user-select: var(--left-user-select, text); - -moz-user-select: var(--left-user-select, text); - -ms-user-select: var(--left-user-select, text); - user-select: var(--left-user-select, text); - } - .content.right { - -webkit-user-select: var(--right-user-select, text); - -moz-user-select: var(--right-user-select, text); - -ms-user-select: var(--right-user-select, text); - user-select: var(--right-user-select, text); - } - .content.add hl, - .content.add.darkHighlight { - background-color: var(--dark-add-highlight-color); - } - .content.add.lightHighlight { - background-color: var(--light-add-highlight-color); - } - .content.remove hl, - .content.remove.darkHighlight { - background-color: var(--dark-remove-highlight-color); - } - .content.remove.lightHighlight { - background-color: var(--light-remove.highlight-color); - } - .contextControl { - color: #849; - background-color: #fef; - } - .contextControl gr-button { - display: block; - font-family: var(--monospace-font-family); - text-decoration: none; - } - .contextControl td:not(.lineNum) { - text-align: center; - } - .br:after { - /* Line feed */ - content: '\A'; - } - .tab { - display: inline-block; - } - .tab.withIndicator:before { - color: #C62828; - /* >> character */ - content: '\00BB'; - } - </style> - <div class="loading" hidden$="[[!_loading]]">Loading...</div> - <div hidden$="[[_loading]]" hidden> - <div class="header"> - <gr-patch-range-select - path="[[path]]" - change-num="[[changeNum]]" - patch-range="[[patchRange]]" - available-patches="[[availablePatches]]"></gr-patch-range-select> - <gr-button link - class="prefsButton" - on-tap="_handlePrefsTap" - hidden$="[[_computePrefsButtonHidden(_prefs, _loggedIn)]]" - hidden>Diff View Preferences</gr-button> - </div> - <gr-overlay id="prefsOverlay" with-backdrop> - <gr-diff-preferences - prefs="{{_prefs}}" - on-save="_handlePrefsSave" - on-cancel="_handlePrefsCancel"></gr-diff-preferences> - </gr-overlay> - - <div class$="[[_computeContainerClass(_loggedIn, _viewMode)]]" - on-tap="_handleTap" - on-mousedown="_handleMouseDown" - on-copy="_handleCopy"> - <table id="diffTable"></table> - </div> - </div> - <gr-rest-api-interface id="restAPI"></gr-rest-api-interface> - </template> - <script src="gr-diff-line.js"></script> - <script src="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-new-diff.js"></script> -</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js deleted file mode 100644 index aafea43..0000000 --- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff.js +++ /dev/null
@@ -1,516 +0,0 @@ -// 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. -(function() { - 'use strict'; - - var DiffViewMode = { - SIDE_BY_SIDE: 'SIDE_BY_SIDE', - UNIFIED: 'UNIFIED_DIFF', - }; - - var DiffSide = { - LEFT: 'left', - RIGHT: 'right', - }; - - Polymer({ - is: 'gr-new-diff', - - /** - * Fired when the diff is rendered. - * - * @event render - */ - - properties: { - availablePatches: Array, - changeNum: String, - patchRange: Object, - path: String, - - projectConfig: { - type: Object, - observer: '_projectConfigChanged', - }, - - _loggedIn: { - type: Boolean, - value: false, - }, - _loading: { - type: Boolean, - value: true, - }, - _viewMode: { - type: String, - value: DiffViewMode.SIDE_BY_SIDE, - }, - _diff: Object, - _diffBuilder: Object, - _prefs: Object, - _selectionSide: { - type: String, - observer: '_selectionSideChanged', - }, - _comments: Object, - _focusedSection: { - type: Number, - value: -1, - }, - _focusedThread: { - type: Number, - value: -1, - }, - }, - - observers: [ - '_prefsChanged(_prefs.*)', - ], - - attached: function() { - this._getLoggedIn().then(function(loggedIn) { - this._loggedIn = loggedIn; - }.bind(this)); - - this.addEventListener('thread-discard', - this._handleThreadDiscard.bind(this)); - this.addEventListener('comment-discard', - this._handleCommentDiscard.bind(this)); - }, - - reload: function() { - this._clearDiffContent(); - this._loading = true; - - var promises = []; - - promises.push(this._getDiff().then(function(diff) { - this._diff = diff; - this._loading = false; - }.bind(this))); - - promises.push(this._getDiffCommentsAndDrafts().then(function(comments) { - this._comments = comments; - }.bind(this))); - - promises.push(this._getDiffPreferences().then(function(prefs) { - this._prefs = prefs; - }.bind(this))); - - return Promise.all(promises).then(function() { - this._render(); - }.bind(this)); - }, - - scrollToLine: function(lineNum) { - if (isNaN(lineNum) || lineNum < 1) { return; } - - var lineEls = Polymer.dom(this.root).querySelectorAll( - '.lineNum[data-value="' + lineNum + '"]'); - - // Always choose the right side. - var el = lineEls.length === 2 ? lineEls[1] : lineEls[0]; - this._scrollToElement(el); - }, - - scrollToNextDiffChunk: function() { - this._focusedSection = this._advanceElementWithinNodeList( - this._getDeltaSections(), this._focusedSection, 1); - }, - - scrollToPreviousDiffChunk: function() { - this._focusedSection = this._advanceElementWithinNodeList( - this._getDeltaSections(), this._focusedSection, -1); - }, - - scrollToNextCommentThread: function() { - this._focusedThread = this._advanceElementWithinNodeList( - this._getCommentThreads(), this._focusedThread, 1); - }, - - scrollToPreviousCommentThread: function() { - this._focusedThread = this._advanceElementWithinNodeList( - this._getCommentThreads(), this._focusedThread, -1); - }, - - _advanceElementWithinNodeList: function(els, curIndex, direction) { - var idx = Math.max(0, Math.min(els.length - 1, curIndex + direction)); - if (curIndex !== idx) { - this._scrollToElement(els[idx]); - return idx; - } - return curIndex; - }, - - _getCommentThreads: function() { - return Polymer.dom(this.root).querySelectorAll('gr-diff-comment-thread'); - }, - - _getDeltaSections: function() { - return Polymer.dom(this.root).querySelectorAll('.section.delta'); - }, - - _scrollToElement: function(el) { - if (!el) { return; } - - // Calculate where the element is relative to the window. - var top = el.offsetTop; - for (var offsetParent = el.offsetParent; - offsetParent; - offsetParent = offsetParent.offsetParent) { - top += offsetParent.offsetTop; - } - - // Scroll the element to the middle of the window. Dividing by a third - // instead of half the inner height feels a bit better otherwise the - // element appears to be below the center of the window even when it - // isn't. - window.scrollTo(0, top - (window.innerHeight / 3) + - (el.offsetHeight / 2)); - }, - - _computeContainerClass: function(loggedIn, viewMode) { - var classes = ['diffContainer']; - switch (viewMode) { - case DiffViewMode.UNIFIED: - classes.push('unified'); - break; - case DiffViewMode.SIDE_BY_SIDE: - classes.push('sideBySide'); - break - default: - throw Error('Invalid view mode: ', viewMode); - } - if (loggedIn) { - classes.push('canComment'); - } - return classes.join(' '); - }, - - _computePrefsButtonHidden: function(prefs, loggedIn) { - return !loggedIn || !prefs; - }, - - _handlePrefsTap: function(e) { - e.preventDefault(); - this.$.prefsOverlay.open(); - }, - - _handlePrefsSave: function(e) { - e.stopPropagation(); - var el = Polymer.dom(e).rootTarget; - el.disabled = true; - this._saveDiffPreferences().then(function() { - this.$.prefsOverlay.close(); - el.disabled = false; - }.bind(this)).catch(function(err) { - el.disabled = false; - alert('Oops. Something went wrong. Check the console and bug the ' + - 'PolyGerrit team for assistance.'); - throw err; - }); - }, - - _saveDiffPreferences: function() { - return this.$.restAPI.saveDiffPreferences(this._prefs); - }, - - _handlePrefsCancel: function(e) { - e.stopPropagation(); - this.$.prefsOverlay.close(); - }, - - _handleTap: function(e) { - var el = Polymer.dom(e).rootTarget; - - if (el.classList.contains('showContext')) { - this._showContext(e.detail.group, e.detail.section); - } else if (el.classList.contains('lineNum')) { - this._handleLineTap(el); - } - }, - - _handleLineTap: function(el) { - this._getLoggedIn().then(function(loggedIn) { - if (!loggedIn) { return; } - - var value = el.getAttribute('data-value'); - if (value === GrDiffLine.FILE) { - this._addDraft(el); - return; - } - var lineNum = parseInt(value, 10); - if (isNaN(lineNum)) { - throw Error('Invalid line number: ' + value); - } - this._addDraft(el, lineNum); - }.bind(this)); - }, - - _addDraft: function(lineEl, opt_lineNum) { - var threadEl; - - // Does a thread already exist at this line? - var contentEl = lineEl.nextSibling; - while (contentEl && !contentEl.classList.contains('content')) { - contentEl = contentEl.nextSibling; - } - if (contentEl.childNodes.length > 0 && - contentEl.lastChild.nodeName === 'GR-DIFF-COMMENT-THREAD') { - threadEl = contentEl.lastChild; - } else { - var patchNum = this.patchRange.patchNum; - var side = 'REVISION'; - if (contentEl.classList.contains(DiffSide.LEFT) || - contentEl.classList.contains('remove')) { - if (this.patchRange.basePatchNum === 'PARENT') { - side = 'PARENT'; - } else { - patchNum = this.patchRange.basePatchNum; - } - } - threadEl = this._builder.createCommentThread(this.changeNum, patchNum, - this.path, side, this.projectConfig); - contentEl.appendChild(threadEl); - } - threadEl.addDraft(opt_lineNum); - }, - - _handleThreadDiscard: function(e) { - var el = Polymer.dom(e).rootTarget; - el.parentNode.removeChild(el); - }, - - _handleCommentDiscard: function(e) { - var comment = Polymer.dom(e).rootTarget.comment; - this._removeComment(comment); - }, - - _removeComment: function(comment) { - if (!comment.id) { return; } - this._removeCommentFromSide(comment, DiffSide.LEFT) || - this._removeCommentFromSide(comment, DiffSide.RIGHT); - }, - - _removeCommentFromSide: function(comment, side) { - var idx = -1; - for (var i = 0; i < this._comments[side].length; i++) { - if (this._comments[side][i].id === comment.id) { - idx = i; - break; - } - } - if (idx !== -1) { - this.splice('_comments.' + side, idx, 1); - return true; - } - return false; - }, - - _handleMouseDown: function(e) { - var el = Polymer.dom(e).rootTarget; - var side; - for (var node = el; node != null; node = node.parentNode) { - if (!node.classList) { continue; } - - if (node.classList.contains(DiffSide.LEFT)) { - side = DiffSide.LEFT; - break; - } else if (node.classList.contains(DiffSide.RIGHT)) { - side = DiffSide.RIGHT; - break; - } - } - this._selectionSide = side; - }, - - _selectionSideChanged: function(side) { - if (side) { - var oppositeSide = side === DiffSide.RIGHT ? - DiffSide.LEFT : DiffSide.RIGHT; - this.customStyle['--' + side + '-user-select'] = 'text'; - this.customStyle['--' + oppositeSide + '-user-select'] = 'none'; - } else { - this.customStyle['--left-user-select'] = 'text'; - this.customStyle['--right-user-select'] = 'text'; - } - this.updateStyles(); - }, - - _handleCopy: function(e) { - var text = this._getSelectedText(this._selectionSide); - e.clipboardData.setData('Text', text); - e.preventDefault(); - }, - - _getSelectedText: function(opt_side) { - var sel = window.getSelection(); - var range = sel.getRangeAt(0); - var doc = range.cloneContents(); - var selector = '.content'; - if (opt_side) { - selector += '.' + opt_side; - } - var contentEls = Polymer.dom(doc).querySelectorAll(selector); - - if (contentEls.length === 0) { - return doc.textContent; - } - - var text = ''; - for (var i = 0; i < contentEls.length; i++) { - text += contentEls[i].textContent + '\n'; - } - return text; - }, - - _showContext: function(group, sectionEl) { - this._builder.emitGroup(group, sectionEl); - sectionEl.parentNode.removeChild(sectionEl); - }, - - _prefsChanged: function(prefsChangeRecord) { - var prefs = prefsChangeRecord.base; - this.customStyle['--content-width'] = prefs.line_length + 'ch'; - this.updateStyles(); - - if (this._diff && this._comments) { - this._render(); - } - }, - - _render: function() { - this._clearDiffContent(); - this._builder = this._getDiffBuilder(this._diff, this._comments, - this._prefs); - this._builder.emitDiff(this._diff.content); - - this.async(function() { - this.fire('render', null, {bubbles: false}); - }.bind(this), 1); - }, - - _clearDiffContent: function() { - this.$.diffTable.innerHTML = null; - }, - - _getDiff: function() { - return this.$.restAPI.getDiff( - this.changeNum, - this.patchRange.basePatchNum, - this.patchRange.patchNum, - this.path); - }, - - _getDiffComments: function() { - return this.$.restAPI.getDiffComments( - this.changeNum, - this.patchRange.basePatchNum, - this.patchRange.patchNum, - this.path); - }, - - _getDiffDrafts: function() { - return this._getLoggedIn().then(function(loggedIn) { - if (!loggedIn) { - return Promise.resolve({baseComments: [], comments: []}); - } - return this.$.restAPI.getDiffDrafts( - this.changeNum, - this.patchRange.basePatchNum, - this.patchRange.patchNum, - this.path); - }.bind(this)); - }, - - _getDiffCommentsAndDrafts: function() { - var promises = []; - promises.push(this._getDiffComments()); - promises.push(this._getDiffDrafts()); - return Promise.all(promises).then(function(results) { - return Promise.resolve({ - comments: results[0], - drafts: results[1], - }); - }).then(this._normalizeDiffCommentsAndDrafts.bind(this)); - }, - - _getDiffPreferences: function() { - return this._getLoggedIn().then(function(loggedIn) { - if (!loggedIn) { - // These defaults should match the defaults in - // gerrit-extension-api/src/main/jcg/gerrit/extensions/client/DiffPreferencesInfo.java - // NOTE: There are some settings that don't apply to PolyGerrit - // (Render mode being at least one of them). - return Promise.resolve({ - auto_hide_diff_table_header: true, - context: 10, - cursor_blink_rate: 0, - ignore_whitespace: 'IGNORE_NONE', - intraline_difference: true, - line_length: 100, - show_line_endings: true, - show_tabs: true, - show_whitespace_errors: true, - syntax_highlighting: true, - tab_size: 8, - theme: 'DEFAULT', - }); - } - return this.$.restAPI.getDiffPreferences(); - }.bind(this)); - }, - - _normalizeDiffCommentsAndDrafts: function(results) { - function markAsDraft(d) { - d.__draft = true; - return d; - } - var baseDrafts = results.drafts.baseComments.map(markAsDraft); - var drafts = results.drafts.comments.map(markAsDraft); - return Promise.resolve({ - meta: { - path: this.path, - changeNum: this.changeNum, - patchRange: this.patchRange, - projectConfig: this.projectConfig, - }, - left: results.comments.baseComments.concat(baseDrafts), - right: results.comments.comments.concat(drafts), - }); - }, - - _getLoggedIn: function() { - return this.$.restAPI.getLoggedIn(); - }, - - _getDiffBuilder: function(diff, comments, prefs) { - if (this._viewMode === DiffViewMode.SIDE_BY_SIDE) { - return new GrDiffBuilderSideBySide(diff, comments, prefs, - this.$.diffTable); - } else if (this._viewMode === DiffViewMode.UNIFIED) { - return new GrDiffBuilderUnified(diff, comments, prefs, - this.$.diffTable); - } - throw Error('Unsupported diff view mode: ' + this._viewMode); - }, - - _projectConfigChanged: function(projectConfig) { - var threadEls = this._getCommentThreads(); - for (var i = 0; i < threadEls.length; i++) { - threadEls[i].projectConfig = projectConfig; - } - }, - }); -})();
diff --git a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html b/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html deleted file mode 100644 index b015df4..0000000 --- a/polygerrit-ui/app/elements/diff/gr-new-diff/gr-new-diff_test.html +++ /dev/null
@@ -1,244 +0,0 @@ -<!DOCTYPE html> -<!-- -Copyright (C) 2015 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. ---> - -<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> -<title>gr-new-diff</title> - -<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script> -<script src="../../../bower_components/web-component-tester/browser.js"></script> - -<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html"> -<link rel="import" href="gr-new-diff.html"> - -<test-fixture id="basic"> - <template> - <gr-new-diff></gr-new-diff> - </template> -</test-fixture> - -<script> - suite('gr-new-diff tests', function() { - var element; - - setup(function() { - stub('gr-rest-api-interface', { - getLoggedIn: function() { return Promise.resolve(false); }, - }) - element = fixture('basic'); - }); - - test('get drafts logged out', function(done) { - element.patchRange = {basePatchNum: 0, patchNum: 0}; - - var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts'); - var loggedInStub = sinon.stub(element, '_getLoggedIn', - function() { return Promise.resolve(false); }); - element._getDiffDrafts().then(function(result) { - assert.deepEqual(result, {baseComments: [], comments: []}); - sinon.assert.notCalled(getDraftsStub); - loggedInStub.restore(); - getDraftsStub.restore(); - done(); - }); - }); - - test('get drafts logged in', function(done) { - element.patchRange = {basePatchNum: 0, patchNum: 0}; - var draftsResponse = { - baseComments: [{id: 'foo'}], - comments: [{id: 'bar'}], - }; - var getDraftsStub = sinon.stub(element.$.restAPI, 'getDiffDrafts', - function() { return Promise.resolve(draftsResponse); }); - var loggedInStub = sinon.stub(element, '_getLoggedIn', - function() { return Promise.resolve(true); }); - element._getDiffDrafts().then(function(result) { - assert.deepEqual(result, draftsResponse); - loggedInStub.restore(); - getDraftsStub.restore(); - done(); - }); - }); - - test('get comments and drafts', function(done) { - var loggedInStub = sinon.stub(element, '_getLoggedIn', - function() { return Promise.resolve(true); }); - var comments = { - baseComments: [ - {id: 'bc1'}, - {id: 'bc2'}, - ], - comments: [ - {id: 'c1'}, - {id: 'c2'}, - ], - }; - var diffCommentsStub = sinon.stub(element, '_getDiffComments', - function() { return Promise.resolve(comments); }); - - var drafts = { - baseComments: [ - {id: 'bd1'}, - {id: 'bd2'}, - ], - comments: [ - {id: 'd1'}, - {id: 'd2'}, - ], - }; - var diffDraftsStub = sinon.stub(element, '_getDiffDrafts', - function() { return Promise.resolve(drafts); }); - - element.changeNum = '42'; - element.patchRange = { - basePatchNum: 'PARENT', - patchNum: 3, - }; - element.path = '/path/to/foo'; - element.projectConfig = {foo: 'bar'}; - - element._getDiffCommentsAndDrafts().then(function(result) { - assert.deepEqual(result, { - meta: { - changeNum: '42', - patchRange: { - basePatchNum: 'PARENT', - patchNum: 3, - }, - path: '/path/to/foo', - projectConfig: {foo: 'bar'}, - }, - left: [ - {id: 'bc1'}, - {id: 'bc2'}, - {id: 'bd1', __draft: true}, - {id: 'bd2', __draft: true}, - ], - right: [ - {id: 'c1'}, - {id: 'c2'}, - {id: 'd1', __draft: true}, - {id: 'd2', __draft: true}, - ], - }); - - diffCommentsStub.restore(); - diffDraftsStub.restore(); - loggedInStub.restore(); - done(); - }); - }); - - test('remove comment', function() { - element._comments = { - meta: { - changeNum: '42', - patchRange: { - basePatchNum: 'PARENT', - patchNum: 3, - }, - path: '/path/to/foo', - projectConfig: {foo: 'bar'}, - }, - left: [ - {id: 'bc1'}, - {id: 'bc2'}, - {id: 'bd1', __draft: true}, - {id: 'bd2', __draft: true}, - ], - right: [ - {id: 'c1'}, - {id: 'c2'}, - {id: 'd1', __draft: true}, - {id: 'd2', __draft: true}, - ], - }; - - element._removeComment({}); - assert.deepEqual(element._comments, { - meta: { - changeNum: '42', - patchRange: { - basePatchNum: 'PARENT', - patchNum: 3, - }, - path: '/path/to/foo', - projectConfig: {foo: 'bar'}, - }, - left: [ - {id: 'bc1'}, - {id: 'bc2'}, - {id: 'bd1', __draft: true}, - {id: 'bd2', __draft: true}, - ], - right: [ - {id: 'c1'}, - {id: 'c2'}, - {id: 'd1', __draft: true}, - {id: 'd2', __draft: true}, - ], - }); - - element._removeComment({id: 'bc2'}); - assert.deepEqual(element._comments, { - meta: { - changeNum: '42', - patchRange: { - basePatchNum: 'PARENT', - patchNum: 3, - }, - path: '/path/to/foo', - projectConfig: {foo: 'bar'}, - }, - left: [ - {id: 'bc1'}, - {id: 'bd1', __draft: true}, - {id: 'bd2', __draft: true}, - ], - right: [ - {id: 'c1'}, - {id: 'c2'}, - {id: 'd1', __draft: true}, - {id: 'd2', __draft: true}, - ], - }); - - element._removeComment({id: 'd2'}); - assert.deepEqual(element._comments, { - meta: { - changeNum: '42', - patchRange: { - basePatchNum: 'PARENT', - patchNum: 3, - }, - path: '/path/to/foo', - projectConfig: {foo: 'bar'}, - }, - left: [ - {id: 'bc1'}, - {id: 'bd1', __draft: true}, - {id: 'bd2', __draft: true}, - ], - right: [ - {id: 'c1'}, - {id: 'c2'}, - {id: 'd1', __draft: true}, - ], - }); - }); - }); -</script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index 5da0ace..71a80332 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html
@@ -39,15 +39,13 @@ '../elements/change-list/gr-change-list-item/gr-change-list-item_test.html', '../elements/core/gr-account-dropdown/gr-account-dropdown_test.html', '../elements/core/gr-search-bar/gr-search-bar_test.html', + '../elements/diff/gr-diff/gr-diff-builder_test.html', + '../elements/diff/gr-diff/gr-diff-group_test.html', '../elements/diff/gr-diff/gr-diff_test.html', '../elements/diff/gr-diff-comment/gr-diff-comment_test.html', '../elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html', '../elements/diff/gr-diff-preferences/gr-diff-preferences_test.html', - '../elements/diff/gr-diff-side/gr-diff-side_test.html', '../elements/diff/gr-diff-view/gr-diff-view_test.html', - '../elements/diff/gr-new-diff/gr-diff-builder_test.html', - '../elements/diff/gr-new-diff/gr-diff-group_test.html', - '../elements/diff/gr-new-diff/gr-new-diff_test.html', '../elements/diff/gr-patch-range-select/gr-patch-range-select_test.html', '../elements/shared/gr-account-label/gr-account-label_test.html', '../elements/shared/gr-account-link/gr-account-link_test.html',