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

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

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

Powered by Google App Engine
This is Rietveld 408576698