| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 Changes.ChangesView = class extends UI.VBox { |
| 6 constructor() { |
| 7 super(true); |
| 8 this.registerRequiredCSS('changes/changesView.css'); |
| 9 var splitWidget = new UI.SplitWidget(true /* vertical */, false /* sidebar o
n left */); |
| 10 var mainWidget = new UI.Widget(); |
| 11 splitWidget.setMainWidget(mainWidget); |
| 12 splitWidget.show(this.contentElement); |
| 13 |
| 14 this._emptyWidget = new UI.EmptyWidget(Common.UIString('No changes')); |
| 15 this._emptyWidget.show(mainWidget.element); |
| 16 |
| 17 this._workspaceDiff = WorkspaceDiff.workspaceDiff(); |
| 18 this._changesSidebar = new Changes.ChangesSidebar(this._workspaceDiff); |
| 19 this._changesSidebar.addEventListener( |
| 20 Changes.ChangesSidebar.Events.SelectedUISourceCodeChanged, this._selecte
dUISourceCodeChanged, this); |
| 21 splitWidget.setSidebarWidget(this._changesSidebar); |
| 22 |
| 23 /** @type {?Workspace.UISourceCode} */ |
| 24 this._selectedUISourceCode = null; |
| 25 |
| 26 /** @type {!Array<!Changes.ChangesView.Row>} */ |
| 27 this._diffRows = []; |
| 28 |
| 29 this._maxLineDigits = 1; |
| 30 |
| 31 this._editor = new TextEditor.CodeMirrorTextEditor({ |
| 32 lineNumbers: true, |
| 33 lineWrapping: false, |
| 34 maxHighlightLength: Infinity // This is to avoid CodeMirror bailing out o
f highlighting big diffs. |
| 35 }); |
| 36 this._editor.setReadOnly(true); |
| 37 this._editor.show(mainWidget.element.createChild('div', 'editor-container'))
; |
| 38 this._editor.hideWidget(); |
| 39 |
| 40 this._editor.element.addEventListener('click', this._click.bind(this), false
); |
| 41 |
| 42 this._toolbar = new UI.Toolbar('changes-toolbar', mainWidget.element); |
| 43 var revertButton = new UI.ToolbarButton(Common.UIString('Revert all changes'
), 'largeicon-undo'); |
| 44 revertButton.addEventListener(UI.ToolbarButton.Events.Click, this._revert.bi
nd(this)); |
| 45 this._toolbar.appendToolbarItem(revertButton); |
| 46 this._diffStats = new UI.ToolbarText(''); |
| 47 this._toolbar.appendToolbarItem(this._diffStats); |
| 48 this._toolbar.setEnabled(false); |
| 49 |
| 50 this._selectedUISourceCodeChanged(); |
| 51 } |
| 52 |
| 53 _selectedUISourceCodeChanged() { |
| 54 this._revealUISourceCode(this._changesSidebar.selectedUISourceCode()); |
| 55 } |
| 56 |
| 57 _revert() { |
| 58 var uiSourceCode = this._selectedUISourceCode; |
| 59 if (!uiSourceCode) |
| 60 return; |
| 61 uiSourceCode.requestOriginalContent().then(original => uiSourceCode.addRevis
ion(original || '')); |
| 62 } |
| 63 |
| 64 /** |
| 65 * @param {!Event} event |
| 66 */ |
| 67 _click(event) { |
| 68 var selection = this._editor.selection(); |
| 69 if (!selection.isEmpty()) |
| 70 return; |
| 71 var row = this._diffRows[selection.startLine]; |
| 72 Common.Revealer.reveal( |
| 73 this._selectedUISourceCode.uiLocation(row.currentLineNumber - 1, selecti
on.startColumn), false); |
| 74 event.consume(true); |
| 75 } |
| 76 |
| 77 /** |
| 78 * @param {?Workspace.UISourceCode} uiSourceCode |
| 79 */ |
| 80 _revealUISourceCode(uiSourceCode) { |
| 81 if (this._selectedUISourceCode === uiSourceCode) |
| 82 return; |
| 83 |
| 84 if (this._selectedUISourceCode) |
| 85 this._workspaceDiff.unsubscribeFromDiffChange(this._selectedUISourceCode,
this._refreshDiff, this); |
| 86 if (uiSourceCode && this.isShowing()) |
| 87 this._workspaceDiff.subscribeToDiffChange(uiSourceCode, this._refreshDiff,
this); |
| 88 |
| 89 this._selectedUISourceCode = uiSourceCode; |
| 90 this._refreshDiff(); |
| 91 } |
| 92 |
| 93 /** |
| 94 * @override |
| 95 */ |
| 96 wasShown() { |
| 97 this._refreshDiff(); |
| 98 } |
| 99 |
| 100 _refreshDiff() { |
| 101 if (!this.isShowing()) |
| 102 return; |
| 103 |
| 104 if (!this._selectedUISourceCode) { |
| 105 this._renderDiffRows(null); |
| 106 return; |
| 107 } |
| 108 var uiSourceCode = this._selectedUISourceCode; |
| 109 this._workspaceDiff.requestDiff(uiSourceCode).then(diff => { |
| 110 if (this._selectedUISourceCode !== uiSourceCode) |
| 111 return; |
| 112 this._renderDiffRows(diff); |
| 113 }); |
| 114 } |
| 115 |
| 116 /** |
| 117 * @param {?Diff.Diff.DiffArray} diff |
| 118 */ |
| 119 _renderDiffRows(diff) { |
| 120 this._diffRows = []; |
| 121 |
| 122 if (!diff || (diff.length === 1 && diff[0][0] === Diff.Diff.Operation.Equal)
) { |
| 123 this._diffStats.setText(''); |
| 124 this._toolbar.setEnabled(false); |
| 125 this._editor.hideWidget(); |
| 126 this._emptyWidget.showWidget(); |
| 127 return; |
| 128 } |
| 129 |
| 130 var insertions = 0; |
| 131 var deletions = 0; |
| 132 var currentLineNumber = 0; |
| 133 var baselineLineNumber = 0; |
| 134 var paddingLines = 3; |
| 135 |
| 136 for (var i = 0; i < diff.length; ++i) { |
| 137 var token = diff[i]; |
| 138 switch (token[0]) { |
| 139 case Diff.Diff.Operation.Equal: |
| 140 this._diffRows.pushAll(createEqualRows(token[1], i === 0, i === diff.l
ength - 1)); |
| 141 break; |
| 142 case Diff.Diff.Operation.Insert: |
| 143 for (var line of token[1]) |
| 144 this._diffRows.push(createRow(line, Changes.ChangesView.RowType.Addi
tion)); |
| 145 insertions += token[1].length; |
| 146 break; |
| 147 case Diff.Diff.Operation.Delete: |
| 148 deletions += token[1].length; |
| 149 if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) { |
| 150 i++; |
| 151 this._diffRows.pushAll(createModifyRows(token[1].join('\n'), diff[i]
[1].join('\n'))); |
| 152 insertions += diff[i][1].length; |
| 153 } else { |
| 154 for (var line of token[1]) |
| 155 this._diffRows.push(createRow(line, Changes.ChangesView.RowType.De
letion)); |
| 156 } |
| 157 break; |
| 158 } |
| 159 } |
| 160 |
| 161 this._maxLineDigits = Math.ceil(Math.log10(Math.max(currentLineNumber, basel
ineLineNumber))); |
| 162 |
| 163 this._diffStats.setText(Common.UIString( |
| 164 '%d insertion%s (+), %d deletion%s (-)', insertions, insertions !== 1 ?
's' : '', deletions, |
| 165 deletions !== 1 ? 's' : '')); |
| 166 this._toolbar.setEnabled(true); |
| 167 this._emptyWidget.hideWidget(); |
| 168 |
| 169 this._editor.operation(() => { |
| 170 this._editor.showWidget(); |
| 171 this._editor.setHighlightMode({name: 'devtools-diff', rows: this._diffRows
}); |
| 172 this._editor.setText(this._diffRows.map(row => row.content.map(t => t.text
).join('')).join('\n')); |
| 173 this._editor.setLineNumberFormatter(this._lineFormatter.bind(this)); |
| 174 }); |
| 175 |
| 176 /** |
| 177 * @param {!Array<string>} lines |
| 178 * @param {boolean} atStart |
| 179 * @param {boolean} atEnd |
| 180 * @return {!Array<!Changes.ChangesView.Row>}} |
| 181 */ |
| 182 function createEqualRows(lines, atStart, atEnd) { |
| 183 var equalRows = []; |
| 184 if (!atStart) { |
| 185 for (var i = 0; i < paddingLines && i < lines.length; i++) |
| 186 equalRows.push(createRow(lines[i], Changes.ChangesView.RowType.Equal))
; |
| 187 if (lines.length > paddingLines * 2 + 1 && !atEnd) { |
| 188 equalRows.push(createRow( |
| 189 Common.UIString('( \u2026 Skipping ') + (lines.length - paddingLin
es * 2) + |
| 190 Common.UIString(' matching lines \u2026 )'), |
| 191 Changes.ChangesView.RowType.Spacer)); |
| 192 } |
| 193 } |
| 194 if (!atEnd) { |
| 195 var start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : padd
ingLines); |
| 196 var skip = lines.length - paddingLines - 1; |
| 197 if (!atStart) |
| 198 skip -= paddingLines; |
| 199 if (skip > 0) { |
| 200 baselineLineNumber += skip; |
| 201 currentLineNumber += skip; |
| 202 } |
| 203 |
| 204 for (var i = start; i < lines.length; i++) |
| 205 equalRows.push(createRow(lines[i], Changes.ChangesView.RowType.Equal))
; |
| 206 } |
| 207 return equalRows; |
| 208 } |
| 209 |
| 210 /** |
| 211 * @param {string} before |
| 212 * @param {string} after |
| 213 * @return {!Array<!Changes.ChangesView.Row>}} |
| 214 */ |
| 215 function createModifyRows(before, after) { |
| 216 var internalDiff = Diff.Diff.charDiff(before, after, true /* cleanup diff
*/); |
| 217 var deletionRows = [createRow('', Changes.ChangesView.RowType.Deletion)]; |
| 218 var insertionRows = [createRow('', Changes.ChangesView.RowType.Addition)]; |
| 219 |
| 220 for (var token of internalDiff) { |
| 221 var text = token[1]; |
| 222 var type = token[0]; |
| 223 var className = type === Diff.Diff.Operation.Equal ? '' : 'inner-diff'; |
| 224 var lines = text.split('\n'); |
| 225 for (var i = 0; i < lines.length; i++) { |
| 226 if (i > 0 && type !== Diff.Diff.Operation.Insert) |
| 227 deletionRows.push(createRow('', Changes.ChangesView.RowType.Deletion
)); |
| 228 if (i > 0 && type !== Diff.Diff.Operation.Delete) |
| 229 insertionRows.push(createRow('', Changes.ChangesView.RowType.Additio
n)); |
| 230 if (!lines[i]) |
| 231 continue; |
| 232 if (type !== Diff.Diff.Operation.Insert) |
| 233 deletionRows[deletionRows.length - 1].content.push({text: lines[i],
className}); |
| 234 if (type !== Diff.Diff.Operation.Delete) |
| 235 insertionRows[insertionRows.length - 1].content.push({text: lines[i]
, className}); |
| 236 } |
| 237 } |
| 238 return deletionRows.concat(insertionRows); |
| 239 } |
| 240 |
| 241 /** |
| 242 * @param {string} text |
| 243 * @param {!Changes.ChangesView.RowType} type |
| 244 * @return {!Changes.ChangesView.Row} |
| 245 */ |
| 246 function createRow(text, type) { |
| 247 if (type === Changes.ChangesView.RowType.Addition) |
| 248 currentLineNumber++; |
| 249 if (type === Changes.ChangesView.RowType.Deletion) |
| 250 baselineLineNumber++; |
| 251 if (type === Changes.ChangesView.RowType.Equal) { |
| 252 baselineLineNumber++; |
| 253 currentLineNumber++; |
| 254 } |
| 255 |
| 256 return {baselineLineNumber, currentLineNumber, content: text ? [{text, cla
ssName: 'inner-diff'}] : [], type}; |
| 257 } |
| 258 } |
| 259 |
| 260 /** |
| 261 * @param {number} lineNumber |
| 262 * @return {string} |
| 263 */ |
| 264 _lineFormatter(lineNumber) { |
| 265 var row = this._diffRows[lineNumber - 1]; |
| 266 var showBaseNumber = row.type === Changes.ChangesView.RowType.Deletion; |
| 267 var showCurrentNumber = row.type === Changes.ChangesView.RowType.Addition; |
| 268 if (row.type === Changes.ChangesView.RowType.Equal) { |
| 269 showBaseNumber = true; |
| 270 showCurrentNumber = true; |
| 271 } |
| 272 var base = showBaseNumber ? numberToStringWithSpacesPadding(row.baselineLine
Number, this._maxLineDigits) : |
| 273 spacesPadding(this._maxLineDigits); |
| 274 var current = showCurrentNumber ? numberToStringWithSpacesPadding(row.curren
tLineNumber, this._maxLineDigits) : |
| 275 spacesPadding(this._maxLineDigits); |
| 276 return base + spacesPadding(1) + current; |
| 277 } |
| 278 }; |
| 279 |
| 280 /** |
| 281 * @typedef {!{ |
| 282 * baselineLineNumber: number, |
| 283 * currentLineNumber: number, |
| 284 * content: !Array<!{text: string, className: string}>, |
| 285 * type: !Changes.ChangesView.RowType |
| 286 * }} |
| 287 */ |
| 288 Changes.ChangesView.Row; |
| 289 |
| 290 /** @enum {string} */ |
| 291 Changes.ChangesView.RowType = { |
| 292 Deletion: 'deletion', |
| 293 Addition: 'addition', |
| 294 Equal: 'equal', |
| 295 Spacer: 'spacer' |
| 296 }; |
| OLD | NEW |