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

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

Issue 2694923002: DevTools: Changes Drawer (Closed)
Patch Set: Undo icon 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
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');
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698