chrome-devtools-frontend
Version:
Chrome DevTools UI
132 lines (112 loc) • 3.43 kB
text/typescript
// 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.
export class FormattedContentBuilder {
#lastOriginalPosition = 0;
#formattedContent: string[] = [];
#formattedContentLength = 0;
#lastFormattedPosition = 0;
#nestingLevel = 0;
#newLines = 0;
#enforceSpaceBetweenWords = true;
#softSpace = false;
#hardSpaces = 0;
#cachedIndents = new Map<number, string>();
#canBeIdentifierOrNumber = /[$\u200C\u200D\p{ID_Continue}]/u;
mapping = {original: [0], formatted: [0]};
constructor(private indentString: string) {
}
setEnforceSpaceBetweenWords(value: boolean): boolean {
const oldValue = this.#enforceSpaceBetweenWords;
this.#enforceSpaceBetweenWords = value;
return oldValue;
}
addToken(token: string, offset: number): void {
const lastCharOfLastToken = this.#formattedContent.at(-1)?.at(-1) ?? '';
if (this.#enforceSpaceBetweenWords && this.#canBeIdentifierOrNumber.test(lastCharOfLastToken) &&
this.#canBeIdentifierOrNumber.test(token)) {
this.addSoftSpace();
}
this.#appendFormatting();
// Insert token.
this.#addMappingIfNeeded(offset);
this.#addText(token);
}
addSoftSpace(): void {
if (!this.#hardSpaces) {
this.#softSpace = true;
}
}
addHardSpace(): void {
this.#softSpace = false;
++this.#hardSpaces;
}
addNewLine(noSquash?: boolean): void {
// Avoid leading newlines.
if (!this.#formattedContentLength) {
return;
}
if (noSquash) {
++this.#newLines;
} else {
this.#newLines = this.#newLines || 1;
}
}
increaseNestingLevel(): void {
this.#nestingLevel += 1;
}
decreaseNestingLevel(): void {
if (this.#nestingLevel > 0) {
this.#nestingLevel -= 1;
}
}
content(): string {
return this.#formattedContent.join('') + (this.#newLines ? '\n' : '');
}
#appendFormatting(): void {
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;
}
#indent(): string {
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;
}
#addText(text: string): void {
this.#formattedContent.push(text);
this.#formattedContentLength += text.length;
}
#addMappingIfNeeded(originalPosition: number): void {
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;
}
}