/*jshint node:true */
|
/*
|
The MIT License (MIT)
|
|
Copyright (c) 2007-2018 Einar Lielmanis, Liam Newman, and contributors.
|
|
Permission is hereby granted, free of charge, to any person
|
obtaining a copy of this software and associated documentation files
|
(the "Software"), to deal in the Software without restriction,
|
including without limitation the rights to use, copy, modify, merge,
|
publish, distribute, sublicense, and/or sell copies of the Software,
|
and to permit persons to whom the Software is furnished to do so,
|
subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be
|
included in all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
SOFTWARE.
|
*/
|
|
'use strict';
|
|
function OutputLine(parent) {
|
this.__parent = parent;
|
this.__character_count = 0;
|
// use indent_count as a marker for this.__lines that have preserved indentation
|
this.__indent_count = -1;
|
this.__alignment_count = 0;
|
this.__wrap_point_index = 0;
|
this.__wrap_point_character_count = 0;
|
this.__wrap_point_indent_count = -1;
|
this.__wrap_point_alignment_count = 0;
|
|
this.__items = [];
|
}
|
|
OutputLine.prototype.clone_empty = function() {
|
var line = new OutputLine(this.__parent);
|
line.set_indent(this.__indent_count, this.__alignment_count);
|
return line;
|
};
|
|
OutputLine.prototype.item = function(index) {
|
if (index < 0) {
|
return this.__items[this.__items.length + index];
|
} else {
|
return this.__items[index];
|
}
|
};
|
|
OutputLine.prototype.has_match = function(pattern) {
|
for (var lastCheckedOutput = this.__items.length - 1; lastCheckedOutput >= 0; lastCheckedOutput--) {
|
if (this.__items[lastCheckedOutput].match(pattern)) {
|
return true;
|
}
|
}
|
return false;
|
};
|
|
OutputLine.prototype.set_indent = function(indent, alignment) {
|
if (this.is_empty()) {
|
this.__indent_count = indent || 0;
|
this.__alignment_count = alignment || 0;
|
this.__character_count = this.__parent.get_indent_size(this.__indent_count, this.__alignment_count);
|
}
|
};
|
|
OutputLine.prototype._set_wrap_point = function() {
|
if (this.__parent.wrap_line_length) {
|
this.__wrap_point_index = this.__items.length;
|
this.__wrap_point_character_count = this.__character_count;
|
this.__wrap_point_indent_count = this.__parent.next_line.__indent_count;
|
this.__wrap_point_alignment_count = this.__parent.next_line.__alignment_count;
|
}
|
};
|
|
OutputLine.prototype._should_wrap = function() {
|
return this.__wrap_point_index &&
|
this.__character_count > this.__parent.wrap_line_length &&
|
this.__wrap_point_character_count > this.__parent.next_line.__character_count;
|
};
|
|
OutputLine.prototype._allow_wrap = function() {
|
if (this._should_wrap()) {
|
this.__parent.add_new_line();
|
var next = this.__parent.current_line;
|
next.set_indent(this.__wrap_point_indent_count, this.__wrap_point_alignment_count);
|
next.__items = this.__items.slice(this.__wrap_point_index);
|
this.__items = this.__items.slice(0, this.__wrap_point_index);
|
|
next.__character_count += this.__character_count - this.__wrap_point_character_count;
|
this.__character_count = this.__wrap_point_character_count;
|
|
if (next.__items[0] === " ") {
|
next.__items.splice(0, 1);
|
next.__character_count -= 1;
|
}
|
return true;
|
}
|
return false;
|
};
|
|
OutputLine.prototype.is_empty = function() {
|
return this.__items.length === 0;
|
};
|
|
OutputLine.prototype.last = function() {
|
if (!this.is_empty()) {
|
return this.__items[this.__items.length - 1];
|
} else {
|
return null;
|
}
|
};
|
|
OutputLine.prototype.push = function(item) {
|
this.__items.push(item);
|
var last_newline_index = item.lastIndexOf('\n');
|
if (last_newline_index !== -1) {
|
this.__character_count = item.length - last_newline_index;
|
} else {
|
this.__character_count += item.length;
|
}
|
};
|
|
OutputLine.prototype.pop = function() {
|
var item = null;
|
if (!this.is_empty()) {
|
item = this.__items.pop();
|
this.__character_count -= item.length;
|
}
|
return item;
|
};
|
|
|
OutputLine.prototype._remove_indent = function() {
|
if (this.__indent_count > 0) {
|
this.__indent_count -= 1;
|
this.__character_count -= this.__parent.indent_size;
|
}
|
};
|
|
OutputLine.prototype._remove_wrap_indent = function() {
|
if (this.__wrap_point_indent_count > 0) {
|
this.__wrap_point_indent_count -= 1;
|
}
|
};
|
OutputLine.prototype.trim = function() {
|
while (this.last() === ' ') {
|
this.__items.pop();
|
this.__character_count -= 1;
|
}
|
};
|
|
OutputLine.prototype.toString = function() {
|
var result = '';
|
if (this.is_empty()) {
|
if (this.__parent.indent_empty_lines) {
|
result = this.__parent.get_indent_string(this.__indent_count);
|
}
|
} else {
|
result = this.__parent.get_indent_string(this.__indent_count, this.__alignment_count);
|
result += this.__items.join('');
|
}
|
return result;
|
};
|
|
function IndentStringCache(options, baseIndentString) {
|
this.__cache = [''];
|
this.__indent_size = options.indent_size;
|
this.__indent_string = options.indent_char;
|
if (!options.indent_with_tabs) {
|
this.__indent_string = new Array(options.indent_size + 1).join(options.indent_char);
|
}
|
|
// Set to null to continue support for auto detection of base indent
|
baseIndentString = baseIndentString || '';
|
if (options.indent_level > 0) {
|
baseIndentString = new Array(options.indent_level + 1).join(this.__indent_string);
|
}
|
|
this.__base_string = baseIndentString;
|
this.__base_string_length = baseIndentString.length;
|
}
|
|
IndentStringCache.prototype.get_indent_size = function(indent, column) {
|
var result = this.__base_string_length;
|
column = column || 0;
|
if (indent < 0) {
|
result = 0;
|
}
|
result += indent * this.__indent_size;
|
result += column;
|
return result;
|
};
|
|
IndentStringCache.prototype.get_indent_string = function(indent_level, column) {
|
var result = this.__base_string;
|
column = column || 0;
|
if (indent_level < 0) {
|
indent_level = 0;
|
result = '';
|
}
|
column += indent_level * this.__indent_size;
|
this.__ensure_cache(column);
|
result += this.__cache[column];
|
return result;
|
};
|
|
IndentStringCache.prototype.__ensure_cache = function(column) {
|
while (column >= this.__cache.length) {
|
this.__add_column();
|
}
|
};
|
|
IndentStringCache.prototype.__add_column = function() {
|
var column = this.__cache.length;
|
var indent = 0;
|
var result = '';
|
if (this.__indent_size && column >= this.__indent_size) {
|
indent = Math.floor(column / this.__indent_size);
|
column -= indent * this.__indent_size;
|
result = new Array(indent + 1).join(this.__indent_string);
|
}
|
if (column) {
|
result += new Array(column + 1).join(' ');
|
}
|
|
this.__cache.push(result);
|
};
|
|
function Output(options, baseIndentString) {
|
this.__indent_cache = new IndentStringCache(options, baseIndentString);
|
this.raw = false;
|
this._end_with_newline = options.end_with_newline;
|
this.indent_size = options.indent_size;
|
this.wrap_line_length = options.wrap_line_length;
|
this.indent_empty_lines = options.indent_empty_lines;
|
this.__lines = [];
|
this.previous_line = null;
|
this.current_line = null;
|
this.next_line = new OutputLine(this);
|
this.space_before_token = false;
|
this.non_breaking_space = false;
|
this.previous_token_wrapped = false;
|
// initialize
|
this.__add_outputline();
|
}
|
|
Output.prototype.__add_outputline = function() {
|
this.previous_line = this.current_line;
|
this.current_line = this.next_line.clone_empty();
|
this.__lines.push(this.current_line);
|
};
|
|
Output.prototype.get_line_number = function() {
|
return this.__lines.length;
|
};
|
|
Output.prototype.get_indent_string = function(indent, column) {
|
return this.__indent_cache.get_indent_string(indent, column);
|
};
|
|
Output.prototype.get_indent_size = function(indent, column) {
|
return this.__indent_cache.get_indent_size(indent, column);
|
};
|
|
Output.prototype.is_empty = function() {
|
return !this.previous_line && this.current_line.is_empty();
|
};
|
|
Output.prototype.add_new_line = function(force_newline) {
|
// never newline at the start of file
|
// otherwise, newline only if we didn't just add one or we're forced
|
if (this.is_empty() ||
|
(!force_newline && this.just_added_newline())) {
|
return false;
|
}
|
|
// if raw output is enabled, don't print additional newlines,
|
// but still return True as though you had
|
if (!this.raw) {
|
this.__add_outputline();
|
}
|
return true;
|
};
|
|
Output.prototype.get_code = function(eol) {
|
this.trim(true);
|
|
// handle some edge cases where the last tokens
|
// has text that ends with newline(s)
|
var last_item = this.current_line.pop();
|
if (last_item) {
|
if (last_item[last_item.length - 1] === '\n') {
|
last_item = last_item.replace(/\n+$/g, '');
|
}
|
this.current_line.push(last_item);
|
}
|
|
if (this._end_with_newline) {
|
this.__add_outputline();
|
}
|
|
var sweet_code = this.__lines.join('\n');
|
|
if (eol !== '\n') {
|
sweet_code = sweet_code.replace(/[\n]/g, eol);
|
}
|
return sweet_code;
|
};
|
|
Output.prototype.set_wrap_point = function() {
|
this.current_line._set_wrap_point();
|
};
|
|
Output.prototype.set_indent = function(indent, alignment) {
|
indent = indent || 0;
|
alignment = alignment || 0;
|
|
// Next line stores alignment values
|
this.next_line.set_indent(indent, alignment);
|
|
// Never indent your first output indent at the start of the file
|
if (this.__lines.length > 1) {
|
this.current_line.set_indent(indent, alignment);
|
return true;
|
}
|
|
this.current_line.set_indent();
|
return false;
|
};
|
|
Output.prototype.add_raw_token = function(token) {
|
for (var x = 0; x < token.newlines; x++) {
|
this.__add_outputline();
|
}
|
this.current_line.set_indent(-1);
|
this.current_line.push(token.whitespace_before);
|
this.current_line.push(token.text);
|
this.space_before_token = false;
|
this.non_breaking_space = false;
|
this.previous_token_wrapped = false;
|
};
|
|
Output.prototype.add_token = function(printable_token) {
|
this.__add_space_before_token();
|
this.current_line.push(printable_token);
|
this.space_before_token = false;
|
this.non_breaking_space = false;
|
this.previous_token_wrapped = this.current_line._allow_wrap();
|
};
|
|
Output.prototype.__add_space_before_token = function() {
|
if (this.space_before_token && !this.just_added_newline()) {
|
if (!this.non_breaking_space) {
|
this.set_wrap_point();
|
}
|
this.current_line.push(' ');
|
}
|
};
|
|
Output.prototype.remove_indent = function(index) {
|
var output_length = this.__lines.length;
|
while (index < output_length) {
|
this.__lines[index]._remove_indent();
|
index++;
|
}
|
this.current_line._remove_wrap_indent();
|
};
|
|
Output.prototype.trim = function(eat_newlines) {
|
eat_newlines = (eat_newlines === undefined) ? false : eat_newlines;
|
|
this.current_line.trim();
|
|
while (eat_newlines && this.__lines.length > 1 &&
|
this.current_line.is_empty()) {
|
this.__lines.pop();
|
this.current_line = this.__lines[this.__lines.length - 1];
|
this.current_line.trim();
|
}
|
|
this.previous_line = this.__lines.length > 1 ?
|
this.__lines[this.__lines.length - 2] : null;
|
};
|
|
Output.prototype.just_added_newline = function() {
|
return this.current_line.is_empty();
|
};
|
|
Output.prototype.just_added_blankline = function() {
|
return this.is_empty() ||
|
(this.current_line.is_empty() && this.previous_line.is_empty());
|
};
|
|
Output.prototype.ensure_empty_line_above = function(starts_with, ends_with) {
|
var index = this.__lines.length - 2;
|
while (index >= 0) {
|
var potentialEmptyLine = this.__lines[index];
|
if (potentialEmptyLine.is_empty()) {
|
break;
|
} else if (potentialEmptyLine.item(0).indexOf(starts_with) !== 0 &&
|
potentialEmptyLine.item(-1) !== ends_with) {
|
this.__lines.splice(index + 1, 0, new OutputLine(this));
|
this.previous_line = this.__lines[this.__lines.length - 2];
|
break;
|
}
|
index--;
|
}
|
};
|
|
module.exports.Output = Output;
|