blob: f3de68c8bfe5f053cce2f7c84cfd57bf25ae8391 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @unrestricted
*/
export class FormattedContentBuilder {
/**
* @param {string} indentString
*/
constructor(indentString) {
this._lastOriginalPosition = 0;
this._formattedContent = [];
this._formattedContentLength = 0;
this._lastFormattedPosition = 0;
/** @type {!{original: !Array.<number>, formatted: !Array.<number>}} */
this._mapping = {original: [0], formatted: [0]};
this._nestingLevel = 0;
this._indentString = indentString;
/** @type {!Map<number, string>} */
this._cachedIndents = new Map();
this._newLines = 0;
this._softSpace = false;
this._hardSpaces = 0;
this._enforceSpaceBetweenWords = true;
}
/**
* @param {boolean} value
* @return {boolean}
*/
setEnforceSpaceBetweenWords(value) {
const oldValue = this._enforceSpaceBetweenWords;
this._enforceSpaceBetweenWords = value;
return oldValue;
}
/**
* @param {string} token
* @param {number} offset
*/
addToken(token, offset) {
const last = this._formattedContent.peekLast();
if (this._enforceSpaceBetweenWords && last && /\w/.test(last[last.length - 1]) && /\w/.test(token)) {
this.addSoftSpace();
}
this._appendFormatting();
// Insert token.
this._addMappingIfNeeded(offset);
this._addText(token);
}
addSoftSpace() {
if (!this._hardSpaces) {
this._softSpace = true;
}
}
addHardSpace() {
this._softSpace = false;
++this._hardSpaces;
}
/**
* @param {boolean=} noSquash
*/
addNewLine(noSquash) {
// Avoid leading newlines.
if (!this._formattedContentLength) {
return;
}
if (noSquash) {
++this._newLines;
} else {
this._newLines = this._newLines || 1;
}
}
increaseNestingLevel() {
this._nestingLevel += 1;
}
decreaseNestingLevel() {
if (this._nestingLevel > 0) {
this._nestingLevel -= 1;
}
}
_appendFormatting() {
if (this._newLines) {
for (let i = 0; i < this._newLines; ++i) {
this._addText('\n');
}
this._addText(this._indent());
} else if (this._softSpace) {
this._addText(' ');
}
if (this._hardSpaces) {
for (let i = 0; i < this._hardSpaces; ++i) {
this._addText(' ');
}
}
this._newLines = 0;
this._softSpace = false;
this._hardSpaces = 0;
}
/**
* @return {string}
*/
content() {
return this._formattedContent.join('') + (this._newLines ? '\n' : '');
}
/**
* @return {!{original: !Array.<number>, formatted: !Array.<number>}}
*/
mapping() {
return this._mapping;
}
/**
* @return {string}
*/
_indent() {
const cachedValue = this._cachedIndents.get(this._nestingLevel);
if (cachedValue) {
return cachedValue;
}
let fullIndent = '';
for (let i = 0; i < this._nestingLevel; ++i) {
fullIndent += this._indentString;
}
// Cache a maximum of 20 nesting level indents.
if (this._nestingLevel <= 20) {
this._cachedIndents.set(this._nestingLevel, fullIndent);
}
return fullIndent;
}
/**
* @param {string} text
*/
_addText(text) {
this._formattedContent.push(text);
this._formattedContentLength += text.length;
}
/**
* @param {number} originalPosition
*/
_addMappingIfNeeded(originalPosition) {
if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition) {
return;
}
this._mapping.original.push(originalPosition);
this._lastOriginalPosition = originalPosition;
this._mapping.formatted.push(this._formattedContentLength);
this._lastFormattedPosition = this._formattedContentLength;
}
}
/* Legacy exported object */
self.FormatterWorker = self.FormatterWorker || {};
/* Legacy exported object */
FormatterWorker = FormatterWorker || {};
/** @constructor */
FormatterWorker.FormattedContentBuilder = FormattedContentBuilder;