1 /* 2 * Copyright (C) 2011 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /** 32 * @interface 33 */ 34 WebInspector.Formatter = function() 35 { 36 } 37 38 /** 39 * @param {!WebInspector.ResourceType} contentType 40 * @return {!WebInspector.Formatter} 41 */ 42 WebInspector.Formatter.createFormatter = function(contentType) 43 { 44 if (contentType === WebInspector.resourceTypes.Script || contentType === WebInspector.resourceTypes.Document || contentType === WebInspector.resourceTypes.Stylesheet) 45 return new WebInspector.ScriptFormatter(); 46 return new WebInspector.IdentityFormatter(); 47 } 48 49 /** 50 * @param {!Array.<number>} lineEndings 51 * @param {number} lineNumber 52 * @param {number} columnNumber 53 * @return {number} 54 */ 55 WebInspector.Formatter.locationToPosition = function(lineEndings, lineNumber, columnNumber) 56 { 57 var position = lineNumber ? lineEndings[lineNumber - 1] + 1 : 0; 58 return position + columnNumber; 59 } 60 61 /** 62 * @param {!Array.<number>} lineEndings 63 * @param {number} position 64 * @return {!Array.<number>} 65 */ 66 WebInspector.Formatter.positionToLocation = function(lineEndings, position) 67 { 68 var lineNumber = lineEndings.upperBound(position - 1); 69 if (!lineNumber) 70 var columnNumber = position; 71 else 72 var columnNumber = position - lineEndings[lineNumber - 1] - 1; 73 return [lineNumber, columnNumber]; 74 } 75 76 WebInspector.Formatter.prototype = { 77 /** 78 * @param {string} mimeType 79 * @param {string} content 80 * @param {function(string, !WebInspector.FormatterSourceMapping)} callback 81 */ 82 formatContent: function(mimeType, content, callback) 83 { 84 } 85 } 86 87 /** 88 * @constructor 89 * @implements {WebInspector.Formatter} 90 */ 91 WebInspector.ScriptFormatter = function() 92 { 93 this._tasks = []; 94 } 95 96 WebInspector.ScriptFormatter.prototype = { 97 /** 98 * @param {string} mimeType 99 * @param {string} content 100 * @param {function(string, !WebInspector.FormatterSourceMapping)} callback 101 */ 102 formatContent: function(mimeType, content, callback) 103 { 104 content = content.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''); 105 const method = "format"; 106 var parameters = { mimeType: mimeType, content: content, indentString: WebInspector.settings.textEditorIndent.get() }; 107 this._tasks.push({ data: parameters, callback: callback }); 108 this._worker.postMessage({ method: method, params: parameters }); 109 }, 110 111 _didFormatContent: function(event) 112 { 113 var task = this._tasks.shift(); 114 var originalContent = task.data.content; 115 var formattedContent = event.data.content; 116 var mapping = event.data["mapping"]; 117 var sourceMapping = new WebInspector.FormatterSourceMappingImpl(originalContent.lineEndings(), formattedContent.lineEndings(), mapping); 118 task.callback(formattedContent, sourceMapping); 119 }, 120 121 /** 122 * @return {!Worker} 123 */ 124 get _worker() 125 { 126 if (!this._cachedWorker) { 127 this._cachedWorker = new Worker("ScriptFormatterWorker.js"); 128 this._cachedWorker.onmessage = /** @type {function(this:Worker)} */ (this._didFormatContent.bind(this)); 129 } 130 return this._cachedWorker; 131 } 132 } 133 134 /** 135 * @constructor 136 * @implements {WebInspector.Formatter} 137 */ 138 WebInspector.IdentityFormatter = function() 139 { 140 this._tasks = []; 141 } 142 143 WebInspector.IdentityFormatter.prototype = { 144 /** 145 * @param {string} mimeType 146 * @param {string} content 147 * @param {function(string, !WebInspector.FormatterSourceMapping)} callback 148 */ 149 formatContent: function(mimeType, content, callback) 150 { 151 callback(content, new WebInspector.IdentityFormatterSourceMapping()); 152 } 153 } 154 155 /** 156 * @constructor 157 */ 158 WebInspector.FormatterMappingPayload = function() 159 { 160 this.original = []; 161 this.formatted = []; 162 } 163 164 /** 165 * @interface 166 */ 167 WebInspector.FormatterSourceMapping = function() 168 { 169 } 170 171 WebInspector.FormatterSourceMapping.prototype = { 172 /** 173 * @param {number} lineNumber 174 * @param {number=} columnNumber 175 * @return {!Array.<number>} 176 */ 177 originalToFormatted: function(lineNumber, columnNumber) { }, 178 179 /** 180 * @param {number} lineNumber 181 * @param {number=} columnNumber 182 * @return {!Array.<number>} 183 */ 184 formattedToOriginal: function(lineNumber, columnNumber) { } 185 } 186 187 /** 188 * @constructor 189 * @implements {WebInspector.FormatterSourceMapping} 190 */ 191 WebInspector.IdentityFormatterSourceMapping = function() 192 { 193 } 194 195 WebInspector.IdentityFormatterSourceMapping.prototype = { 196 /** 197 * @param {number} lineNumber 198 * @param {number=} columnNumber 199 * @return {!Array.<number>} 200 */ 201 originalToFormatted: function(lineNumber, columnNumber) 202 { 203 return [lineNumber, columnNumber || 0]; 204 }, 205 206 /** 207 * @param {number} lineNumber 208 * @param {number=} columnNumber 209 * @return {!Array.<number>} 210 */ 211 formattedToOriginal: function(lineNumber, columnNumber) 212 { 213 return [lineNumber, columnNumber || 0]; 214 } 215 } 216 217 /** 218 * @constructor 219 * @implements {WebInspector.FormatterSourceMapping} 220 * @param {!Array.<number>} originalLineEndings 221 * @param {!Array.<number>} formattedLineEndings 222 * @param {!WebInspector.FormatterMappingPayload} mapping 223 */ 224 WebInspector.FormatterSourceMappingImpl = function(originalLineEndings, formattedLineEndings, mapping) 225 { 226 this._originalLineEndings = originalLineEndings; 227 this._formattedLineEndings = formattedLineEndings; 228 this._mapping = mapping; 229 } 230 231 WebInspector.FormatterSourceMappingImpl.prototype = { 232 /** 233 * @param {number} lineNumber 234 * @param {number=} columnNumber 235 * @return {!Array.<number>} 236 */ 237 originalToFormatted: function(lineNumber, columnNumber) 238 { 239 var originalPosition = WebInspector.Formatter.locationToPosition(this._originalLineEndings, lineNumber, columnNumber || 0); 240 var formattedPosition = this._convertPosition(this._mapping.original, this._mapping.formatted, originalPosition || 0); 241 return WebInspector.Formatter.positionToLocation(this._formattedLineEndings, formattedPosition); 242 }, 243 244 /** 245 * @param {number} lineNumber 246 * @param {number=} columnNumber 247 * @return {!Array.<number>} 248 */ 249 formattedToOriginal: function(lineNumber, columnNumber) 250 { 251 var formattedPosition = WebInspector.Formatter.locationToPosition(this._formattedLineEndings, lineNumber, columnNumber || 0); 252 var originalPosition = this._convertPosition(this._mapping.formatted, this._mapping.original, formattedPosition); 253 return WebInspector.Formatter.positionToLocation(this._originalLineEndings, originalPosition || 0); 254 }, 255 256 /** 257 * @param {!Array.<number>} positions1 258 * @param {!Array.<number>} positions2 259 * @param {number} position 260 * @return {number} 261 */ 262 _convertPosition: function(positions1, positions2, position) 263 { 264 var index = positions1.upperBound(position) - 1; 265 var convertedPosition = positions2[index] + position - positions1[index]; 266 if (index < positions2.length - 1 && convertedPosition > positions2[index + 1]) 267 convertedPosition = positions2[index + 1]; 268 return convertedPosition; 269 } 270 } 271