Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(488)

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/changes/ChangesView.js

Issue 2772643002: DevTools: Changes View (Closed)
Patch Set: Comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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, false);
10 var mainWidget = new UI.Widget();
11 splitWidget.setMainWidget(mainWidget);
12 splitWidget.show(this.contentElement);
13 var diffArea = mainWidget.element.createChild('div', 'diff-area');
14
15 this._emptyWidget = new UI.EmptyWidget(Common.UIString('No changes'));
16 this._emptyWidget.show(diffArea);
17
18 this._workspaceDiff = WorkspaceDiff.workspaceDiff();
19 var fileList = new Changes.FileList(this._workspaceDiff);
20 fileList.on(Changes.FileList.SelectionChanged, this._fileListSelectionChange d, this);
21 splitWidget.setSidebarWidget(fileList);
22
23 /** @type {?Workspace.UISourceCode} */
24 this._uiSourceCode = null;
25
26 /** @type {!Array<!Changes.ChangesView.Row>} */
27 this._rows = [];
28
29 this._maxLineDigits = 1;
30
31 this._editor =
32 new TextEditor.CodeMirrorTextEditor({lineNumbers: true, lineWrapping: fa lse, maxHighlightLength: Infinity});
33 this._editor.setReadOnly(true);
34 this._editor.show(diffArea);
35 this._editor.hideWidget();
36 this._editor.setLineNumberFormatter(() => '');
luoe 2017/03/23 21:59:58 Maybe add a comment to explain how this changes th
einbinder 2017/03/28 00:07:11 Done.
37
38 this._editor.element.addEventListener('click', this._click.bind(this), false );
39
40 this._toolbar = new UI.Toolbar('changes-toolbar', mainWidget.element);
41 var revertButton = new UI.ToolbarButton(Common.UIString('Revert all changes' ), 'largeicon-undo');
42 revertButton.addEventListener(UI.ToolbarButton.Events.Click, this._revert.bi nd(this));
43 this._toolbar.appendToolbarItem(revertButton);
44 this._diffStats = new UI.ToolbarText('');
45 this._toolbar.appendToolbarItem(this._diffStats);
46 this._toolbar.setEnabled(false);
47 }
48
49 /**
50 * @param {!Changes.FileList.SelectionChanged} event
51 */
52 _fileListSelectionChanged(event) {
53 this._revealUISourceCode(event.uiSourceCode);
54 }
55
56 _revert() {
57 var uiSourceCode = this._uiSourceCode;
58 if (!uiSourceCode)
59 return;
60 uiSourceCode.requestOriginalContent().then(original => uiSourceCode.addRevis ion(original || ''));
61 }
62
63 _click(event) {
64 var selection = this._editor.selection();
65 if (!selection.isEmpty())
66 return;
67 var row = this._rows[selection.startLine];
68 if (!row)
69 return;
70 Common.Revealer.reveal(this._uiSourceCode.uiLocation(row.current - 1, select ion.startColumn), false);
71 event.consume(true);
72 }
73
74 /**
75 * @param {?Workspace.UISourceCode} uiSourceCode
76 */
77 _revealUISourceCode(uiSourceCode) {
78 if (this._uiSourceCode === uiSourceCode)
79 return;
80
81 if (this._uiSourceCode)
82 this._workspaceDiff.unsubscribeFromDiffChange(this._uiSourceCode, this._re freshDiff, this);
83 if (uiSourceCode)
84 this._workspaceDiff.subscribeToDiffChange(uiSourceCode, this._refreshDiff, this);
85
86 this._uiSourceCode = uiSourceCode;
87 this._refreshDiff();
88 }
89
90 /**
91 * @override
92 */
93 wasShown() {
94 if (this._uiSourceCode)
95 this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._refres hDiff, this);
96 this._refreshDiff();
97 }
98
99 /**
100 * @override
101 */
102 willHide() {
103 if (this._uiSourceCode)
104 this._workspaceDiff.unsubscribeFromDiffChange(this._uiSourceCode, this._re freshDiff, this);
105 }
106
107 _refreshDiff() {
108 if (!this._uiSourceCode) {
109 this._renderRows(null);
110 return;
111 }
112 var uiSourceCode = this._uiSourceCode;
113 this._workspaceDiff.requestDiff(uiSourceCode).then(diff => {
114 if (this._uiSourceCode !== uiSourceCode)
115 return;
116 this._renderRows(diff);
117 });
118 }
119
120 /**
121 * @param {?Diff.Diff.DiffArray} diff
122 */
123 _renderRows(diff) {
124 this._rows = [];
125
126 if (!diff || (diff.length === 1 && diff[0][0] === Diff.Diff.Operation.Equal) ) {
127 this._diffStats.setText('');
128 this._toolbar.setEnabled(false);
129 this._editor.hideWidget();
130 this._emptyWidget.showWidget();
131 return;
132 }
133
134 var insertions = 0;
135 var deletions = 0;
136 var currentLineNumber = 0;
137 var baselineLineNumber = 0;
138 var paddingLines = 3;
139 var originalLines = [];
140 var currentLines = [];
141
142 for (var i = 0; i < diff.length; ++i) {
143 var token = diff[i];
144 switch (token[0]) {
145 case Diff.Diff.Operation.Equal:
146 this._rows.pushAll(createEqualRows(token[1], i === 0, i === diff.lengt h - 1));
147 originalLines.pushAll(token[1]);
148 currentLines.pushAll(token[1]);
149 break;
150 case Diff.Diff.Operation.Insert:
151 for (var line of token[1])
152 this._rows.push(createRow(line, 'addition'));
153 currentLines.pushAll(token[1]);
154 break;
155 case Diff.Diff.Operation.Delete:
156 originalLines.pushAll(token[1]);
157 if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) {
158 i++;
159 this._rows.pushAll(createModifyRows(token[1].join('\n'), diff[i][1]. join('\n')));
160 currentLines.pushAll(diff[i][1]);
161 } else {
162 for (var line of token[1])
163 this._rows.push(createRow(line, 'deletion'));
164 }
165 break;
166 }
167 }
168
169 this._maxLineDigits = Math.max(Math.ceil(Math.log10(Math.max(currentLineNumb er, baselineLineNumber))), 1);
170
171 this._editor.operation(() => {
172 this._editor.setHighlightMode({
173 name: 'devtools-diff',
174 rows: this._rows,
175 mimeType:
176 Bindings.NetworkProject.uiSourceCodeMimeType(/** @type {!Workspace.U ISourceCode} */ (this._uiSourceCode)),
177 baselineLines: originalLines,
178 currentLines: currentLines
179 });
180 this._editor.setText(this._rows.map(row => row.content.map(t => t.text).jo in('')).join('\n'));
181 this._editor.setLineNumberFormatter(this._lineFormatter.bind(this));
182 });
183
184 this._diffStats.setText(Common.UIString(
185 '%d insertion%s (+), %d deletion%s (-)', insertions, insertions > 1 ? 's ' : '', deletions,
186 deletions > 1 ? 's' : ''));
187 this._toolbar.setEnabled(true);
188 this._emptyWidget.hideWidget();
189 this._editor.showWidget();
190
191 /**
192 * @param {!Array<string>} lines
193 * @param {boolean} atStart
194 * @param {boolean} atEnd
195 * @return {!Array<!Changes.ChangesView.Row>}}
196 */
197 function createEqualRows(lines, atStart, atEnd) {
198 var equalRows = [];
199 if (!atStart) {
200 for (var i = 0; i < paddingLines && i < lines.length; i++)
201 equalRows.push(createRow(lines[i], 'equal'));
202 if (lines.length > paddingLines * 2 + 1 && !atEnd) {
203 equalRows.push(createRow(
204 Common.UIString('( \u2026 Skipping ') + (lines.length - paddingLin es * 2) +
205 Common.UIString(' matching lines \u2026 )'),
206 'spacer'));
207 }
208 }
209 if (!atEnd) {
210 var start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : padd ingLines);
211 var skip = lines.length - paddingLines - 1;
212 if (!atStart)
213 skip -= paddingLines;
214 if (skip > 0) {
215 baselineLineNumber += skip;
216 currentLineNumber += skip;
217 }
218
219 for (var i = start; i < lines.length; i++)
220 equalRows.push(createRow(lines[i], 'equal'));
221 }
222 return equalRows;
223 }
224
225 /**
226 * @param {string} before
227 * @param {string} after
228 * @return {!Array<!Changes.ChangesView.Row>}}
229 */
230 function createModifyRows(before, after) {
231 var internalDiff = Diff.Diff.charDiff(before, after, true);
232 var deletionRows = [createRow('', 'deletion')];
233 var insertionRows = [createRow('', 'addition')];
234
235 for (var token of internalDiff) {
236 var text = token[1];
237 var type = token[0];
238 var className = type === Diff.Diff.Operation.Equal ? '' : 'double';
239 var first = true;
luoe 2017/03/23 21:59:58 I think i === 0 would be clearer
240 for (var line of text.split('\n')) {
241 if (first) {
242 first = false;
243 } else {
244 if (type !== Diff.Diff.Operation.Insert)
245 deletionRows.push(createRow('', 'deletion'));
246 if (type !== Diff.Diff.Operation.Delete)
247 insertionRows.push(createRow('', 'addition'));
248 }
249 if (line) {
250 if (type !== Diff.Diff.Operation.Insert)
251 deletionRows[deletionRows.length - 1].content.push({text: line, cl assName: className});
252
253 if (type !== Diff.Diff.Operation.Delete)
254 insertionRows[insertionRows.length - 1].content.push({text: line, className: className});
255 }
256 }
257 }
258 return deletionRows.concat(insertionRows);
259 }
260
261 /**
262 * @param {string} text
263 * @param {string} className
264 * @return {!Changes.ChangesView.Row}
265 */
266 function createRow(text, className) {
267 if (className === 'addition') {
268 currentLineNumber++;
269 insertions++;
270 }
271 if (className === 'deletion') {
272 baselineLineNumber++;
273 deletions++;
274 }
275 if (className === 'equal') {
276 baselineLineNumber++;
277 currentLineNumber++;
278 }
279 return {
280 base: baselineLineNumber,
281 current: currentLineNumber,
282 content: text ? [{text: text, className: 'double'}] : [],
283 className: className,
284 loc: currentLineNumber
285 };
286 }
287 }
288
289 /**
290 * @param {number} lineNumber
291 * @return {string}
292 */
293 _lineFormatter(lineNumber) {
294 var row = this._rows[lineNumber - 1];
295 if (!row)
296 return spacesPadding(this._maxLineDigits * 2 + 1);
297 var showBaseNumber = row.className === 'deletion';
298 var showCurrentNumber = row.className === 'addition';
299 if (row.className === 'equal') {
300 showBaseNumber = true;
301 showCurrentNumber = true;
302 }
303 var base = showBaseNumber ? numberToStringWithSpacesPadding(row.base, this._ maxLineDigits) :
304 spacesPadding(this._maxLineDigits);
305 var current = showCurrentNumber ? numberToStringWithSpacesPadding(row.curren t, this._maxLineDigits) :
306 spacesPadding(this._maxLineDigits);
307 return base + spacesPadding(1) + current;
308 }
309
310 /**
311 * @param {number} baselineLineNumber
312 * @param {number} currentLineNumber
313 * @param {string} text
314 * @param {string=} className
315 * @return {!Element}
316 */
317 _createRow(baselineLineNumber, currentLineNumber, text, className) {
luoe 2017/03/23 21:59:58 dead code
318 var element = createElementWithClass('div', className);
319 element.createChild('span', 'number').textContent = baselineLineNumber ?
320 numberToStringWithSpacesPadding(baselineLineNumber, this._maxLineDigits) :
321 spacesPadding(this._maxLineDigits);
322 element.createChild('span', 'number').textContent = currentLineNumber ?
323 numberToStringWithSpacesPadding(currentLineNumber, this._maxLineDigits) :
324 spacesPadding(this._maxLineDigits);
325 element.createChild('span', 'double').textContent = text;
326 return element;
327 }
328 };
329
330 /** @typedef {!{base: number, current: number, content: !Array<!{text: string, c lassName: string}>, className: string, loc: number}} */
331 Changes.ChangesView.Row;
332
333 /** @typedef {!{lineNumber: number, index: number}} */
334 Changes.ChangesView.DiffState;
335
336 CodeMirror.defineMode('devtools-diff', function(config, parserConfig) {
337 var rows = parserConfig.rows || [];
338 var baselineLines = parserConfig.baselineLines;
339 var currentLines = parserConfig.currentLines;
340 var innerMode = CodeMirror.getMode({indentUnit: 2}, parserConfig.mimeType);
341
342 function fastForward(state, baselineLineNumber, currentLineNumber) {
343 if (baselineLineNumber > state.baselineLineNumber) {
344 fastForwardState(state.baselineState, state.baselineLineNumber, baselineLi neNumber, baselineLines);
345 state.baselineLineNumber = baselineLineNumber;
346 }
347 if (currentLineNumber > state.currentLineNumber) {
348 fastForwardState(state.currentState, state.currentLineNumber, currentLineN umber, currentLines);
349 state.currentLineNumber = currentLineNumber;
350 }
351 }
352
353 function fastForwardState(state, from, to, lines) {
354 var lineNumber = from;
355 while (lineNumber < to && lineNumber < lines.length) {
356 var stream = new CodeMirror.StringStream(lines[lineNumber]);
357 while (!stream.eol()) {
358 innerMode.token(stream, state);
359 stream.start = stream.pos;
360 }
361 lineNumber++;
362 }
363 }
364
365 return {
366 /**
367 * @return {!Changes.ChangesView.DiffState}
368 */
369 startState: function() {
370 return {
371 lineNumber: 0,
372 index: 0,
373 currentLineNumber: 0,
374 baselineLineNumber: 0,
375 currentState: CodeMirror.startState(innerMode),
376 baselineState: CodeMirror.startState(innerMode),
377 mismatch: 0
378 };
379 },
380
381 /**
382 * @param {!{next: function()}} stream
383 * @param {!Changes.ChangesView.DiffState} state
384 * @return {string}
385 */
386 token: function(stream, state) {
387 var row = rows[state.lineNumber];
388 if (!row) {
389 stream.next();
390 return '';
391 }
392 fastForward(state, row.base - 1, row.current - 1);
393 var classes = '';
394 if (state.index === 0)
395 classes += ' line-background-' + row.className + ' line-' + row.classNam e;
396 var chars = row.content[state.index].text.length;
397 if (state.mismatch > 0)
398 chars = state.mismatch;
399 var pos = stream.pos;
400 var innerStyle = state.lastInnerStyle;
401 var innerPos = pos - state.mismatch;
402 if (state.mismatch >= 0) {
403 if (row.className === 'deletion' || row.className === 'addition' || row. className === 'equal') {
404 innerStyle = innerMode.token(stream, row.className === 'deletion' ? st ate.baselineState : state.currentState);
405 innerPos = stream.pos;
406 } else {
407 innerStyle = '';
408 innerPos = pos + chars;
409 }
410 }
411 stream.pos = Math.min(innerPos, pos + chars);
412 if (innerStyle)
413 classes += ' ' + innerStyle;
414 classes += ' ' + row.content[state.index].className;
415 state.mismatch = pos + chars - innerPos;
416 if (state.mismatch <= 0) {
417 state.index++;
418 state.lastInnerStyle = innerStyle;
419 }
420 if (state.index >= row.content.length) {
421 state.lineNumber++;
422 if (row.className === 'deletion')
423 state.baselineLineNumber++;
424 else
425 state.currentLineNumber++;
426 state.index = 0;
427 }
428 return classes;
429 },
430
431 /**
432 * @param {!Changes.ChangesView.DiffState} state
433 * @return {string}
434 */
435 blankLine: function(state) {
436 var row = rows[state.lineNumber];
437 state.lineNumber++;
438 state.mismatch = 0;
439 state.index = 0;
440 if (!row)
441 return '';
442 if (row.className === 'deletion')
443 state.baselineLineNumber++;
444 else
445 state.currentLineNumber++;
446 var style = '';
447 if (innerMode.blankLine)
448 style = innerMode.blankLine(row.className === 'deletion' ? state.baselin eState : state.currentState);
449 return style + ' line-background-' + row.className + ' line-' + row.classN ame;
450 },
451
452 /**
453 * @param {!Changes.ChangesView.DiffState} state
454 * @return {!Changes.ChangesView.DiffState}
455 */
456 copyState: function(state) {
457 var newState = /** @type {!Changes.ChangesView.DiffState} */ ({});
458 for (var i in state)
459 newState[i] = state[i];
460 newState.currentState = CodeMirror.copyState(innerMode, state.currentState );
461 newState.baselineState = CodeMirror.copyState(innerMode, state.baselineSta te);
462 return newState;
463 }
464 };
465 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698