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

Side by Side Diff: third_party/WebKit/Source/devtools/front_end/sources/SourceCodeDiff.js

Issue 2608043002: DevTools: extract modules (with extensions) (Closed)
Patch Set: fix externs (PerfUI) Created 3 years, 11 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 2016 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 * @unrestricted
6 */
7 Sources.SourceCodeDiff = class {
8 /**
9 * @param {!Promise<?string>} diffBaseline
10 * @param {!TextEditor.CodeMirrorTextEditor} textEditor
11 */
12 constructor(diffBaseline, textEditor) {
13 this._textEditor = textEditor;
14 this._decorations = [];
15 this._textEditor.installGutter(Sources.SourceCodeDiff.DiffGutterType, true);
16 this._diffBaseline = diffBaseline;
17 /** @type {!Array<!TextEditor.TextEditorPositionHandle>}*/
18 this._animatedLines = [];
19 }
20
21 updateDiffMarkersWhenPossible() {
22 if (this._updateTimeout)
23 clearTimeout(this._updateTimeout);
24 this._updateTimeout =
25 setTimeout(this.updateDiffMarkersImmediately.bind(this), Sources.SourceC odeDiff.UpdateTimeout);
26 }
27
28 updateDiffMarkersImmediately() {
29 if (this._updateTimeout)
30 clearTimeout(this._updateTimeout);
31 this._updateTimeout = null;
32 this._diffBaseline.then(this._innerUpdate.bind(this));
33 }
34
35 /**
36 * @param {?string} oldContent
37 * @param {?string} newContent
38 */
39 highlightModifiedLines(oldContent, newContent) {
40 if (typeof oldContent !== 'string' || typeof newContent !== 'string')
41 return;
42
43 var diff = this._computeDiff(oldContent, newContent);
44 var changedLines = [];
45 for (var i = 0; i < diff.length; ++i) {
46 var diffEntry = diff[i];
47 if (diffEntry.type === Sources.SourceCodeDiff.GutterDecorationType.Delete)
48 continue;
49 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum ber) {
50 var position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
51 if (position)
52 changedLines.push(position);
53 }
54 }
55 this._updateHighlightedLines(changedLines);
56 this._animationTimeout = setTimeout(
57 this._updateHighlightedLines.bind(this, []), 400); // // Keep this time out in sync with sourcesView.css.
58 }
59
60 /**
61 * @param {!Array<!TextEditor.TextEditorPositionHandle>} newLines
62 */
63 _updateHighlightedLines(newLines) {
64 if (this._animationTimeout)
65 clearTimeout(this._animationTimeout);
66 this._animationTimeout = null;
67 this._textEditor.operation(operation.bind(this));
68
69 /**
70 * @this {Sources.SourceCodeDiff}
71 */
72 function operation() {
73 toggleLines.call(this, false);
74 this._animatedLines = newLines;
75 toggleLines.call(this, true);
76 }
77
78 /**
79 * @param {boolean} value
80 * @this {Sources.SourceCodeDiff}
81 */
82 function toggleLines(value) {
83 for (var i = 0; i < this._animatedLines.length; ++i) {
84 var location = this._animatedLines[i].resolve();
85 if (location)
86 this._textEditor.toggleLineClass(location.lineNumber, 'highlight-line- modification', value);
87 }
88 }
89 }
90
91 /**
92 * @param {!Array<!Sources.SourceCodeDiff.GutterDecoration>} removed
93 * @param {!Array<!Sources.SourceCodeDiff.GutterDecoration>} added
94 */
95 _updateDecorations(removed, added) {
96 this._textEditor.operation(operation);
97
98 function operation() {
99 for (var decoration of removed)
100 decoration.remove();
101 for (var decoration of added)
102 decoration.install();
103 }
104 }
105
106 /**
107 * @param {string} baseline
108 * @param {string} current
109 * @return {!Array<!{type: !Sources.SourceCodeDiff.GutterDecorationType, from: number, to: number}>}
110 */
111 _computeDiff(baseline, current) {
112 var diff = Diff.Diff.lineDiff(baseline.split('\n'), current.split('\n'));
113 var result = [];
114 var hasAdded = false;
115 var hasRemoved = false;
116 var blockStartLineNumber = 0;
117 var currentLineNumber = 0;
118 var isInsideBlock = false;
119 for (var i = 0; i < diff.length; ++i) {
120 var token = diff[i];
121 if (token[0] === Diff.Diff.Operation.Equal) {
122 if (isInsideBlock)
123 flush();
124 currentLineNumber += token[1].length;
125 continue;
126 }
127
128 if (!isInsideBlock) {
129 isInsideBlock = true;
130 blockStartLineNumber = currentLineNumber;
131 }
132
133 if (token[0] === Diff.Diff.Operation.Delete) {
134 hasRemoved = true;
135 } else {
136 currentLineNumber += token[1].length;
137 hasAdded = true;
138 }
139 }
140 if (isInsideBlock)
141 flush();
142 if (result.length > 1 && result[0].from === 0 && result[1].from === 0) {
143 var merged = {type: Sources.SourceCodeDiff.GutterDecorationType.Modify, fr om: 0, to: result[1].to};
144 result.splice(0, 2, merged);
145 }
146 return result;
147
148 function flush() {
149 var type = Sources.SourceCodeDiff.GutterDecorationType.Insert;
150 var from = blockStartLineNumber;
151 var to = currentLineNumber;
152 if (hasAdded && hasRemoved) {
153 type = Sources.SourceCodeDiff.GutterDecorationType.Modify;
154 } else if (!hasAdded && hasRemoved && from === 0 && to === 0) {
155 type = Sources.SourceCodeDiff.GutterDecorationType.Modify;
156 to = 1;
157 } else if (!hasAdded && hasRemoved) {
158 type = Sources.SourceCodeDiff.GutterDecorationType.Delete;
159 from -= 1;
160 }
161 result.push({type: type, from: from, to: to});
162 isInsideBlock = false;
163 hasAdded = false;
164 hasRemoved = false;
165 }
166 }
167
168 /**
169 * @param {?string} baseline
170 */
171 _innerUpdate(baseline) {
172 var current = this._textEditor.text();
173 if (typeof baseline !== 'string') {
174 this._updateDecorations(this._decorations, [] /* added */);
175 this._decorations = [];
176 return;
177 }
178
179 var diff = this._computeDiff(baseline, current);
180
181 /** @type {!Map<number, !Sources.SourceCodeDiff.GutterDecoration>} */
182 var oldDecorations = new Map();
183 for (var i = 0; i < this._decorations.length; ++i) {
184 var decoration = this._decorations[i];
185 var lineNumber = decoration.lineNumber();
186 if (lineNumber === -1)
187 continue;
188 oldDecorations.set(lineNumber, decoration);
189 }
190
191 /** @type {!Map<number, !{lineNumber: number, type: !Sources.SourceCodeDiff. GutterDecorationType}>} */
192 var newDecorations = new Map();
193 for (var i = 0; i < diff.length; ++i) {
194 var diffEntry = diff[i];
195 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum ber)
196 newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry. type});
197 }
198
199 var decorationDiff = oldDecorations.diff(newDecorations, (e1, e2) => e1.type === e2.type);
200 var addedDecorations = decorationDiff.added.map(
201 entry => new Sources.SourceCodeDiff.GutterDecoration(this._textEditor, e ntry.lineNumber, entry.type));
202
203 this._decorations = decorationDiff.equal.concat(addedDecorations);
204 this._updateDecorations(decorationDiff.removed, addedDecorations);
205 this._decorationsSetForTest(newDecorations);
206 }
207
208 /**
209 * @param {!Map<number, !{lineNumber: number, type: !Sources.SourceCodeDiff.Gu tterDecorationType}>} decorations
210 */
211 _decorationsSetForTest(decorations) {
212 }
213 };
214
215 /** @type {number} */
216 Sources.SourceCodeDiff.UpdateTimeout = 200;
217
218 /** @type {string} */
219 Sources.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff';
220
221 /** @enum {symbol} */
222 Sources.SourceCodeDiff.GutterDecorationType = {
223 Insert: Symbol('Insert'),
224 Delete: Symbol('Delete'),
225 Modify: Symbol('Modify'),
226 };
227
228 /**
229 * @unrestricted
230 */
231 Sources.SourceCodeDiff.GutterDecoration = class {
232 /**
233 * @param {!TextEditor.CodeMirrorTextEditor} textEditor
234 * @param {number} lineNumber
235 * @param {!Sources.SourceCodeDiff.GutterDecorationType} type
236 */
237 constructor(textEditor, lineNumber, type) {
238 this._textEditor = textEditor;
239 this._position = this._textEditor.textEditorPositionHandle(lineNumber, 0);
240 this._className = '';
241 if (type === Sources.SourceCodeDiff.GutterDecorationType.Insert)
242 this._className = 'diff-entry-insert';
243 else if (type === Sources.SourceCodeDiff.GutterDecorationType.Delete)
244 this._className = 'diff-entry-delete';
245 else if (type === Sources.SourceCodeDiff.GutterDecorationType.Modify)
246 this._className = 'diff-entry-modify';
247 this.type = type;
248 }
249
250 /**
251 * @return {number}
252 */
253 lineNumber() {
254 var location = this._position.resolve();
255 if (!location)
256 return -1;
257 return location.lineNumber;
258 }
259
260 install() {
261 var location = this._position.resolve();
262 if (!location)
263 return;
264 var element = createElementWithClass('div', 'diff-marker');
265 element.textContent = '\u00A0';
266 this._textEditor.setGutterDecoration(location.lineNumber, Sources.SourceCode Diff.DiffGutterType, element);
267 this._textEditor.toggleLineClass(location.lineNumber, this._className, true) ;
268 }
269
270 remove() {
271 var location = this._position.resolve();
272 if (!location)
273 return;
274 this._textEditor.setGutterDecoration(location.lineNumber, Sources.SourceCode Diff.DiffGutterType, null);
275 this._textEditor.toggleLineClass(location.lineNumber, this._className, false );
276 }
277 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698