Home | History | Annotate | Download | only in front_end
      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