/* * 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); } }