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