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

Side by Side Diff: Source/devtools/front_end/bindings/SourceMapEditor.js

Issue 1307063005: DevTools: edit SASS through SourceMaps. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: improvements Created 5 years, 3 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 /**
2 * @constructor
3 * @param {!Map.<string, string>} sources
4 * @param {!WebInspector.SourceMapEditorFlavor} flavor
5 */
6 WebInspector.SourceMapEditor = function(sources, flavor)
7 {
8 this._sources = sources;
9 this._editFlavor = flavor;
10 }
11 /**
12 * @param {string} sourceURL
13 * @param {string} contentV1
14 * @param {string} contentV2
15 * @return {!Array.<!WebInspector.SourceMapEdit>}
16 */
17 WebInspector.SourceMapEditor.computeEdits = function(sourceURL, contentV1, conte ntV2)
18 {
19 var differ = new diff_match_patch();
20 var diff = differ.diff_main(contentV1, contentV2);
21 differ.diff_cleanupEfficiency(diff);
22 var oldSourceOffset = 0;
23 var edits = [];
24 var i = 0;
25 while (i < diff.length) {
26 var token = diff[i];
27 if (token[0] === 0) {
28 oldSourceOffset += token[1].length;
29 ++i;
30 continue;
31 }
32
33 var nextToken = diff[i + 1];
34 if (nextToken && nextToken[0] === -token[0]) {
35 // If this is an EDIT command.
36 var add, remove;
37 if (token[0] === 1) {
38 add = token;
39 remove = nextToken;
40 } else {
41 add = nextToken;
42 remove = token;
43 }
44 var sourceRange = new WebInspector.SourceRange(oldSourceOffset, remo ve[1].length);
45 var oldRange = WebInspector.TextRange.createFromSourceRange(contentV 1, sourceRange);
46 edits.push(new WebInspector.SourceMapEdit(sourceURL, oldRange, remov e[1], add[1]));
47 oldSourceOffset += remove[1].length;
48 i += 2;
49 } else if (token[0] === 1) {
50 // If this is an ADD command.
51 var sourceRange = new WebInspector.SourceRange(oldSourceOffset, 0);
52 var oldRange = WebInspector.TextRange.createFromSourceRange(contentV 1, sourceRange);
53 edits.push(new WebInspector.SourceMapEdit(sourceURL, oldRange, "", t oken[1]));
54 i += 1;
55 } else {
56 // If this is a REMOVE command.
57 var sourceRange = new WebInspector.SourceRange(oldSourceOffset, toke n[1].length);
58 var oldRange = WebInspector.TextRange.createFromSourceRange(contentV 1, sourceRange);
59 edits.push(new WebInspector.SourceMapEdit(sourceURL, oldRange, token [1], ""));
60 oldSourceOffset += token[1].length;
61 i += 1;
62 }
63 }
64 return edits;
65 }
66
67 WebInspector.SourceMapEditor.prototype = {
68 /**
69 * @param {!WebInspector.SourceMap} map
70 * @param {!Array<!WebInspector.SourceMapEdit>} edits
71 * @return {!Set<string, string>}
72 */
73 handleCompiledEdits: function(map, edits)
74 {
75 var backEditInfos = [];
76 for (var i = 0; i < edits.length; ++i)
77 backEditInfos.push(this._doCompiledEdit(map, edits[i]));
78
79 var didBackEdits = false;
80 for (var i = 0; i < edits.length; ++i)
81 didBackEdits = this._doBackEdits(map, edits[i], backEditInfos[i]) || didBackEdits;
82
83 var result = new Set();
84 if (didBackEdits)
85 result.add(edits[0].sourceURL);
86 for (var i = 0; i < backEditInfos.length; ++i) {
87 var sourceURL = backEditInfos[i].compiledBaseMapping.sourceURL;
88 result.add(sourceURL);
89 }
90 return result;
91 },
92
93 /**
94 * @param {!WebInspector.SourceMap} map
95 * @param {!WebInspector.SourceMapEdit} edit
96 * @return {!WebInspector.SourceMapEditor.BackEditInfo}
97 */
98 _doCompiledEdit: function(map, edit)
99 {
100 var startMapping = map.findEntry(edit.oldRange.startLine, edit.oldRange. startColumn);
101 var endMapping = map.findEntry(edit.oldRange.endLine, edit.oldRange.endC olumn);
102 if (startMapping.sourceURL !== endMapping.sourceURL)
103 throw new Error("An edit spans multiple source files.");
104
105 var sourceURL = startMapping.sourceURL;
106 var compiledText = this._sources.get(edit.sourceURL);
107 var sourceText = this._sources.get(sourceURL);
108
109 // Return if we don't have source for mapped entries.
110 if (!compiledText || !sourceText)
111 throw new Error("Source of " + sourceURL + " is missing");
112
113 var startLocation = this._mapCompiledLocation(startMapping, edit.oldRang e.startLine, edit.oldRange.startColumn);
114 var endLocation = this._mapCompiledLocation(endMapping, edit.oldRange.en dLine, edit.oldRange.endColumn);
115 var sourceOldRange = new WebInspector.TextRange(startLocation.line, star tLocation.column, endLocation.line, endLocation.column);
116 var sourceOldText = sourceOldRange.extract(sourceText);
117 // If the text in compiled and original differs, then we cannot edit it.
118 var sourceNewText = this._editFlavor.sourceEditText(edit.oldText, source OldText, edit.newText)
119 if (sourceNewText === null)
120 throw new Error("The edited range is not equal in origin and source" );
121
122 var newTextLineCount = sourceNewText.lineEndings().length;
123 if (newTextLineCount > 1)
124 throw new Error("Not implemented for multi-line edit");
125
126 if (!map.compiledRangeEdited(edit.oldRange, edit.newRange()))
127 throw new Error("Failed to update compiled locations in sourcemap.") ;
128
129 var sourceEdit = new WebInspector.SourceMapEdit(sourceURL, sourceOldRang e, sourceOldText, sourceNewText);
130 if (!map.sourceRangeEdited(sourceURL, sourceEdit.oldRange, sourceEdit.ne wRange()))
131 throw new Error("Failed to update source locations in sourcemap.");
132
133 this._sources.set(sourceURL, sourceEdit.applyToText(sourceText));
134
135 var offsetRange = edit.oldRange.relativeTo(startMapping.lineNumber, star tMapping.columnNumber);
136 var sourceNewRange = sourceEdit.newRange();
137 var reversedMappings = map.findEntriesReversed(sourceEdit.sourceURL, sou rceNewRange.startLine, sourceNewRange.startColumn + 1);
138 return new WebInspector.SourceMapEditor.BackEditInfo(startMapping, offse tRange, reversedMappings);
139 },
140
141 _doBackEdits: function(map, compiledEdit, backEditInfo)
142 {
143 // There is no multimapping from source position to compiled position.
144 if (backEditInfo.reversedMappings.length < 2)
145 return false;
146 var didEdit = false;
147 var compiledText = this._sources.get(compiledEdit.sourceURL);
148 for (var i = 0; i < backEditInfo.reversedMappings.length; ++i) {
149 var mapping = backEditInfo.reversedMappings[i];
150 if (mapping === backEditInfo.compiledBaseMapping)
151 continue;
152 var oldEditRange = rebaseOffsetRange(backEditInfo.offsetRange, mappi ng);
153 var reverseEdit = new WebInspector.SourceMapEdit(compiledEdit.source URL, oldEditRange, oldEditRange.extract(compiledText), compiledEdit.newText);
154 if (reverseEdit.oldText !== compiledEdit.oldText) {
155 console.error("compiled texts differ.");
156 continue;
157 }
158 if (!map.compiledRangeEdited(reverseEdit.oldRange, reverseEdit.newRa nge()))
159 throw new Error("Failed to update reversed compiled locations in sourcemap.");
160 compiledText = reverseEdit.applyToText(compiledText);
161 didEdit = true;
162 }
163 if (didEdit)
164 this._sources.set(compiledEdit.sourceURL, compiledText);
165
166 return didEdit;
167
168 function rebaseOffsetRange(range, mapping)
169 {
170 return new WebInspector.TextRange(
171 range.startLine + mapping.lineNumber,
172 range.startColumn + mapping.columnNumber,
173 range.endLine + mapping.lineNumber,
174 range.endColumn + mapping.columnNumber
175 );
176 }
177 },
178
179 /**
180 * @param {!WebInspector.SourceMap.Entry} entry
181 * @param {number} lineNumber
182 * @param {number} columnNumber
183 * @return {!{line: number, column: number}}
184 */
185 _mapCompiledLocation: function(entry, lineNumber, columnNumber)
186 {
187 var lineOffset = lineNumber - entry.lineNumber;
188 var columnOffset = columnNumber - entry.columnNumber;
189 return {line: entry.sourceLineNumber + lineOffset, column: entry.sourceC olumnNumber + columnOffset};
190 }
191 }
192
193 /**
194 * @constructor
195 * @param {string} sourceURL
196 * @param {!WebInspector.TextRange} oldRange
197 * @param {string} oldText
198 * @param {string} newText
199 */
200 WebInspector.SourceMapEdit = function(sourceURL, oldRange, oldText, newText)
201 {
202 this.sourceURL = sourceURL;
203 this.oldRange = oldRange;
204 this.oldText = oldText;
205 this.newText = newText;
206 }
207
208 WebInspector.SourceMapEdit.prototype = {
209 /**
210 * @return {!WebInspector.TextRange}
211 */
212 newRange: function()
213 {
214 var endLine = this.oldRange.startLine;
215 var endColumn = this.oldRange.startColumn + this.newText.length;
216 var lineEndings = this.newText.lineEndings();
217 if (lineEndings.length > 1) {
218 endLine = this.oldRange.startLine + lineEndings.length - 1;
219 var len = lineEndings.length;
220 endColumn = lineEndings[len - 1] - lineEndings[len - 2] - 1;
221 }
222 return new WebInspector.TextRange(
223 this.oldRange.startLine,
224 this.oldRange.startColumn,
225 endLine,
226 endColumn);
227 },
228
229 applyToText: function(text)
230 {
231 return this.oldRange.replaceInText(text, this.newText);
232 },
233 }
234
235 WebInspector.SourceMapEditor.BackEditInfo = function(compiledBaseMapping, offset Range, reversedMappings)
236 {
237 this.compiledBaseMapping = compiledBaseMapping;
238 this.offsetRange = offsetRange;
239 this.reversedMappings = reversedMappings;
240 }
241
242 /**
243 * @interface
244 */
245 WebInspector.SourceMapEditorFlavor = function() { }
246
247 WebInspector.SourceMapEditorFlavor.prototype = {
248 /**
249 * @param {string} compiledURL
250 * @param {string} compiledText
251 * @param {!WebInspector.TextRange} compiledRange
252 * @param {string} sourceURL
253 * @param {string} sourceText
254 * @param {!WebInspector.TextRange} sourceRange
255 * @return {?string}
256 */
257 sourceEditText: function(compiledURL, compiledText, compiledRange, sourceTex t, sourceURL, sourceRange)
258 {
259 },
260 }
OLDNEW
« no previous file with comments | « Source/devtools/front_end/bindings/SASSStructureMapping.js ('k') | Source/devtools/front_end/bindings/diff_match_patch.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698