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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/devtools/front_end/changes/ChangesView.js
diff --git a/third_party/WebKit/Source/devtools/front_end/changes/ChangesView.js b/third_party/WebKit/Source/devtools/front_end/changes/ChangesView.js
new file mode 100644
index 0000000000000000000000000000000000000000..f6834e8c58ffe135e073fadf00bbc3342d9c5657
--- /dev/null
+++ b/third_party/WebKit/Source/devtools/front_end/changes/ChangesView.js
@@ -0,0 +1,460 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @unrestricted
lushnikov 2017/02/15 02:29:54 why?
einbinder 2017/03/14 01:19:35 Gone.
+ * @implements {UI.ListDelegate}
lushnikov 2017/02/15 02:29:54 template pls
einbinder 2017/03/14 01:19:36 Gone.
+ */
+Changes.ChangesView = class extends UI.VBox {
+ constructor() {
+ super();
lushnikov 2017/02/15 02:29:54 super(true)
einbinder 2017/03/14 01:19:36 Done.
+ Changes.ChangesView.SharedInstance = this;
+ this.registerRequiredCSS('changes/changesView.css');
+ var splitWidget = new UI.SplitWidget(true, false);
+ var mainWidget = new UI.Widget();
+ splitWidget.setMainWidget(mainWidget);
+
+ /** @type {?Workspace.UISourceCode} */
+ this._uiSourceCode = null;
+
+ /** @type {!Array<!Changes.ChangesView.Row>} */
+ this._rows = [];
+
+ this._maxLineDigits = 1;
+
+
lushnikov 2017/02/15 02:29:54 two lines
einbinder 2017/03/14 01:19:36 Done.
+ var code = mainWidget.element.createChild('div', 'code');
+ this._editor = new TextEditor.CodeMirrorTextEditor(
+ {lineNumbers: true, lineWrapping: false, mimeType: 'devtools-diff', maxHighlightLength: Infinity});
+ this._editor.setReadOnly(true);
+ this._editor.show(code);
+ this._editor.setLineNumberFormatter(() => '');
+
+ this._toolbar = new UI.Toolbar('changes-toolbar', mainWidget.element);
+ var revertButton = new UI.ToolbarButton(Common.UIString('Revert all changes'), 'largeicon-undo');
+ revertButton.addEventListener(UI.ToolbarButton.Events.Click, this._revert.bind(this));
+ this._toolbar.appendToolbarItem(revertButton);
+ this._diffStats = new UI.ToolbarText('');
+ this._toolbar.appendToolbarItem(this._diffStats);
+ this._toolbar.setEnabled(false);
+
+ this._navigator = new Changes.ChangesNavigator();
+ splitWidget.setSidebarWidget(this._navigator);
+ splitWidget.show(this.element);
+
+ Workspace.workspace.addEventListener(
+ Workspace.Workspace.Events.WorkingCopyCommittedByUser, this._refreshUISourceCode, this);
+ Workspace.workspace.addEventListener(
+ Workspace.Workspace.Events.WorkingCopyChanged, this._refreshUISourceCode, this);
+ }
+
+ /**
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ */
+ static showChanges(uiSourceCode) {
+ UI.viewManager.showView('changes.changes');
+ var changesView =
+ /** @type {!Changes.ChangesView} */ (self.runtime.sharedInstance(Changes.ChangesView));
+ this._navigator.revealUISourceCode(uiSourceCode, true);
+ changesView._revealUISourceCode(uiSourceCode);
+ }
+
+ _revert() {
+ var uiSourceCode = this._uiSourceCode;
+ if (!uiSourceCode)
+ return;
+ uiSourceCode.requestOriginalContent().then(original => uiSourceCode.addRevision(original || ''));
+ }
+
+ /**
+ * @param {!Common.Event} event
+ */
+ _refreshUISourceCode(event) {
+ var uiSourceCode = /** @type !Workspace.UISourceCode */ (event.data.uiSourceCode);
+ this._navigator.refreshUISourceCode(uiSourceCode);
+ if (uiSourceCode === this._uiSourceCode)
+ this._revealUISourceCode(uiSourceCode);
+ }
+
+ /**
+ * @param {?Workspace.UISourceCode} uiSourceCode
+ */
+ _revealUISourceCode(uiSourceCode) {
+ var scrollTop = 0;
+ if (this._uiSourceCode === uiSourceCode) {
+ scrollTop = this._editor.element.scrollTop;
+ } else {
+ this._diffStats.setText('Loading..');
lushnikov 2017/02/15 02:29:54 UIString
einbinder 2017/03/14 01:19:36 Done.
+ this._editor.setText('');
+ }
+
+ this._uiSourceCode = uiSourceCode;
+ this._toolbar.setEnabled(!!uiSourceCode);
+
+ if (!uiSourceCode) {
+ this._rows = [];
+ this._diffStats.setText('');
+ this._editor.setText('');
+ return;
+ }
+ uiSourceCode.requestOriginalContent().then(originalContent => {
+ if (this._uiSourceCode !== uiSourceCode)
+ return;
+ this._renderRows(originalContent || '', uiSourceCode.workingCopy());
+ this._editor.element.scrollTop = scrollTop;
+ });
+ }
+
+ /**
+ * @param {string} originalContent
+ * @param {string} currentContent
+ */
+ _renderRows(originalContent, currentContent) {
+ this._rows = [];
+ var lineSeparator = originalContent.indexOf('\r\n') >= 0 ? '\r\n' : '\n';
+ var originalLines = originalContent.split(lineSeparator);
+ var currentLines = currentContent.split(lineSeparator);
+
+ var insertions = 0;
+ var deletions = 0;
+ this._maxLineDigits = Math.ceil(Math.log10(Math.max(originalLines.length, currentLines.length)));
+
+ 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
+ var currentLineNumber = 0;
+ var baselineLineNumber = 0;
+ var paddingLines = 3;
+
+ for (var i = 0; i < diff.length; ++i) {
+ var token = diff[i];
+ switch (token[0]) {
+ case Diff.Diff.Operation.Equal:
+ this._rows.pushAll(createEqualRows(token[1], i === 0, i === diff.length - 1));
+ break;
+ case Diff.Diff.Operation.Insert:
+ for (var line in token[1])
+ this._rows.push(createRow(line, 'addition'));
+ break;
+ case Diff.Diff.Operation.Delete:
+ if (diff[i + 1] && diff[i + 1][0] === Diff.Diff.Operation.Insert) {
+ this._rows.pushAll(createModifyRows(token[1].join(lineSeparator), diff[i + 1][1].join(lineSeparator)));
+ i++;
+ } else {
+ for (var line in token[1])
+ this._rows.push(createRow(line, 'addition'));
+ }
+ break;
+ }
+ }
+ Changes.ChangesView._currentContent = this._rows;
+ this._editor.setText(this._rows.map(row => row.content.map(t => t.text).join('')).join(lineSeparator));
+ this._editor.setLineNumberFormatter(this._lineFormatter.bind(this));
+
+ this._diffStats.setText(Common.UIString(
+ '%d insertion%s (+), %d deletion%s (-)', insertions, insertions > 1 ? 's' : '', deletions,
+ deletions > 1 ? 's' : ''));
+
+ /**
+ * @param {!Array<string>} lines
+ * @param {boolean} atStart
+ * @param {boolean} atEnd
+ * @return {!Array<!Changes.ChangesView.Row>}}
+ */
+ function createEqualRows(lines, atStart, atEnd) {
+ var equalRows = [];
+ if (!atStart) {
+ for (var i = 0; i < paddingLines && i < lines.length; i++)
+ equalRows.push(createRow(lines[i], 'equal'));
+ if (lines.length > paddingLines * 2 + 1 && !atEnd) {
+ equalRows.push(createRow(
+ Common.UIString('( \u2026 Skipping ') + (lines.length - paddingLines * 2) +
+ Common.UIString(' matching lines \u2026 )'),
+ 'spacer'));
+ }
+ }
+ if (!atEnd) {
+ var start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : paddingLines);
+ baselineLineNumber += start - Math.min(atStart ? 0 : paddingLines, lines.length);
+ currentLineNumber += start - Math.min(atStart ? 0 : paddingLines, lines.length);
+ for (var i = start; i < lines.length; i++)
+ equalRows.push(createRow(lines[i], 'equal'));
+ }
+ return equalRows;
+ }
+
+ /**
+ * @param {string} before
+ * @param {string} after
+ * @return {!Array<!Changes.ChangesView.Row>}}
+ */
+ function createModifyRows(before, after) {
+ var internalDiff = Diff.Diff.charDiff(before, after);
+ var deletionRows = [createRow('', 'deletion')];
+ var insertionRows = [createRow('', 'addition')];
+
+ for (var token of internalDiff) {
+ var text = token[1];
+ var type = token[0];
+ var className = type === Diff.Diff.Operation.Equal ? '' : 'double';
+ var first = true;
+ for (var line of text.split(lineSeparator)) {
+ if (first) {
+ first = false;
+ } else {
+ if (type !== Diff.Diff.Operation.Insert)
+ deletionRows.push(createRow('', 'deletion'));
+ if (type !== Diff.Diff.Operation.Delete)
+ insertionRows.push(createRow('', 'addition'));
+ }
+ if (line) {
+ if (type !== Diff.Diff.Operation.Insert)
+ deletionRows[deletionRows.length - 1].content.push({text: line, className: className});
+
+ if (type !== Diff.Diff.Operation.Delete)
+ insertionRows[insertionRows.length - 1].content.push({text: line, className: className});
+ }
+ }
+ }
+ return deletionRows.concat(insertionRows);
+ }
+
+ /**
+ * @param {string} text
+ * @param {string} className
+ * @return {!Changes.ChangesView.Row}
+ */
+ function createRow(text, className) {
+ if (className === 'addition') {
+ currentLineNumber++;
+ insertions++;
+ }
+ if (className === 'deletion') {
+ baselineLineNumber++;
+ deletions++;
+ }
+ if (className === 'equal') {
+ baselineLineNumber++;
+ currentLineNumber++;
+ }
+ return {
+ base: baselineLineNumber,
+ current: currentLineNumber,
+ content: text ? [{text: text, className: 'double'}] : [],
+ className: className,
+ loc: currentLineNumber
+ };
+ }
+ }
+
+ /**
+ * @param {number} lineNumber
+ * @return {string}
+ */
+ _lineFormatter(lineNumber) {
+ var row = this._rows[lineNumber - 1];
+ if (!row)
+ return spacesPadding(this._maxLineDigits * 2 + 1);
+ var showBaseNumber = row.className === 'deletion';
+ var showCurrentNumber = row.className === 'addition';
+ if (row.className === 'equal') {
+ showBaseNumber = true;
+ showCurrentNumber = true;
+ }
+ var base = showBaseNumber ? numberToStringWithSpacesPadding(row.base, this._maxLineDigits) :
+ spacesPadding(this._maxLineDigits);
+ var current = showCurrentNumber ? numberToStringWithSpacesPadding(row.current, this._maxLineDigits) :
+ spacesPadding(this._maxLineDigits);
+ return base + spacesPadding(1) + current;
+ }
+
+ /**
+ * @param {number} baselineLineNumber
+ * @param {number} currentLineNumber
+ * @param {string} text
+ * @param {string=} className
+ * @return {!Element}
+ */
+ _createRow(baselineLineNumber, currentLineNumber, text, className) {
+ var element = createElementWithClass('div', className);
+ element.createChild('span', 'number').textContent = baselineLineNumber ?
+ numberToStringWithSpacesPadding(baselineLineNumber, this._maxLineDigits) :
+ spacesPadding(this._maxLineDigits);
+ element.createChild('span', 'number').textContent = currentLineNumber ?
+ numberToStringWithSpacesPadding(currentLineNumber, this._maxLineDigits) :
+ spacesPadding(this._maxLineDigits);
+ element.createChild('span', 'double').textContent = text;
+ return element;
+ }
+
+ /**
+ * @override
+ * @param {!Element} item
+ * @return {!Element}
+ */
+ createElementForItem(item) {
+ return item;
+ }
+
+ /**
+ * @override
+ * @return {number}
+ */
+ heightForItem() {
+ return 0;
+ }
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ isItemSelectable(item) {
+ return false;
+ }
+
+ /**
+ * @override
+ */
+ selectedItemChanged() {
+ }
+};
+
+/** @typedef {!{base: number, current: number, content: !Array<!{text: string, className: string}>, className: string, loc: number}} */
+Changes.ChangesView.Row;
+
+Changes.ChangesNavigator = class extends Sources.NavigatorView {
+ constructor() {
+ super();
+ this.dontGroup();
+ this.element.classList.add('changes-navigator');
+ }
+
+ /**
+ * @override
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @return {boolean}
+ */
+ accept(uiSourceCode) {
+ if (uiSourceCode.project().type() !== Workspace.projectTypes.Network)
+ return false;
+
+ if (!uiSourceCode.mightHaveChanges)
+ return false;
+ uiSourceCode.hasChanges().then(hasChanges => {
+ if (!hasChanges)
+ this.refreshUISourceCode(uiSourceCode);
+ });
+ return true;
+ }
+
+ /**
+ * @override
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ * @param {boolean} focusSource
+ */
+ revealSource(uiSourceCode, focusSource) {
+ Changes.ChangesView.SharedInstance._revealUISourceCode(uiSourceCode);
+ if (focusSource)
+ Changes.ChangesView.SharedInstance._editor.focus();
+ }
+
+ /**
+ * @override
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ */
+ uiSourceCodeAdded(uiSourceCode) {
+ if (!Changes.ChangesView.SharedInstance._uiSourceCode)
+ this.revealSource(uiSourceCode, false);
+ }
+
+ /**
+ * @override
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ */
+ uiSourceCodeRemoved(uiSourceCode) {
+ if (Changes.ChangesView.SharedInstance._uiSourceCode !== uiSourceCode)
+ return;
+ var anyUISourceCode = this.anyUISourceCode();
+ if (anyUISourceCode)
+ this.revealSource(anyUISourceCode, false);
+ }
+};
+
+Changes.UISourceCodeChange = class {
+ /**
+ * @param {!Workspace.UISourceCode} uiSourceCode
+ */
+ constructor(uiSourceCode) {
+ this.uiSourceCode = uiSourceCode;
+ }
+};
+
+/**
+ * @implements {Common.Revealer}
+ */
+Changes.UISourceCodeChangeRevealer = class {
+ /**
+ * @override
+ * @param {!Object} uiSourceCodeChange
+ * @param {boolean=} omitFocus
+ * @return {!Promise}
+ */
+ reveal(uiSourceCodeChange, omitFocus) {
+ if (!(uiSourceCodeChange instanceof Changes.UISourceCodeChange))
+ return Promise.reject(new Error('Changes.UISourceCodeChange'));
+ Changes.ChangesView.showChanges(uiSourceCodeChange.uiSourceCode);
+ return Promise.resolve();
+ }
+};
+
+Changes.ChangesView._currentContent = [];
+/** @typedef {!{lineNumber: number, index: number}} */
+Changes.ChangesView.DiffState;
+
+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.
+ return {
+ /**
+ * @return {!Changes.ChangesView.DiffState}
+ */
+ startState: function() {
+ return {lineNumber: 0, index: 0};
+ },
+
+ /**
+ * @param {!{next: function()}} stream
+ * @param {!Changes.ChangesView.DiffState} state
+ * @return {string}
+ */
+ token: function(stream, state) {
+ 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.
+ if (!row) {
+ stream.next();
+ return '';
+ }
+ var classes = '';
+ if (state.index === 0)
+ classes += ' line-background-' + row.className + ' line-' + row.className;
+ var chars = row.content[state.index].text.length;
+ for (var i = 0; i < chars; i++)
+ stream.next();
+ classes += ' ' + row.content[state.index].className;
+ state.index++;
+ if (state.index >= row.content.length) {
+ state.lineNumber++;
+ state.index = 0;
+ }
+ return classes;
+ },
+
+ /**
+ * @param {!Changes.ChangesView.DiffState} state
+ * @return {string}
+ */
+ blankLine: function(state) {
+ var row = Changes.ChangesView._currentContent[state.lineNumber];
+ state.lineNumber++;
+ return row ? 'line-background-' + row.className + ' line-' + row.className : '';
+ },
+ };
+});
+
+CodeMirror.defineMIME('devtools-diff', 'devtools-diff');

Powered by Google App Engine
This is Rietveld 408576698