Chromium Code Reviews| 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 /** | |
| 6 * @unrestricted | |
| 7 * @implements {UI.ListDelegate} | |
| 8 */ | |
| 9 Changes.ChangesView = class extends UI.VBox { | |
| 10 constructor() { | |
| 11 super(); | |
| 12 Changes.ChangesView.SharedInstance = this; | |
| 13 this.registerRequiredCSS('changes/changesView.css'); | |
| 14 var splitWidget = new UI.SplitWidget(true, false); | |
| 15 var mainWidget = new UI.Widget(); | |
| 16 splitWidget.setMainWidget(mainWidget); | |
| 17 | |
| 18 /** @type {?Workspace.UISourceCode} */ | |
| 19 this._uiSourceCode = null; | |
| 20 | |
| 21 /** @type {!Array<!Changes.ChangesView.Row>} */ | |
| 22 this._rows = []; | |
| 23 | |
| 24 this._maxLineDigits = 1; | |
| 25 | |
| 26 | |
| 27 var code = mainWidget.element.createChild('div', 'code'); | |
| 28 this._editor = new TextEditor.CodeMirrorTextEditor( | |
| 29 {lineNumbers: true, lineWrapping: false, mimeType: 'devtools-diff', maxH ighlightLength: Infinity}); | |
| 30 this._editor.setReadOnly(true); | |
| 31 this._editor.show(code); | |
| 32 this._editor.setLineNumberFormatter(() => ''); | |
| 33 | |
| 34 this._toolbar = new UI.Toolbar('changes-toolbar', mainWidget.element); | |
| 35 var revertButton = new UI.ToolbarButton(Common.UIString('Revert all changes' ), 'largeicon-undo'); | |
| 36 revertButton.addEventListener(UI.ToolbarButton.Events.Click, this._revert.bi nd(this)); | |
| 37 this._toolbar.appendToolbarItem(revertButton); | |
| 38 this._diffStats = new UI.ToolbarText(''); | |
| 39 this._toolbar.appendToolbarItem(this._diffStats); | |
| 40 this._toolbar.setEnabled(false); | |
| 41 | |
| 42 this._navigator = new Changes.ChangesNavigator(); | |
| 43 splitWidget.setSidebarWidget(this._navigator); | |
| 44 splitWidget.show(this.element); | |
| 45 | |
| 46 Workspace.workspace.addEventListener( | |
| 47 Workspace.Workspace.Events.WorkingCopyCommittedByUser, this._refreshUISo urceCode, this); | |
| 48 Workspace.workspace.addEventListener( | |
| 49 Workspace.Workspace.Events.WorkingCopyChanged, this._refreshUISourceCode , this); | |
| 50 } | |
| 51 | |
| 52 /** | |
| 53 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 54 */ | |
| 55 static showChanges(uiSourceCode) { | |
| 56 UI.viewManager.showView('changes.changes'); | |
| 57 var changesView = | |
| 58 /** @type {!Changes.ChangesView} */ (self.runtime.sharedInstance(Changes .ChangesView)); | |
| 59 this._navigator.revealUISourceCode(uiSourceCode, true); | |
| 60 changesView._revealUISourceCode(uiSourceCode); | |
| 61 } | |
| 62 | |
| 63 _revert() { | |
| 64 var uiSourceCode = this._uiSourceCode; | |
| 65 if (!uiSourceCode) | |
| 66 return; | |
| 67 uiSourceCode.requestOriginalContent().then(original => uiSourceCode.addRevis ion(original || '')); | |
| 68 } | |
| 69 | |
| 70 /** | |
| 71 * @param {!Common.Event} event | |
| 72 */ | |
| 73 _refreshUISourceCode(event) { | |
| 74 var uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data.uiSource Code); | |
| 75 this._navigator.refreshUISourceCode(uiSourceCode); | |
| 76 if (uiSourceCode === this._uiSourceCode) | |
| 77 this._revealUISourceCode(uiSourceCode); | |
| 78 } | |
| 79 | |
| 80 /** | |
| 81 * @param {?Workspace.UISourceCode} uiSourceCode | |
| 82 */ | |
| 83 _revealUISourceCode(uiSourceCode) { | |
| 84 var scrollTop = 0; | |
| 85 if (this._uiSourceCode === uiSourceCode) { | |
| 86 scrollTop = this._editor.element.scrollTop; | |
| 87 } else { | |
| 88 this._diffStats.setText('Loading..'); | |
| 89 this._editor.setText(''); | |
| 90 } | |
| 91 | |
| 92 this._uiSourceCode = uiSourceCode; | |
| 93 this._toolbar.setEnabled(!!uiSourceCode); | |
| 94 | |
| 95 if (!uiSourceCode) { | |
| 96 this._rows = []; | |
| 97 this._diffStats.setText(''); | |
| 98 this._editor.setText(''); | |
| 99 return; | |
| 100 } | |
| 101 uiSourceCode.requestOriginalContent().then(originalContent => { | |
| 102 if (this._uiSourceCode !== uiSourceCode) | |
| 103 return; | |
| 104 this._rows = []; | |
|
lushnikov
2017/02/14 00:05:11
this is scary to follow
| |
| 105 var lineSeparator = (originalContent || '').indexOf('\r\n') >= 0 ? '\r\n' : '\n'; | |
| 106 var originalLines = (originalContent || '').split(lineSeparator); | |
| 107 var currentLines = uiSourceCode.workingCopy().split(lineSeparator); | |
| 108 | |
| 109 var insertions = 0; | |
| 110 var deletions = 0; | |
| 111 this._maxLineDigits = Math.ceil(Math.log10(Math.max(originalLines.length, currentLines.length))); | |
| 112 | |
| 113 /** | |
| 114 * @param {number} numbers | |
| 115 * @param {string} text | |
| 116 * @param {string} className | |
| 117 * @return {!Changes.ChangesView.Row} | |
| 118 */ | |
| 119 var createRow = (numbers, text, className) => { | |
| 120 if (className === 'addition') | |
| 121 insertions++; | |
| 122 if (className === 'deletion') | |
| 123 deletions++; | |
| 124 return { | |
| 125 base: numbers & 1 ? baselineLineNumber : 0, | |
| 126 current: numbers & 2 ? currentLineNumber : 0, | |
| 127 content: text ? [{text: text, className: 'double'}] : [], | |
| 128 className: className, | |
| 129 loc: currentLineNumber | |
| 130 }; | |
| 131 }; | |
| 132 | |
| 133 var diff = Diff.Diff.lineDiff(originalLines, currentLines); | |
| 134 var currentLineNumber = 0; | |
| 135 var baselineLineNumber = 0; | |
| 136 var paddingLines = 3; | |
| 137 | |
| 138 for (var i = 0; i < diff.length; ++i) { | |
| 139 var token = diff[i]; | |
| 140 if (token[0] === Diff.Diff.Operation.Insert && diff[i - 1] && diff[i - 1 ][0] === Diff.Diff.Operation.Delete) | |
|
lushnikov
2017/02/14 00:05:10
it feels like this all is already written in sourc
| |
| 141 continue; | |
| 142 if (token[0] === Diff.Diff.Operation.Equal) { | |
| 143 if (i > 0) { | |
| 144 for (var j = 0; j < paddingLines && j < token[1].length; j++) { | |
| 145 baselineLineNumber++; | |
| 146 currentLineNumber++; | |
| 147 this._rows.push(createRow(3, token[1][j], 'equal')); | |
| 148 } | |
| 149 if (token[1].length > paddingLines * 2 + 1 && i < diff.length - 1) { | |
| 150 this._rows.push(createRow( | |
| 151 0, Common.UIString('( \u2026 Skipping ') + (token[1].length - paddingLines * 2) + | |
| 152 Common.UIString(' matching lines \u2026 )'), | |
| 153 'spacer')); | |
| 154 } | |
| 155 } | |
| 156 if (i < diff.length - 1) { | |
| 157 var start = Math.max(token[1].length - paddingLines - 1, i > 0 ? pad dingLines : 0); | |
| 158 baselineLineNumber += start - Math.min(i > 0 ? paddingLines : 0, tok en[1].length); | |
| 159 currentLineNumber += start - Math.min(i > 0 ? paddingLines : 0, toke n[1].length); | |
| 160 for (var j = start; j < token[1].length; j++) { | |
| 161 baselineLineNumber++; | |
| 162 currentLineNumber++; | |
| 163 this._rows.push(createRow(3, token[1][j], 'equal')); | |
| 164 } | |
| 165 } | |
| 166 continue; | |
| 167 } | |
| 168 var deletion = token[0] === Diff.Diff.Operation.Delete; | |
| 169 if (deletion && diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.In sert) { | |
| 170 var internalDiff = Diff.Diff.charDiff(token[1].join(lineSeparator), di ff[i + 1][1].join(lineSeparator)); | |
| 171 baselineLineNumber++; | |
| 172 currentLineNumber++; | |
| 173 var deletionElements = [createRow(1, '', 'deletion')]; | |
| 174 var insertionElements = [createRow(2, '', 'addition')]; | |
| 175 | |
| 176 for (var j = 0; j < internalDiff.length; j++) { | |
| 177 var text = internalDiff[j][1]; | |
| 178 var type = internalDiff[j][0]; | |
| 179 var className = type === Diff.Diff.Operation.Equal ? '' : 'double'; | |
| 180 var first = true; | |
| 181 for (var line of text.split(lineSeparator)) { | |
| 182 if (first) { | |
| 183 first = false; | |
| 184 } else { | |
| 185 if (type !== Diff.Diff.Operation.Insert) { | |
| 186 baselineLineNumber++; | |
| 187 deletionElements.push(createRow(1, '', 'deletion')); | |
| 188 } | |
| 189 if (type !== Diff.Diff.Operation.Delete) { | |
| 190 currentLineNumber++; | |
| 191 insertionElements.push(createRow(2, '', 'addition')); | |
| 192 } | |
| 193 } | |
| 194 if (type !== Diff.Diff.Operation.Insert) | |
| 195 deletionElements[deletionElements.length - 1].content.push({text : line, className: className}); | |
| 196 | |
| 197 if (type !== Diff.Diff.Operation.Delete) | |
| 198 insertionElements[insertionElements.length - 1].content.push({te xt: line, className: className}); | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 this._rows = this._rows.concat(deletionElements); | |
| 203 this._rows = this._rows.concat(insertionElements); | |
| 204 continue; | |
| 205 } else { | |
| 206 for (var j = 0; j < token[1].length; j++) { | |
| 207 if (deletion) { | |
| 208 baselineLineNumber++; | |
| 209 this._rows.push(createRow(1, token[1][j], 'deletion')); | |
| 210 } else { | |
| 211 currentLineNumber++; | |
| 212 this._rows.push(createRow(2, token[1][j], 'addition')); | |
| 213 } | |
| 214 } | |
| 215 } | |
| 216 } | |
| 217 Changes.ChangesView._currentContent = this._rows; | |
| 218 this._editor.setText(this._rows.map(row => row.content.map(t => t.text).jo in('')).join(lineSeparator)); | |
| 219 this._editor.setLineNumberFormatter(this._lineFormatter.bind(this)); | |
| 220 this._editor.element.scrollTop = scrollTop; | |
| 221 | |
| 222 this._diffStats.setText(Common.UIString( | |
| 223 '%d insertion%s (+), %d deletion%s (-)', insertions, insertions > 1 ? 's' : '', deletions, | |
| 224 deletions > 1 ? 's' : '')); | |
| 225 }); | |
| 226 } | |
| 227 | |
| 228 /** | |
| 229 * @param {number} lineNumber | |
| 230 * @return {string} | |
| 231 */ | |
| 232 _lineFormatter(lineNumber) { | |
| 233 var row = this._rows[lineNumber - 1]; | |
| 234 if (!row) | |
| 235 return spacesPadding(this._maxLineDigits * 2 + 1); | |
| 236 var base = | |
| 237 row.base ? numberToStringWithSpacesPadding(row.base, this._maxLineDigits ) : spacesPadding(this._maxLineDigits); | |
| 238 var current = row.current ? numberToStringWithSpacesPadding(row.current, thi s._maxLineDigits) : | |
| 239 spacesPadding(this._maxLineDigits); | |
| 240 return base + spacesPadding(1) + current; | |
| 241 } | |
| 242 | |
| 243 /** | |
| 244 * @param {number} baselineLineNumber | |
| 245 * @param {number} currentLineNumber | |
| 246 * @param {string} text | |
| 247 * @param {string=} className | |
| 248 * @return {!Element} | |
| 249 */ | |
| 250 _createRow(baselineLineNumber, currentLineNumber, text, className) { | |
| 251 var element = createElementWithClass('div', className); | |
| 252 element.createChild('span', 'number').textContent = baselineLineNumber ? | |
| 253 numberToStringWithSpacesPadding(baselineLineNumber, this._maxLineDigits) : | |
| 254 spacesPadding(this._maxLineDigits); | |
| 255 element.createChild('span', 'number').textContent = currentLineNumber ? | |
| 256 numberToStringWithSpacesPadding(currentLineNumber, this._maxLineDigits) : | |
| 257 spacesPadding(this._maxLineDigits); | |
| 258 element.createChild('span', 'double').textContent = text; | |
| 259 return element; | |
| 260 } | |
| 261 | |
| 262 /** | |
| 263 * @override | |
| 264 * @param {!Element} item | |
| 265 * @return {!Element} | |
| 266 */ | |
| 267 createElementForItem(item) { | |
| 268 return item; | |
| 269 } | |
| 270 | |
| 271 /** | |
| 272 * @override | |
| 273 * @return {number} | |
| 274 */ | |
| 275 heightForItem() { | |
| 276 return 0; | |
| 277 } | |
| 278 | |
| 279 /** | |
| 280 * @override | |
| 281 * @return {boolean} | |
| 282 */ | |
| 283 isItemSelectable(item) { | |
| 284 return false; | |
| 285 } | |
| 286 | |
| 287 /** | |
| 288 * @override | |
| 289 */ | |
| 290 selectedItemChanged() { | |
| 291 } | |
| 292 }; | |
| 293 | |
| 294 /** @typedef {!{base: number, current: number, content: !Array<!{text: string, c lassName: string}>, className: string, loc: number}} */ | |
| 295 Changes.ChangesView.Row; | |
| 296 | |
| 297 Changes.ChangesNavigator = class extends Sources.NavigatorView { | |
| 298 constructor() { | |
| 299 super(); | |
| 300 this.dontGroup(); | |
| 301 this.element.classList.add('changes-navigator'); | |
| 302 } | |
| 303 | |
| 304 /** | |
| 305 * @override | |
| 306 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 307 * @return {boolean} | |
| 308 */ | |
| 309 accept(uiSourceCode) { | |
| 310 if (uiSourceCode.project().type() !== Workspace.projectTypes.Network) | |
| 311 return false; | |
| 312 | |
| 313 if (!uiSourceCode.mightHaveChanges) | |
| 314 return false; | |
| 315 uiSourceCode.hasChanges().then(hasChanges => { | |
| 316 if (!hasChanges) | |
| 317 this.refreshUISourceCode(uiSourceCode); | |
| 318 }); | |
| 319 return true; | |
| 320 } | |
| 321 | |
| 322 /** | |
| 323 * @override | |
| 324 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 325 * @param {boolean} focusSource | |
| 326 */ | |
| 327 revealSource(uiSourceCode, focusSource) { | |
| 328 Changes.ChangesView.SharedInstance._revealUISourceCode(uiSourceCode); | |
| 329 if (focusSource) | |
| 330 Changes.ChangesView.SharedInstance._editor.focus(); | |
| 331 } | |
| 332 | |
| 333 /** | |
| 334 * @override | |
| 335 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 336 */ | |
| 337 uiSourceCodeAdded(uiSourceCode) { | |
| 338 if (!Changes.ChangesView.SharedInstance._uiSourceCode) | |
| 339 this.revealSource(uiSourceCode, false); | |
| 340 } | |
| 341 | |
| 342 /** | |
| 343 * @override | |
| 344 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 345 */ | |
| 346 uiSourceCodeRemoved(uiSourceCode) { | |
| 347 if (Changes.ChangesView.SharedInstance._uiSourceCode !== uiSourceCode) | |
| 348 return; | |
| 349 var anyUISourceCode = this.anyUISourceCode(); | |
| 350 if (anyUISourceCode) | |
| 351 this.revealSource(anyUISourceCode, false); | |
| 352 } | |
| 353 }; | |
| 354 | |
| 355 Changes.UISourceCodeChange = class { | |
| 356 /** | |
| 357 * @param {!Workspace.UISourceCode} uiSourceCode | |
| 358 */ | |
| 359 constructor(uiSourceCode) { | |
| 360 this.uiSourceCode = uiSourceCode; | |
| 361 } | |
| 362 }; | |
| 363 | |
| 364 /** | |
| 365 * @implements {Common.Revealer} | |
| 366 */ | |
| 367 Changes.UISourceCodeChangeRevealer = class { | |
| 368 /** | |
| 369 * @override | |
| 370 * @param {!Object} uiSourceCodeChange | |
| 371 * @param {boolean=} omitFocus | |
| 372 * @return {!Promise} | |
| 373 */ | |
| 374 reveal(uiSourceCodeChange, omitFocus) { | |
| 375 if (!(uiSourceCodeChange instanceof Changes.UISourceCodeChange)) | |
| 376 return Promise.reject(new Error('Changes.UISourceCodeChange')); | |
| 377 Changes.ChangesView.showChanges(uiSourceCodeChange.uiSourceCode); | |
| 378 return Promise.resolve(); | |
| 379 } | |
| 380 }; | |
| 381 | |
| 382 Changes.ChangesView._currentContent = []; | |
| 383 /** @typedef {!{lineNumber: number, index: number}} */ | |
| 384 Changes.ChangesView.DiffState; | |
| 385 | |
| 386 CodeMirror.defineMode('devtools-diff', function() { | |
| 387 return { | |
| 388 /** | |
| 389 * @return {!Changes.ChangesView.DiffState} | |
| 390 */ | |
| 391 startState: function() { | |
| 392 return {lineNumber: 0, index: 0}; | |
| 393 }, | |
| 394 | |
| 395 /** | |
| 396 * @param {!{next: function()}} stream | |
| 397 * @param {!Changes.ChangesView.DiffState} state | |
| 398 * @return {string} | |
| 399 */ | |
| 400 token: function(stream, state) { | |
| 401 var row = Changes.ChangesView._currentContent[state.lineNumber]; | |
|
lushnikov
2017/02/14 00:05:10
let's not use singleton
| |
| 402 if (!row) { | |
| 403 stream.next(); | |
| 404 return ''; | |
| 405 } | |
| 406 var classes = ''; | |
| 407 if (state.index === 0) | |
| 408 classes += ' line-background-' + row.className + ' line-' + row.classNam e; | |
| 409 var chars = row.content[state.index].text.length; | |
| 410 for (var i = 0; i < chars; i++) | |
| 411 stream.next(); | |
| 412 classes += ' ' + row.content[state.index].className; | |
| 413 state.index++; | |
| 414 if (state.index >= row.content.length) { | |
| 415 state.lineNumber++; | |
| 416 state.index = 0; | |
| 417 } | |
| 418 return classes; | |
| 419 }, | |
| 420 | |
| 421 /** | |
| 422 * @param {!Changes.ChangesView.DiffState} state | |
| 423 * @return {string} | |
| 424 */ | |
| 425 blankLine: function(state) { | |
| 426 var row = Changes.ChangesView._currentContent[state.lineNumber]; | |
| 427 state.lineNumber++; | |
| 428 return row ? 'line-background-' + row.className + ' line-' + row.className : ''; | |
| 429 }, | |
| 430 }; | |
| 431 }); | |
| 432 | |
| 433 CodeMirror.defineMIME('devtools-diff', 'devtools-diff'); | |
| OLD | NEW |