| 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 SourceFrame.SourceCodeDiff = class { | 7 SourceFrame.SourceCodeDiff = class { |
| 8 /** | 8 /** |
| 9 * @param {!Promise<?string>} diffBaseline | 9 * @param {!WorkspaceDiff.WorkspaceDiff} workspaceDiff |
| 10 * @param {!Workspace.UISourceCode} uiSourceCode |
| 10 * @param {!TextEditor.CodeMirrorTextEditor} textEditor | 11 * @param {!TextEditor.CodeMirrorTextEditor} textEditor |
| 11 */ | 12 */ |
| 12 constructor(diffBaseline, textEditor) { | 13 constructor(workspaceDiff, uiSourceCode, textEditor) { |
| 13 this._textEditor = textEditor; | 14 this._textEditor = textEditor; |
| 14 this._decorations = []; | 15 this._decorations = []; |
| 15 this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, tr
ue); | 16 this._textEditor.installGutter(SourceFrame.SourceCodeDiff.DiffGutterType, tr
ue); |
| 16 this._diffBaseline = diffBaseline; | 17 this._uiSourceCode = uiSourceCode; |
| 18 this._workspaceDiff = workspaceDiff; |
| 17 /** @type {!Array<!TextEditor.TextEditorPositionHandle>}*/ | 19 /** @type {!Array<!TextEditor.TextEditorPositionHandle>}*/ |
| 18 this._animatedLines = []; | 20 this._animatedLines = []; |
| 19 } | |
| 20 | 21 |
| 21 updateDiffMarkersWhenPossible() { | 22 this._workspaceDiff.subscribeToDiffChange(this._uiSourceCode, this._update,
this); |
| 22 if (this._updateTimeout) | 23 this._update(); |
| 23 clearTimeout(this._updateTimeout); | |
| 24 this._updateTimeout = | |
| 25 setTimeout(this.updateDiffMarkersImmediately.bind(this), SourceFrame.Sou
rceCodeDiff.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 } | 24 } |
| 34 | 25 |
| 35 /** | 26 /** |
| 36 * @param {?string} oldContent | 27 * @param {?string} oldContent |
| 37 * @param {?string} newContent | 28 * @param {?string} newContent |
| 38 */ | 29 */ |
| 39 highlightModifiedLines(oldContent, newContent) { | 30 highlightModifiedLines(oldContent, newContent) { |
| 40 if (typeof oldContent !== 'string' || typeof newContent !== 'string') | 31 if (typeof oldContent !== 'string' || typeof newContent !== 'string') |
| 41 return; | 32 return; |
| 42 | 33 |
| 43 var diff = this._computeDiff(oldContent, newContent); | 34 var diff = this._computeDiff(Diff.Diff.lineDiff(oldContent.split('\n'), newC
ontent.split('\n'))); |
| 44 var changedLines = []; | 35 var changedLines = []; |
| 45 for (var i = 0; i < diff.length; ++i) { | 36 for (var i = 0; i < diff.length; ++i) { |
| 46 var diffEntry = diff[i]; | 37 var diffEntry = diff[i]; |
| 47 if (diffEntry.type === SourceFrame.SourceCodeDiff.GutterDecorationType.Del
ete) | 38 if (diffEntry.type === SourceFrame.SourceCodeDiff.GutterDecorationType.Del
ete) |
| 48 continue; | 39 continue; |
| 49 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum
ber) { | 40 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum
ber) { |
| 50 var position = this._textEditor.textEditorPositionHandle(lineNumber, 0); | 41 var position = this._textEditor.textEditorPositionHandle(lineNumber, 0); |
| 51 if (position) | 42 if (position) |
| 52 changedLines.push(position); | 43 changedLines.push(position); |
| 53 } | 44 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 97 | 88 |
| 98 function operation() { | 89 function operation() { |
| 99 for (var decoration of removed) | 90 for (var decoration of removed) |
| 100 decoration.remove(); | 91 decoration.remove(); |
| 101 for (var decoration of added) | 92 for (var decoration of added) |
| 102 decoration.install(); | 93 decoration.install(); |
| 103 } | 94 } |
| 104 } | 95 } |
| 105 | 96 |
| 106 /** | 97 /** |
| 107 * @param {string} baseline | 98 * @param {!Diff.Diff.DiffArray} diff |
| 108 * @param {string} current | |
| 109 * @return {!Array<!{type: !SourceFrame.SourceCodeDiff.GutterDecorationType, f
rom: number, to: number}>} | 99 * @return {!Array<!{type: !SourceFrame.SourceCodeDiff.GutterDecorationType, f
rom: number, to: number}>} |
| 110 */ | 100 */ |
| 111 _computeDiff(baseline, current) { | 101 _computeDiff(diff) { |
| 112 var diff = Diff.Diff.lineDiff(baseline.split('\n'), current.split('\n')); | |
| 113 var result = []; | 102 var result = []; |
| 114 var hasAdded = false; | 103 var hasAdded = false; |
| 115 var hasRemoved = false; | 104 var hasRemoved = false; |
| 116 var blockStartLineNumber = 0; | 105 var blockStartLineNumber = 0; |
| 117 var currentLineNumber = 0; | 106 var currentLineNumber = 0; |
| 118 var isInsideBlock = false; | 107 var isInsideBlock = false; |
| 119 for (var i = 0; i < diff.length; ++i) { | 108 for (var i = 0; i < diff.length; ++i) { |
| 120 var token = diff[i]; | 109 var token = diff[i]; |
| 121 if (token[0] === Diff.Diff.Operation.Equal) { | 110 if (token[0] === Diff.Diff.Operation.Equal) { |
| 122 if (isInsideBlock) | 111 if (isInsideBlock) |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 type = SourceFrame.SourceCodeDiff.GutterDecorationType.Delete; | 147 type = SourceFrame.SourceCodeDiff.GutterDecorationType.Delete; |
| 159 from -= 1; | 148 from -= 1; |
| 160 } | 149 } |
| 161 result.push({type: type, from: from, to: to}); | 150 result.push({type: type, from: from, to: to}); |
| 162 isInsideBlock = false; | 151 isInsideBlock = false; |
| 163 hasAdded = false; | 152 hasAdded = false; |
| 164 hasRemoved = false; | 153 hasRemoved = false; |
| 165 } | 154 } |
| 166 } | 155 } |
| 167 | 156 |
| 157 _update() { |
| 158 this._workspaceDiff.requestDiff(this._uiSourceCode).then(this._innerUpdate.b
ind(this)); |
| 159 } |
| 160 |
| 168 /** | 161 /** |
| 169 * @param {?string} baseline | 162 * @param {?Diff.Diff.DiffArray} lineDiff |
| 170 */ | 163 */ |
| 171 _innerUpdate(baseline) { | 164 _innerUpdate(lineDiff) { |
| 172 var current = this._textEditor.text(); | 165 if (!lineDiff) |
| 173 if (typeof baseline !== 'string') { | |
| 174 this._updateDecorations(this._decorations, [] /* added */); | |
| 175 this._decorations = []; | |
| 176 return; | 166 return; |
| 177 } | |
| 178 | |
| 179 var diff = this._computeDiff(baseline, current); | |
| 180 | 167 |
| 181 /** @type {!Map<number, !SourceFrame.SourceCodeDiff.GutterDecoration>} */ | 168 /** @type {!Map<number, !SourceFrame.SourceCodeDiff.GutterDecoration>} */ |
| 182 var oldDecorations = new Map(); | 169 var oldDecorations = new Map(); |
| 183 for (var i = 0; i < this._decorations.length; ++i) { | 170 for (var i = 0; i < this._decorations.length; ++i) { |
| 184 var decoration = this._decorations[i]; | 171 var decoration = this._decorations[i]; |
| 185 var lineNumber = decoration.lineNumber(); | 172 var lineNumber = decoration.lineNumber(); |
| 186 if (lineNumber === -1) | 173 if (lineNumber === -1) |
| 187 continue; | 174 continue; |
| 188 oldDecorations.set(lineNumber, decoration); | 175 oldDecorations.set(lineNumber, decoration); |
| 189 } | 176 } |
| 190 | 177 |
| 178 var diff = this._computeDiff(lineDiff); |
| 179 |
| 191 /** @type {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeD
iff.GutterDecorationType}>} */ | 180 /** @type {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeD
iff.GutterDecorationType}>} */ |
| 192 var newDecorations = new Map(); | 181 var newDecorations = new Map(); |
| 193 for (var i = 0; i < diff.length; ++i) { | 182 for (var i = 0; i < diff.length; ++i) { |
| 194 var diffEntry = diff[i]; | 183 var diffEntry = diff[i]; |
| 195 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum
ber) | 184 for (var lineNumber = diffEntry.from; lineNumber < diffEntry.to; ++lineNum
ber) |
| 196 newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry.
type}); | 185 newDecorations.set(lineNumber, {lineNumber: lineNumber, type: diffEntry.
type}); |
| 197 } | 186 } |
| 198 | 187 |
| 199 var decorationDiff = oldDecorations.diff(newDecorations, (e1, e2) => e1.type
=== e2.type); | 188 var decorationDiff = oldDecorations.diff(newDecorations, (e1, e2) => e1.type
=== e2.type); |
| 200 var addedDecorations = decorationDiff.added.map( | 189 var addedDecorations = decorationDiff.added.map( |
| 201 entry => new SourceFrame.SourceCodeDiff.GutterDecoration(this._textEdito
r, entry.lineNumber, entry.type)); | 190 entry => new SourceFrame.SourceCodeDiff.GutterDecoration(this._textEdito
r, entry.lineNumber, entry.type)); |
| 202 | 191 |
| 203 this._decorations = decorationDiff.equal.concat(addedDecorations); | 192 this._decorations = decorationDiff.equal.concat(addedDecorations); |
| 204 this._updateDecorations(decorationDiff.removed, addedDecorations); | 193 this._updateDecorations(decorationDiff.removed, addedDecorations); |
| 205 this._decorationsSetForTest(newDecorations); | 194 this._decorationsSetForTest(newDecorations); |
| 206 } | 195 } |
| 207 | 196 |
| 208 /** | 197 /** |
| 209 * @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDif
f.GutterDecorationType}>} decorations | 198 * @param {!Map<number, !{lineNumber: number, type: !SourceFrame.SourceCodeDif
f.GutterDecorationType}>} decorations |
| 210 */ | 199 */ |
| 211 _decorationsSetForTest(decorations) { | 200 _decorationsSetForTest(decorations) { |
| 212 } | 201 } |
| 202 |
| 203 dispose() { |
| 204 WorkspaceDiff.workspaceDiff().unsubscribeFromDiffChange(this._uiSourceCode,
this._update, this); |
| 205 } |
| 213 }; | 206 }; |
| 214 | 207 |
| 215 /** @type {number} */ | |
| 216 SourceFrame.SourceCodeDiff.UpdateTimeout = 200; | |
| 217 | |
| 218 /** @type {string} */ | 208 /** @type {string} */ |
| 219 SourceFrame.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff'; | 209 SourceFrame.SourceCodeDiff.DiffGutterType = 'CodeMirror-gutter-diff'; |
| 220 | 210 |
| 221 /** @enum {symbol} */ | 211 /** @enum {symbol} */ |
| 222 SourceFrame.SourceCodeDiff.GutterDecorationType = { | 212 SourceFrame.SourceCodeDiff.GutterDecorationType = { |
| 223 Insert: Symbol('Insert'), | 213 Insert: Symbol('Insert'), |
| 224 Delete: Symbol('Delete'), | 214 Delete: Symbol('Delete'), |
| 225 Modify: Symbol('Modify'), | 215 Modify: Symbol('Modify'), |
| 226 }; | 216 }; |
| 227 | 217 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 268 } | 258 } |
| 269 | 259 |
| 270 remove() { | 260 remove() { |
| 271 var location = this._position.resolve(); | 261 var location = this._position.resolve(); |
| 272 if (!location) | 262 if (!location) |
| 273 return; | 263 return; |
| 274 this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.Source
CodeDiff.DiffGutterType, null); | 264 this._textEditor.setGutterDecoration(location.lineNumber, SourceFrame.Source
CodeDiff.DiffGutterType, null); |
| 275 this._textEditor.toggleLineClass(location.lineNumber, this._className, false
); | 265 this._textEditor.toggleLineClass(location.lineNumber, this._className, false
); |
| 276 } | 266 } |
| 277 }; | 267 }; |
| OLD | NEW |