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

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

Issue 2694923002: DevTools: Changes Drawer (Closed)
Patch Set: slightly better Created 3 years, 10 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 /**
6 * @unrestricted
lushnikov 2017/02/15 02:29:54 why?
einbinder 2017/03/14 01:19:35 Gone.
7 * @implements {UI.ListDelegate}
lushnikov 2017/02/15 02:29:54 template pls
einbinder 2017/03/14 01:19:36 Gone.
8 */
9 Changes.ChangesView = class extends UI.VBox {
10 constructor() {
11 super();
lushnikov 2017/02/15 02:29:54 super(true)
einbinder 2017/03/14 01:19:36 Done.
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
lushnikov 2017/02/15 02:29:54 two lines
einbinder 2017/03/14 01:19:36 Done.
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..');
lushnikov 2017/02/15 02:29:54 UIString
einbinder 2017/03/14 01:19:36 Done.
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._renderRows(originalContent || '', uiSourceCode.workingCopy());
105 this._editor.element.scrollTop = scrollTop;
106 });
107 }
108
109 /**
110 * @param {string} originalContent
111 * @param {string} currentContent
112 */
113 _renderRows(originalContent, currentContent) {
114 this._rows = [];
115 var lineSeparator = originalContent.indexOf('\r\n') >= 0 ? '\r\n' : '\n';
116 var originalLines = originalContent.split(lineSeparator);
117 var currentLines = currentContent.split(lineSeparator);
118
119 var insertions = 0;
120 var deletions = 0;
121 this._maxLineDigits = Math.ceil(Math.log10(Math.max(originalLines.length, cu rrentLines.length)));
122
123 var diff = Diff.Diff.lineDiff(originalLines, currentLines);
lushnikov 2017/02/15 02:29:54 let's extract this and use the entity as a target
124 var currentLineNumber = 0;
125 var baselineLineNumber = 0;
126 var paddingLines = 3;
127
128 for (var i = 0; i < diff.length; ++i) {
129 var token = diff[i];
130 switch (token[0]) {
131 case Diff.Diff.Operation.Equal:
132 this._rows.pushAll(createEqualRows(token[1], i === 0, i === diff.lengt h - 1));
133 break;
134 case Diff.Diff.Operation.Insert:
135 for (var line in token[1])
136 this._rows.push(createRow(line, 'addition'));
137 break;
138 case Diff.Diff.Operation.Delete:
139 if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) {
140 this._rows.pushAll(createModifyRows(token[1].join(lineSeparator), di ff[i + 1][1].join(lineSeparator)));
141 i++;
142 } else {
143 for (var line in token[1])
144 this._rows.push(createRow(line, 'addition'));
145 }
146 break;
147 }
148 }
149 Changes.ChangesView._currentContent = this._rows;
150 this._editor.setText(this._rows.map(row => row.content.map(t => t.text).join ('')).join(lineSeparator));
151 this._editor.setLineNumberFormatter(this._lineFormatter.bind(this));
152
153 this._diffStats.setText(Common.UIString(
154 '%d insertion%s (+), %d deletion%s (-)', insertions, insertions > 1 ? 's ' : '', deletions,
155 deletions > 1 ? 's' : ''));
156
157 /**
158 * @param {!Array<string>} lines
159 * @param {boolean} atStart
160 * @param {boolean} atEnd
161 * @return {!Array<!Changes.ChangesView.Row>}}
162 */
163 function createEqualRows(lines, atStart, atEnd) {
164 var equalRows = [];
165 if (!atStart) {
166 for (var i = 0; i < paddingLines && i < lines.length; i++)
167 equalRows.push(createRow(lines[i], 'equal'));
168 if (lines.length > paddingLines * 2 + 1 && !atEnd) {
169 equalRows.push(createRow(
170 Common.UIString('( \u2026 Skipping ') + (lines.length - paddingLin es * 2) +
171 Common.UIString(' matching lines \u2026 )'),
172 'spacer'));
173 }
174 }
175 if (!atEnd) {
176 var start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : padd ingLines);
177 baselineLineNumber += start - Math.min(atStart ? 0 : paddingLines, lines .length);
178 currentLineNumber += start - Math.min(atStart ? 0 : paddingLines, lines. length);
179 for (var i = start; i < lines.length; i++)
180 equalRows.push(createRow(lines[i], 'equal'));
181 }
182 return equalRows;
183 }
184
185 /**
186 * @param {string} before
187 * @param {string} after
188 * @return {!Array<!Changes.ChangesView.Row>}}
189 */
190 function createModifyRows(before, after) {
191 var internalDiff = Diff.Diff.charDiff(before, after);
192 var deletionRows = [createRow('', 'deletion')];
193 var insertionRows = [createRow('', 'addition')];
194
195 for (var token of internalDiff) {
196 var text = token[1];
197 var type = token[0];
198 var className = type === Diff.Diff.Operation.Equal ? '' : 'double';
199 var first = true;
200 for (var line of text.split(lineSeparator)) {
201 if (first) {
202 first = false;
203 } else {
204 if (type !== Diff.Diff.Operation.Insert)
205 deletionRows.push(createRow('', 'deletion'));
206 if (type !== Diff.Diff.Operation.Delete)
207 insertionRows.push(createRow('', 'addition'));
208 }
209 if (line) {
210 if (type !== Diff.Diff.Operation.Insert)
211 deletionRows[deletionRows.length - 1].content.push({text: line, cl assName: className});
212
213 if (type !== Diff.Diff.Operation.Delete)
214 insertionRows[insertionRows.length - 1].content.push({text: line, className: className});
215 }
216 }
217 }
218 return deletionRows.concat(insertionRows);
219 }
220
221 /**
222 * @param {string} text
223 * @param {string} className
224 * @return {!Changes.ChangesView.Row}
225 */
226 function createRow(text, className) {
227 if (className === 'addition') {
228 currentLineNumber++;
229 insertions++;
230 }
231 if (className === 'deletion') {
232 baselineLineNumber++;
233 deletions++;
234 }
235 if (className === 'equal') {
236 baselineLineNumber++;
237 currentLineNumber++;
238 }
239 return {
240 base: baselineLineNumber,
241 current: currentLineNumber,
242 content: text ? [{text: text, className: 'double'}] : [],
243 className: className,
244 loc: currentLineNumber
245 };
246 }
247 }
248
249 /**
250 * @param {number} lineNumber
251 * @return {string}
252 */
253 _lineFormatter(lineNumber) {
254 var row = this._rows[lineNumber - 1];
255 if (!row)
256 return spacesPadding(this._maxLineDigits * 2 + 1);
257 var showBaseNumber = row.className === 'deletion';
258 var showCurrentNumber = row.className === 'addition';
259 if (row.className === 'equal') {
260 showBaseNumber = true;
261 showCurrentNumber = true;
262 }
263 var base = showBaseNumber ? numberToStringWithSpacesPadding(row.base, this._ maxLineDigits) :
264 spacesPadding(this._maxLineDigits);
265 var current = showCurrentNumber ? numberToStringWithSpacesPadding(row.curren t, this._maxLineDigits) :
266 spacesPadding(this._maxLineDigits);
267 return base + spacesPadding(1) + current;
268 }
269
270 /**
271 * @param {number} baselineLineNumber
272 * @param {number} currentLineNumber
273 * @param {string} text
274 * @param {string=} className
275 * @return {!Element}
276 */
277 _createRow(baselineLineNumber, currentLineNumber, text, className) {
278 var element = createElementWithClass('div', className);
279 element.createChild('span', 'number').textContent = baselineLineNumber ?
280 numberToStringWithSpacesPadding(baselineLineNumber, this._maxLineDigits) :
281 spacesPadding(this._maxLineDigits);
282 element.createChild('span', 'number').textContent = currentLineNumber ?
283 numberToStringWithSpacesPadding(currentLineNumber, this._maxLineDigits) :
284 spacesPadding(this._maxLineDigits);
285 element.createChild('span', 'double').textContent = text;
286 return element;
287 }
288
289 /**
290 * @override
291 * @param {!Element} item
292 * @return {!Element}
293 */
294 createElementForItem(item) {
295 return item;
296 }
297
298 /**
299 * @override
300 * @return {number}
301 */
302 heightForItem() {
303 return 0;
304 }
305
306 /**
307 * @override
308 * @return {boolean}
309 */
310 isItemSelectable(item) {
311 return false;
312 }
313
314 /**
315 * @override
316 */
317 selectedItemChanged() {
318 }
319 };
320
321 /** @typedef {!{base: number, current: number, content: !Array<!{text: string, c lassName: string}>, className: string, loc: number}} */
322 Changes.ChangesView.Row;
323
324 Changes.ChangesNavigator = class extends Sources.NavigatorView {
325 constructor() {
326 super();
327 this.dontGroup();
328 this.element.classList.add('changes-navigator');
329 }
330
331 /**
332 * @override
333 * @param {!Workspace.UISourceCode} uiSourceCode
334 * @return {boolean}
335 */
336 accept(uiSourceCode) {
337 if (uiSourceCode.project().type() !== Workspace.projectTypes.Network)
338 return false;
339
340 if (!uiSourceCode.mightHaveChanges)
341 return false;
342 uiSourceCode.hasChanges().then(hasChanges => {
343 if (!hasChanges)
344 this.refreshUISourceCode(uiSourceCode);
345 });
346 return true;
347 }
348
349 /**
350 * @override
351 * @param {!Workspace.UISourceCode} uiSourceCode
352 * @param {boolean} focusSource
353 */
354 revealSource(uiSourceCode, focusSource) {
355 Changes.ChangesView.SharedInstance._revealUISourceCode(uiSourceCode);
356 if (focusSource)
357 Changes.ChangesView.SharedInstance._editor.focus();
358 }
359
360 /**
361 * @override
362 * @param {!Workspace.UISourceCode} uiSourceCode
363 */
364 uiSourceCodeAdded(uiSourceCode) {
365 if (!Changes.ChangesView.SharedInstance._uiSourceCode)
366 this.revealSource(uiSourceCode, false);
367 }
368
369 /**
370 * @override
371 * @param {!Workspace.UISourceCode} uiSourceCode
372 */
373 uiSourceCodeRemoved(uiSourceCode) {
374 if (Changes.ChangesView.SharedInstance._uiSourceCode !== uiSourceCode)
375 return;
376 var anyUISourceCode = this.anyUISourceCode();
377 if (anyUISourceCode)
378 this.revealSource(anyUISourceCode, false);
379 }
380 };
381
382 Changes.UISourceCodeChange = class {
383 /**
384 * @param {!Workspace.UISourceCode} uiSourceCode
385 */
386 constructor(uiSourceCode) {
387 this.uiSourceCode = uiSourceCode;
388 }
389 };
390
391 /**
392 * @implements {Common.Revealer}
393 */
394 Changes.UISourceCodeChangeRevealer = class {
395 /**
396 * @override
397 * @param {!Object} uiSourceCodeChange
398 * @param {boolean=} omitFocus
399 * @return {!Promise}
400 */
401 reveal(uiSourceCodeChange, omitFocus) {
402 if (!(uiSourceCodeChange instanceof Changes.UISourceCodeChange))
403 return Promise.reject(new Error('Changes.UISourceCodeChange'));
404 Changes.ChangesView.showChanges(uiSourceCodeChange.uiSourceCode);
405 return Promise.resolve();
406 }
407 };
408
409 Changes.ChangesView._currentContent = [];
410 /** @typedef {!{lineNumber: number, index: number}} */
411 Changes.ChangesView.DiffState;
412
413 CodeMirror.defineMode('devtools-diff', function() {
lushnikov 2017/02/15 02:39:19 this potentially will cache the outdated diff. We
einbinder 2017/03/14 01:19:36 Done.
414 return {
415 /**
416 * @return {!Changes.ChangesView.DiffState}
417 */
418 startState: function() {
419 return {lineNumber: 0, index: 0};
420 },
421
422 /**
423 * @param {!{next: function()}} stream
424 * @param {!Changes.ChangesView.DiffState} state
425 * @return {string}
426 */
427 token: function(stream, state) {
428 var row = Changes.ChangesView._currentContent[state.lineNumber];
lushnikov 2017/02/15 02:39:19 you probably can pass this through options of the
einbinder 2017/03/14 01:19:36 Done.
429 if (!row) {
430 stream.next();
431 return '';
432 }
433 var classes = '';
434 if (state.index === 0)
435 classes += ' line-background-' + row.className + ' line-' + row.classNam e;
436 var chars = row.content[state.index].text.length;
437 for (var i = 0; i < chars; i++)
438 stream.next();
439 classes += ' ' + row.content[state.index].className;
440 state.index++;
441 if (state.index >= row.content.length) {
442 state.lineNumber++;
443 state.index = 0;
444 }
445 return classes;
446 },
447
448 /**
449 * @param {!Changes.ChangesView.DiffState} state
450 * @return {string}
451 */
452 blankLine: function(state) {
453 var row = Changes.ChangesView._currentContent[state.lineNumber];
454 state.lineNumber++;
455 return row ? 'line-background-' + row.className + ' line-' + row.className : '';
456 },
457 };
458 });
459
460 CodeMirror.defineMIME('devtools-diff', 'devtools-diff');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698