Commit 4648e7b6 authored by Joel Einbinder's avatar Joel Einbinder Committed by Commit Bot

DevTools: Extract the gutter diff into GutterDiffPlugin

Bug: 778043
Change-Id: I7f80eb1553eb48456441d5ccab6be903875bbcff
Reviewed-on: https://chromium-review.googlesource.com/1023033
Commit-Queue: Andrey Lushnikov <lushnikov@chromium.org>
Reviewed-by: default avatarAndrey Lushnikov <lushnikov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#553319}
parent 38837f83
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
function onBeforeUISourceCode(uiSourceCode) { function onBeforeUISourceCode(uiSourceCode) {
uiSourceCode.setWorkingCopy(textAfter); uiSourceCode.setWorkingCopy(textAfter);
TestRunner.addSniffer(SourceFrame.SourceCodeDiff.prototype, '_decorationsSetForTest', decorationsSet); TestRunner.addSniffer(
Sources.GutterDiffPlugin.prototype, '_decorationsSetForTest',
decorationsSet);
SourcesTestRunner.showUISourceCodePromise(uiSourceCode); SourcesTestRunner.showUISourceCodePromise(uiSourceCode);
} }
...@@ -32,11 +34,11 @@ ...@@ -32,11 +34,11 @@
function print(decoration) { function print(decoration) {
var type = decoration[1].type; var type = decoration[1].type;
var name = 'Unknown'; var name = 'Unknown';
if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Insert) if (type === SourceFrame.SourceCodeDiff.EditType.Insert)
name = 'Insert'; name = 'Insert';
else if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Delete) else if (type === SourceFrame.SourceCodeDiff.EditType.Delete)
name = 'Delete'; name = 'Delete';
else if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Modify) else if (type === SourceFrame.SourceCodeDiff.EditType.Modify)
name = 'Modify'; name = 'Modify';
TestRunner.addResult(decoration[0] + ':' + name); TestRunner.addResult(decoration[0] + ':' + name);
......
...@@ -656,6 +656,7 @@ all_devtools_files = [ ...@@ -656,6 +656,7 @@ all_devtools_files = [
"front_end/sources/FilePathScoreFunction.js", "front_end/sources/FilePathScoreFunction.js",
"front_end/sources/FilteredUISourceCodeListProvider.js", "front_end/sources/FilteredUISourceCodeListProvider.js",
"front_end/sources/GoToLineQuickOpen.js", "front_end/sources/GoToLineQuickOpen.js",
"front_end/sources/GutterDiffPlugin.js",
"front_end/sources/InplaceFormatterEditorAction.js", "front_end/sources/InplaceFormatterEditorAction.js",
"front_end/sources/javaScriptBreakpointsSidebarPane.css", "front_end/sources/javaScriptBreakpointsSidebarPane.css",
"front_end/sources/JavaScriptBreakpointsSidebarPane.js", "front_end/sources/JavaScriptBreakpointsSidebarPane.js",
......
// Copyright 2016 The Chromium Authors. All rights reserved. // Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
/**
* @unrestricted
*/
SourceFrame.SourceCodeDiff = class { SourceFrame.SourceCodeDiff = class {
/** /**
* @param {!WorkspaceDiff.WorkspaceDiff} workspaceDiff
* @param {!TextEditor.CodeMirrorTextEditor} textEditor * @param {!TextEditor.CodeMirrorTextEditor} textEditor
*/ */
constructor(workspaceDiff, textEditor) { constructor(textEditor) {
this._textEditor = textEditor; this._textEditor = textEditor;
this._decorations = [];
this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, true);
this._uiSourceCode = null;
this._workspaceDiff = workspaceDiff;
/** @type {!Array<!TextEditor.TextEditorPositionHandle>}*/ /** @type {!Array<!TextEditor.TextEditorPositionHandle>}*/
this._animatedLines = []; this._animatedLines = [];
/** @type {?number} */
this._update(); this._animationTimeout = null;
}
/**
* @param {?Workspace.UISourceCode} uiSourceCode
*/
setUISourceCode(uiSourceCode) {
if (uiSourceCode === this._uiSourceCode)
return;
if (this._uiSourceCode)
this._workspaceDiff.unsubscribeFromDiffChange(this._uiSourceCode, this._update, this);
if (uiSourceCode)
this._workspaceDiff.subscribeToDiffChange(uiSourceCode, this._update, this);
this._uiSourceCode = uiSourceCode;
this._update();
} }
/** /**
...@@ -43,11 +22,12 @@ SourceFrame.SourceCodeDiff = class { ...@@ -43,11 +22,12 @@ SourceFrame.SourceCodeDiff = class {
if (typeof oldContent !== 'string' || typeof newContent !== 'string') if (typeof oldContent !== 'string' || typeof newContent !== 'string')
return; return;
const diff = this._computeDiff(Diff.Diff.lineDiff(oldContent.split('\n'), newContent.split('\n'))); const diff =
SourceFrame.SourceCodeDiff.computeDiff(Diff.Diff.lineDiff(oldContent.split('\n'), newContent.split('\n')));
const changedLines = []; const changedLines = [];
for (let i = 0; i < diff.length; ++i) { for (let i = 0; i < diff.length; ++i) {
const diffEntry = diff[i]; const diffEntry = diff[i];
if (diffEntry.type === SourceFrame.SourceCodeDiff.GutterDecorationType.Delete) if (diffEntry.type === SourceFrame.SourceCodeDiff.EditType.Delete)
continue; continue;
for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber) { for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber) {
const position = this._textEditor.textEditorPositionHandle(lineNumber, 0); const position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
...@@ -91,26 +71,11 @@ SourceFrame.SourceCodeDiff = class { ...@@ -91,26 +71,11 @@ SourceFrame.SourceCodeDiff = class {
} }
} }
/**
* @param {!Array<!SourceFrame.SourceCodeDiff.GutterDecoration>} removed
* @param {!Array<!SourceFrame.SourceCodeDiff.GutterDecoration>} added
*/
_updateDecorations(removed, added) {
this._textEditor.operation(operation);
function operation() {
for (const decoration of removed)
decoration.remove();
for (const decoration of added)
decoration.install();
}
}
/** /**
* @param {!Diff.Diff.DiffArray} diff * @param {!Diff.Diff.DiffArray} diff
* @return {!Array<!{type: !SourceFrame.SourceCodeDiff.GutterDecorationType, from: number, to: number}>} * @return {!Array<!{type: !SourceFrame.SourceCodeDiff.EditType, from: number, to: number}>}
*/ */
_computeDiff(diff) { static computeDiff(diff) {
const result = []; const result = [];
let hasAdded = false; let hasAdded = false;
let hasRemoved = false; let hasRemoved = false;
...@@ -141,22 +106,22 @@ SourceFrame.SourceCodeDiff = class { ...@@ -141,22 +106,22 @@ SourceFrame.SourceCodeDiff = class {
if (isInsideBlock) if (isInsideBlock)
flush(); flush();
if (result.length > 1 && result[0].from === 0 && result[1].from === 0) { if (result.length > 1 && result[0].from === 0 && result[1].from === 0) {
const merged = {type: SourceFrame.SourceCodeDiff.GutterDecorationType.Modify, from: 0, to: result[1].to}; const merged = {type: SourceFrame.SourceCodeDiff.EditType.Modify, from: 0, to: result[1].to};
result.splice(0, 2, merged); result.splice(0, 2, merged);
} }
return result; return result;
function flush() { function flush() {
let type = SourceFrame.SourceCodeDiff.GutterDecorationType.Insert; let type = SourceFrame.SourceCodeDiff.EditType.Insert;
let from = blockStartLineNumber; let from = blockStartLineNumber;
let to = currentLineNumber; let to = currentLineNumber;
if (hasAdded && hasRemoved) { if (hasAdded && hasRemoved) {
type = SourceFrame.SourceCodeDiff.GutterDecorationType.Modify; type = SourceFrame.SourceCodeDiff.EditType.Modify;
} else if (!hasAdded && hasRemoved && from === 0 && to === 0) { } else if (!hasAdded && hasRemoved && from === 0 && to === 0) {
type = SourceFrame.SourceCodeDiff.GutterDecorationType.Modify; type = SourceFrame.SourceCodeDiff.EditType.Modify;
to = 1; to = 1;
} else if (!hasAdded && hasRemoved) { } else if (!hasAdded && hasRemoved) {
type = SourceFrame.SourceCodeDiff.GutterDecorationType.Delete; type = SourceFrame.SourceCodeDiff.EditType.Delete;
from -= 1; from -= 1;
} }
result.push({type: type, from: from, to: to}); result.push({type: type, from: from, to: to});
...@@ -165,122 +130,11 @@ SourceFrame.SourceCodeDiff = class { ...@@ -165,122 +130,11 @@ SourceFrame.SourceCodeDiff = class {
hasRemoved = false; hasRemoved = false;
} }
} }
_update() {
if (this._uiSourceCode)
this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.bind(this));
else
this._innerUpdate(null);
}
/**
* @param {?Diff.Diff.DiffArray} lineDiff
*/
_innerUpdate(lineDiff) {
if (!lineDiff) {
this._updateDecorations(this._decorations, []);
this._decorations = [];
return;
}
/** @type {!Map<number, !SourceFrame.SourceCodeDiff.GutterDecoration>} */
const oldDecorations = new Map();
for (let i = 0; i < this._decorations.length; ++i) {
const decoration = this._decorations[i];
const lineNumber = decoration.lineNumber();
if (lineNumber === -1)
continue;
oldDecorations.set(lineNumber, decoration);
}
const diff = this._computeDiff(lineDiff);
/** @type {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.GutterDecorationType}>} */
const newDecorations = new Map();
for (let i = 0; i < diff.length; ++i) {
const diffEntry = diff[i];
for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber)
newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry.type});
}
const decorationDiff = oldDecorations.diff(newDecorations, (e1, e2) => e1.type === e2.type);
const addedDecorations = decorationDiff.added.map(
entry => new SourceFrame.SourceCodeDiff.GutterDecoration(this._textEditor, entry.lineNumber, entry.type));
this._decorations = decorationDiff.equal.concat(addedDecorations);
this._updateDecorations(decorationDiff.removed, addedDecorations);
this._decorationsSetForTest(newDecorations);
}
/**
* @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.GutterDecorationType}>} decorations
*/
_decorationsSetForTest(decorations) {
}
dispose() {
if (this._uiSourceCode)
WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this);
}
}; };
/** @type {string} */
SourceFrame.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff';
/** @enum {symbol} */ /** @enum {symbol} */
SourceFrame.SourceCodeDiff.GutterDecorationType = { SourceFrame.SourceCodeDiff.EditType = {
Insert: Symbol('Insert'), Insert: Symbol('Insert'),
Delete: Symbol('Delete'), Delete: Symbol('Delete'),
Modify: Symbol('Modify'), Modify: Symbol('Modify'),
}; };
/**
* @unrestricted
*/
SourceFrame.SourceCodeDiff.GutterDecoration = class {
/**
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
* @param {number} lineNumber
* @param {!SourceFrame.SourceCodeDiff.GutterDecorationType} type
*/
constructor(textEditor, lineNumber, type) {
this._textEditor = textEditor;
this._position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
this._className = '';
if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Insert)
this._className = 'diff-entry-insert';
else if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Delete)
this._className = 'diff-entry-delete';
else if (type === SourceFrame.SourceCodeDiff.GutterDecorationType.Modify)
this._className = 'diff-entry-modify';
this.type = type;
}
/**
* @return {number}
*/
lineNumber() {
const location = this._position.resolve();
if (!location)
return -1;
return location.lineNumber;
}
install() {
const location = this._position.resolve();
if (!location)
return;
const element = createElementWithClass('div', 'diff-marker');
element.textContent = '\u00A0';
this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, element);
this._textEditor.toggleLineClass(location.lineNumber, this._className, true);
}
remove() {
const location = this._position.resolve();
if (!location)
return;
this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, null);
this._textEditor.toggleLineClass(location.lineNumber, this._className, false);
}
};
// Copyright 2018 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.
Sources.GutterDiffPlugin = class extends Sources.UISourceCodeFrame.Plugin {
/**
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
* @param {!Workspace.UISourceCode} uiSourceCode
*/
constructor(textEditor, uiSourceCode) {
super();
this._textEditor = textEditor;
this._uiSourceCode = uiSourceCode;
/** @type {!Array<!Sources.GutterDiffPlugin.GutterDecoration>} */
this._decorations = [];
this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, true);
this._workspaceDiff = WorkspaceDiff.workspaceDiff();
this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update, this);
this._update();
}
/**
* @param {!Workspace.UISourceCode} uiSourceCode
* @return {boolean}
*/
static accepts(uiSourceCode) {
return uiSourceCode.project().type() === Workspace.projectTypes.Network;
}
/**
* @param {!Array<!Sources.GutterDiffPlugin.GutterDecoration>} removed
* @param {!Array<!Sources.GutterDiffPlugin.GutterDecoration>} added
*/
_updateDecorations(removed, added) {
this._textEditor.operation(operation);
function operation() {
for (const decoration of removed)
decoration.remove();
for (const decoration of added)
decoration.install();
}
}
_update() {
if (this._uiSourceCode)
this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.bind(this));
else
this._innerUpdate(null);
}
/**
* @param {?Diff.Diff.DiffArray} lineDiff
*/
_innerUpdate(lineDiff) {
if (!lineDiff) {
this._updateDecorations(this._decorations, []);
this._decorations = [];
return;
}
/** @type {!Map<number, !Sources.GutterDiffPlugin.GutterDecoration>} */
const oldDecorations = new Map();
for (let i = 0; i < this._decorations.length; ++i) {
const decoration = this._decorations[i];
const lineNumber = decoration.lineNumber();
if (lineNumber === -1)
continue;
oldDecorations.set(lineNumber, decoration);
}
const diff = SourceFrame.SourceCodeDiff.computeDiff(lineDiff);
/** @type {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} */
const newDecorations = new Map();
for (let i = 0; i < diff.length; ++i) {
const diffEntry = diff[i];
for (let lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNumber)
newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry.type});
}
const decorationDiff = oldDecorations.diff(newDecorations, (e1, e2) => e1.type === e2.type);
const addedDecorations = decorationDiff.added.map(
entry => new Sources.GutterDiffPlugin.GutterDecoration(this._textEditor, entry.lineNumber, entry.type));
this._decorations = decorationDiff.equal.concat(addedDecorations);
this._updateDecorations(decorationDiff.removed, addedDecorations);
this._decorationsSetForTest(newDecorations);
}
/**
* @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDiff.EditType}>} decorations
*/
_decorationsSetForTest(decorations) {
}
/**
* @override
*/
dispose() {
for (const decoration of this._decorations)
decoration.remove();
WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode, this._update, this);
}
};
Sources.GutterDiffPlugin.GutterDecoration = class {
/**
* @param {!TextEditor.CodeMirrorTextEditor} textEditor
* @param {number} lineNumber
* @param {!SourceFrame.SourceCodeDiff.EditType} type
*/
constructor(textEditor, lineNumber, type) {
this._textEditor = textEditor;
this._position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
this._className = '';
if (type === SourceFrame.SourceCodeDiff.EditType.Insert)
this._className = 'diff-entry-insert';
else if (type === SourceFrame.SourceCodeDiff.EditType.Delete)
this._className = 'diff-entry-delete';
else if (type === SourceFrame.SourceCodeDiff.EditType.Modify)
this._className = 'diff-entry-modify';
this.type = type;
}
/**
* @return {number}
*/
lineNumber() {
const location = this._position.resolve();
if (!location)
return -1;
return location.lineNumber;
}
install() {
const location = this._position.resolve();
if (!location)
return;
const element = createElementWithClass('div', 'diff-marker');
element.textContent = '\u00A0';
this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, element);
this._textEditor.toggleLineClass(location.lineNumber, this._className, true);
}
remove() {
const location = this._position.resolve();
if (!location)
return;
this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.SourceCodeDiff.DiffGutterType, null);
this._textEditor.toggleLineClass(location.lineNumber, this._className, false);
}
};
/** @type {string} */
SourceFrame.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff';
...@@ -35,7 +35,7 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { ...@@ -35,7 +35,7 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame {
this._uiSourceCode = uiSourceCode; this._uiSourceCode = uiSourceCode;
if (Runtime.experiments.isEnabled('sourceDiff')) if (Runtime.experiments.isEnabled('sourceDiff'))
this._diff = new SourceFrame.SourceCodeDiff(WorkspaceDiff.workspaceDiff(), this.textEditor); this._diff = new SourceFrame.SourceCodeDiff(this.textEditor);
this._muteSourceCodeEvents = false; this._muteSourceCodeEvents = false;
this._isSettingContent = false; this._isSettingContent = false;
...@@ -164,7 +164,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { ...@@ -164,7 +164,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame {
this._installMessageAndDecorationListeners(); this._installMessageAndDecorationListeners();
this._updateStyle(); this._updateStyle();
this._decorateAllTypes(); this._decorateAllTypes();
this._updateDiffUISourceCode();
this._refreshHighlighterType(); this._refreshHighlighterType();
this._ensurePluginsLoaded(); this._ensurePluginsLoaded();
} }
...@@ -313,6 +312,8 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { ...@@ -313,6 +312,8 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame {
this._plugins.push(new Sources.JavaScriptCompilerPlugin(this.textEditor, pluginUISourceCode)); this._plugins.push(new Sources.JavaScriptCompilerPlugin(this.textEditor, pluginUISourceCode));
if (Sources.SnippetsPlugin.accepts(pluginUISourceCode)) if (Sources.SnippetsPlugin.accepts(pluginUISourceCode))
this._plugins.push(new Sources.SnippetsPlugin(this.textEditor, pluginUISourceCode)); this._plugins.push(new Sources.SnippetsPlugin(this.textEditor, pluginUISourceCode));
if (Runtime.experiments.isEnabled('sourceDiff') && Sources.GutterDiffPlugin.accepts(pluginUISourceCode))
this._plugins.push(new Sources.GutterDiffPlugin(this.textEditor, pluginUISourceCode));
this.dispatchEventToListeners(Sources.UISourceCodeFrame.Events.ToolbarItemsChanged); this.dispatchEventToListeners(Sources.UISourceCodeFrame.Events.ToolbarItemsChanged);
for (const plugin of this._plugins) for (const plugin of this._plugins)
...@@ -334,17 +335,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { ...@@ -334,17 +335,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame {
this._initializeUISourceCode(); this._initializeUISourceCode();
} }
_updateDiffUISourceCode() {
if (!this._diff)
return;
if (this._persistenceBinding)
this._diff.setUISourceCode(this._persistenceBinding.network);
else if (this._uiSourceCode.project().type() === Workspace.projectTypes.Network)
this._diff.setUISourceCode(this._uiSourceCode);
else
this._diff.setUISourceCode(null);
}
_updateStyle() { _updateStyle() {
this.setEditable(this._canEditSource()); this.setEditable(this._canEditSource());
} }
...@@ -378,8 +368,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame { ...@@ -378,8 +368,6 @@ Sources.UISourceCodeFrame = class extends SourceFrame.SourceFrame {
dispose() { dispose() {
this._errorPopoverHelper.dispose(); this._errorPopoverHelper.dispose();
this._unloadUISourceCode(); this._unloadUISourceCode();
if (this._diff)
this._diff.dispose();
this.textEditor.dispose(); this.textEditor.dispose();
this.detach(); this.detach();
Common.settings.moduleSetting('persistenceNetworkOverridesEnabled') Common.settings.moduleSetting('persistenceNetworkOverridesEnabled')
......
...@@ -858,6 +858,7 @@ ...@@ -858,6 +858,7 @@
"UISourceCodeFrame.js", "UISourceCodeFrame.js",
"DebuggerPlugin.js", "DebuggerPlugin.js",
"CSSPlugin.js", "CSSPlugin.js",
"GutterDiffPlugin.js",
"SearchSourcesView.js", "SearchSourcesView.js",
"NavigatorView.js", "NavigatorView.js",
"ScopeChainSidebarPane.js", "ScopeChainSidebarPane.js",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment