Update CodeMirror to 3.14.1.
[WebKit-https.git] / Source / WebInspectorUI / Tools / PrettyPrinting / FormatterContentBuilder.js
1 /*
2  * Copyright (C) 2013 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 function FormatterContentBuilder(mapping, originalLineEndings, formattedLineEndings, originalOffset, formattedOffset, indentString)
27 {
28     this._originalContent = null;
29     this._formattedContent = [];
30     this._formattedContentLength = 0;
31
32     this._startOfLine = true;
33     this.lastTokenWasNewline = false;
34     this.lastTokenWasWhitespace = false;
35     this.lastNewlineAppendWasMultiple = false;
36
37     this._indent = 0;
38     this._indentString = indentString;
39     this._indentCache = ["", this._indentString];
40
41     this._mapping = mapping;
42     this._originalLineEndings = originalLineEndings || [];
43     this._formattedLineEndings = formattedLineEndings || [];
44     this._originalOffset = originalOffset || 0;
45     this._formattedOffset = formattedOffset || 0;
46
47     this._lastOriginalPosition = 0;
48     this._lastFormattedPosition = 0;
49 }
50
51 FormatterContentBuilder.prototype = {
52     constructor: FormatterContentBuilder,
53
54     // Public
55
56     get originalContent()
57     {
58         return this._originalContent;
59     },
60
61     get formattedContent()
62     {
63         var formatted = this._formattedContent.join("");
64         console.assert(formatted.length === this._formattedContentLength);
65         return formatted;
66     },
67
68     get mapping()
69     {
70         return this._mapping;
71     },
72
73     get originalLineEndings()
74     {
75         return this._originalLineEndings;
76     },
77
78     get formattedLineEndings()
79     {
80         return this._formattedLineEndings;
81     },
82
83     setOriginalContent: function(originalContent)
84     {
85         console.assert(!this._originalContent);
86         this._originalContent = originalContent;
87     },
88
89     appendToken: function(string, originalPosition)
90     {
91         if (this._startOfLine)
92             this._appendIndent();
93
94         this._addMappingIfNeeded(originalPosition);
95
96         this._append(string);
97         this._startOfLine = false;
98         this.lastTokenWasNewline = false;
99         this.lastTokenWasWhitespace = false;
100     },
101
102     appendSpace: function()
103     {
104         if (!this._startOfLine) {
105             this._append(" ");
106             this.lastTokenWasNewline = false;
107             this.lastTokenWasWhitespace = true;
108         }
109     },
110
111     appendNewline: function(force)
112     {
113         if ((!this.lastTokenWasNewline && !this._startOfLine) || force) {
114             this._append("\n");
115             this._addFormattedLineEnding();
116             this._startOfLine = true;
117             this.lastTokenWasNewline = true;
118             this.lastTokenWasWhitespace = false;
119             this.lastNewlineAppendWasMultiple = false;
120         }
121     },
122
123     appendMultipleNewlines: function(newlines)
124     {
125         console.assert(newlines > 0);
126
127         var wasMultiple = newlines > 1;
128
129         while (newlines-- > 0)
130             this.appendNewline(true);
131
132         if (wasMultiple)
133             this.lastNewlineAppendWasMultiple = true;
134     },
135
136     removeLastNewline: function()
137     {
138         console.assert(this.lastTokenWasNewline);
139         console.assert(this._formattedContent.lastValue === "\n");
140         if (this.lastTokenWasNewline) {
141             this._popNewLine();
142             this._startOfLine = false;
143             this.lastTokenWasNewline = false;
144             this.lastTokenWasWhitespace = false;
145         }
146     },
147
148     indent: function()
149     {
150         ++this._indent;
151     },
152
153     dedent: function()
154     {
155         --this._indent;
156
157         console.assert(this._indent >= 0);
158         if (this._indent < 0)
159             this._indent = 0;
160     },
161
162     addOriginalLineEnding: function(originalPosition)
163     {
164         this._originalLineEndings.push(originalPosition);
165     },
166
167     finish: function()
168     {
169         this.appendNewline();
170     },
171
172     // Private
173
174     _popNewLine: function()
175     {
176         var removed = this._formattedContent.pop();
177         this._formattedContentLength -= removed.length;
178         this._formattedLineEndings.pop();
179     },
180
181     _append: function(str)
182     {
183         this._formattedContent.push(str);
184         this._formattedContentLength += str.length;
185     },
186
187     _appendIndent: function()
188     {
189         // Indent is already in the cache.
190         if (this._indent < this._indentCache.length) {
191             this._append(this._indentCache[this._indent]);
192             return;
193         }
194
195         // Indent was not in the cache, fill up the cache up with what was needed.
196         const maxCacheIndent = 20;
197         var max = Math.min(this._indent, maxCacheIndent);
198         for (var i = this._indentCache.length; i <= max; ++i)
199             this._indentCache[i] = this._indentCache[i-1] + this._indentString;
200
201         // Append indents as needed.
202         var indent = this._indent;
203         do {
204             if (indent >= maxCacheIndent)
205                 this._append(this._indentCache[maxCacheIndent])
206             else
207                 this._append(this._indentCache[indent]);
208             indent -= maxCacheIndent;
209         } while (indent > 0);
210     },
211
212     _addMappingIfNeeded: function(originalPosition)
213     {
214         if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition)
215             return;
216
217         this._mapping.original.push(this._originalOffset + originalPosition);
218         this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength);
219
220         this._lastOriginalPosition = originalPosition;
221         this._lastFormattedPosition = this._formattedContentLength;
222     },
223
224     _addFormattedLineEnding: function()
225     {
226         console.assert(this._formattedContent.lastValue === "\n");
227         this._formattedLineEndings.push(this._formattedContentLength - 1);
228     }
229 }