blob: 3c36a2bb3f3844e837c6b0a86ff8a8c8e5e300b4 [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
function FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, originalOffset, formattedOffset, indentString)
{
this._originalContent = null;
this._formattedContent = [];
this._formattedContentLength = 0;
this._startOfLine = true;
this.lastTokenWasNewline = false;
this.lastTokenWasWhitespace = false;
this.lastNewlineAppendWasMultiple = false;
this._indent = 0;
this._indentString = indentString;
this._indentCache = ["", this._indentString];
this._mapping = mapping;
this._originalLineEndings = originalLineEndings || [];
this._formattedLineEndings = formattedLineEndings || [];
this._originalOffset = originalOffset || 0;
this._formattedOffset = formattedOffset || 0;
this._lastOriginalPosition = 0;
this._lastFormattedPosition = 0;
}
FormatterContentBuilder.prototype = {
constructor: FormatterContentBuilder,
// Public
get originalContent()
{
return this._originalContent;
},
get formattedContent()
{
var formatted = this._formattedContent.join("");
console.assert(formatted.length === this._formattedContentLength);
return formatted;
},
get mapping()
{
return this._mapping;
},
get originalLineEndings()
{
return this._originalLineEndings;
},
get formattedLineEndings()
{
return this._formattedLineEndings;
},
setOriginalContent: function(originalContent)
{
console.assert(!this._originalContent);
this._originalContent = originalContent;
},
appendToken: function(string, originalPosition)
{
if (this._startOfLine)
this._appendIndent();
this._addMappingIfNeeded(originalPosition);
this._append(string);
this._startOfLine = false;
this.lastTokenWasNewline = false;
this.lastTokenWasWhitespace = false;
},
appendSpace: function()
{
if (!this._startOfLine) {
this._append(" ");
this.lastTokenWasNewline = false;
this.lastTokenWasWhitespace = true;
}
},
appendNewline: function(force)
{
if ((!this.lastTokenWasNewline && !this._startOfLine) || force) {
this._append("\n");
this._addFormattedLineEnding();
this._startOfLine = true;
this.lastTokenWasNewline = true;
this.lastTokenWasWhitespace = false;
this.lastNewlineAppendWasMultiple = false;
}
},
appendMultipleNewlines: function(newlines)
{
console.assert(newlines > 0);
var wasMultiple = newlines > 1;
while (newlines-- > 0)
this.appendNewline(true);
if (wasMultiple)
this.lastNewlineAppendWasMultiple = true;
},
removeLastNewline: function()
{
console.assert(this.lastTokenWasNewline);
console.assert(this._formattedContent.lastValue === "\n");
if (this.lastTokenWasNewline) {
this._popNewLine();
this._startOfLine = false;
this.lastTokenWasNewline = false;
this.lastTokenWasWhitespace = false;
}
},
indent: function()
{
++this._indent;
},
dedent: function()
{
--this._indent;
console.assert(this._indent >= 0);
if (this._indent < 0)
this._indent = 0;
},
addOriginalLineEnding: function(originalPosition)
{
this._originalLineEndings.push(originalPosition);
},
finish: function()
{
this.appendNewline();
},
// Private
_popNewLine: function()
{
var removed = this._formattedContent.pop();
this._formattedContentLength -= removed.length;
this._formattedLineEndings.pop();
},
_append: function(str)
{
this._formattedContent.push(str);
this._formattedContentLength += str.length;
},
_appendIndent: function()
{
// Indent is already in the cache.
if (this._indent < this._indentCache.length) {
this._append(this._indentCache[this._indent]);
return;
}
// Indent was not in the cache, fill up the cache up with what was needed.
const maxCacheIndent = 20;
var max = Math.min(this._indent, maxCacheIndent);
for (var i = this._indentCache.length; i <= max; ++i)
this._indentCache[i] = this._indentCache[i - 1] + this._indentString;
// Append indents as needed.
var indent = this._indent;
do {
if (indent >= maxCacheIndent)
this._append(this._indentCache[maxCacheIndent]);
else
this._append(this._indentCache[indent]);
indent -= maxCacheIndent;
} while (indent > 0);
},
_addMappingIfNeeded: function(originalPosition)
{
if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition)
return;
this._mapping.original.push(this._originalOffset + originalPosition);
this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength);
this._lastOriginalPosition = originalPosition;
this._lastFormattedPosition = this._formattedContentLength;
},
_addFormattedLineEnding: function()
{
console.assert(this._formattedContent.lastValue === "\n");
this._formattedLineEndings.push(this._formattedContentLength - 1);
}
};