123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- const string_utils_js_1 = require("./utils/string_utils.js");
- /** @internal */
- var CommentChar;
- (function (CommentChar) {
- CommentChar[CommentChar["Line"] = 0] = "Line";
- CommentChar[CommentChar["Star"] = 1] = "Star";
- })(CommentChar || (CommentChar = {}));
- // Using the char codes is a performance improvement (about 5.5% faster when writing because it eliminates additional string allocations).
- const CHARS = {
- BACK_SLASH: "\\".charCodeAt(0),
- FORWARD_SLASH: "/".charCodeAt(0),
- NEW_LINE: "\n".charCodeAt(0),
- CARRIAGE_RETURN: "\r".charCodeAt(0),
- ASTERISK: "*".charCodeAt(0),
- DOUBLE_QUOTE: "\"".charCodeAt(0),
- SINGLE_QUOTE: "'".charCodeAt(0),
- BACK_TICK: "`".charCodeAt(0),
- OPEN_BRACE: "{".charCodeAt(0),
- CLOSE_BRACE: "}".charCodeAt(0),
- DOLLAR_SIGN: "$".charCodeAt(0),
- SPACE: " ".charCodeAt(0),
- TAB: "\t".charCodeAt(0),
- };
- const isCharToHandle = new Set([
- CHARS.BACK_SLASH,
- CHARS.FORWARD_SLASH,
- CHARS.NEW_LINE,
- CHARS.CARRIAGE_RETURN,
- CHARS.ASTERISK,
- CHARS.DOUBLE_QUOTE,
- CHARS.SINGLE_QUOTE,
- CHARS.BACK_TICK,
- CHARS.OPEN_BRACE,
- CHARS.CLOSE_BRACE,
- ]);
- /**
- * Code writer that assists with formatting and visualizing blocks of JavaScript or TypeScript code.
- */
- class CodeBlockWriter {
- /**
- * Constructor.
- * @param opts - Options for the writer.
- */
- constructor(opts = {}) {
- /** @internal */
- Object.defineProperty(this, "_indentationText", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_newLine", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_useTabs", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_quoteChar", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_indentNumberOfSpaces", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_currentIndentation", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: 0
- });
- /** @internal */
- Object.defineProperty(this, "_queuedIndentation", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_queuedOnlyIfNotBlock", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: void 0
- });
- /** @internal */
- Object.defineProperty(this, "_length", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: 0
- });
- /** @internal */
- Object.defineProperty(this, "_newLineOnNextWrite", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: false
- });
- /** @internal */
- Object.defineProperty(this, "_currentCommentChar", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: undefined
- });
- /** @internal */
- Object.defineProperty(this, "_stringCharStack", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: []
- });
- /** @internal */
- Object.defineProperty(this, "_isInRegEx", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: false
- });
- /** @internal */
- Object.defineProperty(this, "_isOnFirstLineOfBlock", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: true
- });
- // An array of strings is used rather than a single string because it was
- // found to be ~11x faster when printing a 10K line file (~11s to ~1s).
- /** @internal */
- Object.defineProperty(this, "_texts", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: []
- });
- this._newLine = opts.newLine || "\n";
- this._useTabs = opts.useTabs || false;
- this._indentNumberOfSpaces = opts.indentNumberOfSpaces || 4;
- this._indentationText = getIndentationText(this._useTabs, this._indentNumberOfSpaces);
- this._quoteChar = opts.useSingleQuote ? "'" : `"`;
- }
- /**
- * Gets the options.
- */
- getOptions() {
- return {
- indentNumberOfSpaces: this._indentNumberOfSpaces,
- newLine: this._newLine,
- useTabs: this._useTabs,
- useSingleQuote: this._quoteChar === "'",
- };
- }
- queueIndentationLevel(countOrText) {
- this._queuedIndentation = this._getIndentationLevelFromArg(countOrText);
- this._queuedOnlyIfNotBlock = undefined;
- return this;
- }
- /**
- * Writes the text within the provided action with hanging indentation.
- * @param action - Action to perform with hanging indentation.
- */
- hangingIndent(action) {
- return this._withResetIndentation(() => this.queueIndentationLevel(this.getIndentationLevel() + 1), action);
- }
- /**
- * Writes the text within the provided action with hanging indentation unless writing a block.
- * @param action - Action to perform with hanging indentation unless a block is written.
- */
- hangingIndentUnlessBlock(action) {
- return this._withResetIndentation(() => {
- this.queueIndentationLevel(this.getIndentationLevel() + 1);
- this._queuedOnlyIfNotBlock = true;
- }, action);
- }
- setIndentationLevel(countOrText) {
- this._currentIndentation = this._getIndentationLevelFromArg(countOrText);
- return this;
- }
- withIndentationLevel(countOrText, action) {
- return this._withResetIndentation(() => this.setIndentationLevel(countOrText), action);
- }
- /** @internal */
- _withResetIndentation(setStateAction, writeAction) {
- const previousState = this._getIndentationState();
- setStateAction();
- try {
- writeAction();
- }
- finally {
- this._setIndentationState(previousState);
- }
- return this;
- }
- /**
- * Gets the current indentation level.
- */
- getIndentationLevel() {
- return this._currentIndentation;
- }
- /**
- * Writes a block using braces.
- * @param block - Write using the writer within this block.
- */
- block(block) {
- this._newLineIfNewLineOnNextWrite();
- if (this.getLength() > 0 && !this.isLastNewLine()) {
- this.spaceIfLastNot();
- }
- this.inlineBlock(block);
- this._newLineOnNextWrite = true;
- return this;
- }
- /**
- * Writes an inline block with braces.
- * @param block - Write using the writer within this block.
- */
- inlineBlock(block) {
- this._newLineIfNewLineOnNextWrite();
- this.write("{");
- this._indentBlockInternal(block);
- this.newLineIfLastNot().write("}");
- return this;
- }
- indent(timesOrBlock = 1) {
- if (typeof timesOrBlock === "number") {
- this._newLineIfNewLineOnNextWrite();
- return this.write(this._indentationText.repeat(timesOrBlock));
- }
- else {
- this._indentBlockInternal(timesOrBlock);
- if (!this.isLastNewLine()) {
- this._newLineOnNextWrite = true;
- }
- return this;
- }
- }
- /** @internal */
- _indentBlockInternal(block) {
- if (this.getLastChar() != null) {
- this.newLineIfLastNot();
- }
- this._currentIndentation++;
- this._isOnFirstLineOfBlock = true;
- if (block != null) {
- block();
- }
- this._isOnFirstLineOfBlock = false;
- this._currentIndentation = Math.max(0, this._currentIndentation - 1);
- }
- conditionalWriteLine(condition, strOrFunc) {
- if (condition) {
- this.writeLine((0, string_utils_js_1.getStringFromStrOrFunc)(strOrFunc));
- }
- return this;
- }
- /**
- * Writes a line of text.
- * @param text - String to write.
- */
- writeLine(text) {
- this._newLineIfNewLineOnNextWrite();
- if (this.getLastChar() != null) {
- this.newLineIfLastNot();
- }
- this._writeIndentingNewLines(text);
- this.newLine();
- return this;
- }
- /**
- * Writes a newline if the last line was not a newline.
- */
- newLineIfLastNot() {
- this._newLineIfNewLineOnNextWrite();
- if (!this.isLastNewLine()) {
- this.newLine();
- }
- return this;
- }
- /**
- * Writes a blank line if the last written text was not a blank line.
- */
- blankLineIfLastNot() {
- if (!this.isLastBlankLine()) {
- this.blankLine();
- }
- return this;
- }
- /**
- * Writes a blank line if the condition is true.
- * @param condition - Condition to evaluate.
- */
- conditionalBlankLine(condition) {
- if (condition) {
- this.blankLine();
- }
- return this;
- }
- /**
- * Writes a blank line.
- */
- blankLine() {
- return this.newLineIfLastNot().newLine();
- }
- /**
- * Writes a newline if the condition is true.
- * @param condition - Condition to evaluate.
- */
- conditionalNewLine(condition) {
- if (condition) {
- this.newLine();
- }
- return this;
- }
- /**
- * Writes a newline.
- */
- newLine() {
- this._newLineOnNextWrite = false;
- this._baseWriteNewline();
- return this;
- }
- quote(text) {
- this._newLineIfNewLineOnNextWrite();
- this._writeIndentingNewLines(text == null ? this._quoteChar : this._quoteChar + (0, string_utils_js_1.escapeForWithinString)(text, this._quoteChar) + this._quoteChar);
- return this;
- }
- /**
- * Writes a space if the last character was not a space.
- */
- spaceIfLastNot() {
- this._newLineIfNewLineOnNextWrite();
- if (!this.isLastSpace()) {
- this._writeIndentingNewLines(" ");
- }
- return this;
- }
- /**
- * Writes a space.
- * @param times - Number of times to write a space.
- */
- space(times = 1) {
- this._newLineIfNewLineOnNextWrite();
- this._writeIndentingNewLines(" ".repeat(times));
- return this;
- }
- /**
- * Writes a tab if the last character was not a tab.
- */
- tabIfLastNot() {
- this._newLineIfNewLineOnNextWrite();
- if (!this.isLastTab()) {
- this._writeIndentingNewLines("\t");
- }
- return this;
- }
- /**
- * Writes a tab.
- * @param times - Number of times to write a tab.
- */
- tab(times = 1) {
- this._newLineIfNewLineOnNextWrite();
- this._writeIndentingNewLines("\t".repeat(times));
- return this;
- }
- conditionalWrite(condition, textOrFunc) {
- if (condition) {
- this.write((0, string_utils_js_1.getStringFromStrOrFunc)(textOrFunc));
- }
- return this;
- }
- /**
- * Writes the provided text.
- * @param text - Text to write.
- */
- write(text) {
- this._newLineIfNewLineOnNextWrite();
- this._writeIndentingNewLines(text);
- return this;
- }
- /**
- * Writes text to exit a comment if in a comment.
- */
- closeComment() {
- const commentChar = this._currentCommentChar;
- switch (commentChar) {
- case CommentChar.Line:
- this.newLine();
- break;
- case CommentChar.Star:
- if (!this.isLastNewLine()) {
- this.spaceIfLastNot();
- }
- this.write("*/");
- break;
- default: {
- const _assertUndefined = commentChar;
- break;
- }
- }
- return this;
- }
- /**
- * Inserts text at the provided position.
- *
- * This method is "unsafe" because it won't update the state of the writer unless
- * inserting at the end position. It is biased towards being fast at inserting closer
- * to the start or end, but slower to insert in the middle. Only use this if
- * absolutely necessary.
- * @param pos - Position to insert at.
- * @param text - Text to insert.
- */
- unsafeInsert(pos, text) {
- const textLength = this._length;
- const texts = this._texts;
- verifyInput();
- if (pos === textLength) {
- return this.write(text);
- }
- updateInternalArray();
- this._length += text.length;
- return this;
- function verifyInput() {
- if (pos < 0) {
- throw new Error(`Provided position of '${pos}' was less than zero.`);
- }
- if (pos > textLength) {
- throw new Error(`Provided position of '${pos}' was greater than the text length of '${textLength}'.`);
- }
- }
- function updateInternalArray() {
- const { index, localIndex } = getArrayIndexAndLocalIndex();
- if (localIndex === 0) {
- texts.splice(index, 0, text);
- }
- else if (localIndex === texts[index].length) {
- texts.splice(index + 1, 0, text);
- }
- else {
- const textItem = texts[index];
- const startText = textItem.substring(0, localIndex);
- const endText = textItem.substring(localIndex);
- texts.splice(index, 1, startText, text, endText);
- }
- }
- function getArrayIndexAndLocalIndex() {
- if (pos < textLength / 2) {
- // start searching from the front
- let endPos = 0;
- for (let i = 0; i < texts.length; i++) {
- const textItem = texts[i];
- const startPos = endPos;
- endPos += textItem.length;
- if (endPos >= pos) {
- return { index: i, localIndex: pos - startPos };
- }
- }
- }
- else {
- // start searching from the back
- let startPos = textLength;
- for (let i = texts.length - 1; i >= 0; i--) {
- const textItem = texts[i];
- startPos -= textItem.length;
- if (startPos <= pos) {
- return { index: i, localIndex: pos - startPos };
- }
- }
- }
- throw new Error("Unhandled situation inserting. This should never happen.");
- }
- }
- /**
- * Gets the length of the string in the writer.
- */
- getLength() {
- return this._length;
- }
- /**
- * Gets if the writer is currently in a comment.
- */
- isInComment() {
- return this._currentCommentChar !== undefined;
- }
- /**
- * Gets if the writer is currently at the start of the first line of the text, block, or indentation block.
- */
- isAtStartOfFirstLineOfBlock() {
- return this.isOnFirstLineOfBlock() && (this.isLastNewLine() || this.getLastChar() == null);
- }
- /**
- * Gets if the writer is currently on the first line of the text, block, or indentation block.
- */
- isOnFirstLineOfBlock() {
- return this._isOnFirstLineOfBlock;
- }
- /**
- * Gets if the writer is currently in a string.
- */
- isInString() {
- return this._stringCharStack.length > 0 && this._stringCharStack[this._stringCharStack.length - 1] !== CHARS.OPEN_BRACE;
- }
- /**
- * Gets if the last chars written were for a newline.
- */
- isLastNewLine() {
- const lastChar = this.getLastChar();
- return lastChar === "\n" || lastChar === "\r";
- }
- /**
- * Gets if the last chars written were for a blank line.
- */
- isLastBlankLine() {
- let foundCount = 0;
- // todo: consider extracting out iterating over past characters, but don't use
- // an iterator because it will be slow.
- for (let i = this._texts.length - 1; i >= 0; i--) {
- const currentText = this._texts[i];
- for (let j = currentText.length - 1; j >= 0; j--) {
- const currentChar = currentText.charCodeAt(j);
- if (currentChar === CHARS.NEW_LINE) {
- foundCount++;
- if (foundCount === 2) {
- return true;
- }
- }
- else if (currentChar !== CHARS.CARRIAGE_RETURN) {
- return false;
- }
- }
- }
- return false;
- }
- /**
- * Gets if the last char written was a space.
- */
- isLastSpace() {
- return this.getLastChar() === " ";
- }
- /**
- * Gets if the last char written was a tab.
- */
- isLastTab() {
- return this.getLastChar() === "\t";
- }
- /**
- * Gets the last char written.
- */
- getLastChar() {
- const charCode = this._getLastCharCodeWithOffset(0);
- return charCode == null ? undefined : String.fromCharCode(charCode);
- }
- /**
- * Gets if the writer ends with the provided text.
- * @param text - Text to check if the writer ends with the provided text.
- */
- endsWith(text) {
- const length = this._length;
- return this.iterateLastCharCodes((charCode, index) => {
- const offset = length - index;
- const textIndex = text.length - offset;
- if (text.charCodeAt(textIndex) !== charCode) {
- return false;
- }
- return textIndex === 0 ? true : undefined;
- }) || false;
- }
- /**
- * Iterates over the writer characters in reverse order. The iteration stops when a non-null or
- * undefined value is returned from the action. The returned value is then returned by the method.
- *
- * @remarks It is much more efficient to use this method rather than `#toString()` since `#toString()`
- * will combine the internal array into a string.
- */
- iterateLastChars(action) {
- return this.iterateLastCharCodes((charCode, index) => action(String.fromCharCode(charCode), index));
- }
- /**
- * Iterates over the writer character char codes in reverse order. The iteration stops when a non-null or
- * undefined value is returned from the action. The returned value is then returned by the method.
- *
- * @remarks It is much more efficient to use this method rather than `#toString()` since `#toString()`
- * will combine the internal array into a string. Additionally, this is slightly more efficient that
- * `iterateLastChars` as this won't allocate a string per character.
- */
- iterateLastCharCodes(action) {
- let index = this._length;
- for (let i = this._texts.length - 1; i >= 0; i--) {
- const currentText = this._texts[i];
- for (let j = currentText.length - 1; j >= 0; j--) {
- index--;
- const result = action(currentText.charCodeAt(j), index);
- if (result != null) {
- return result;
- }
- }
- }
- return undefined;
- }
- /**
- * Gets the writer's text.
- */
- toString() {
- if (this._texts.length > 1) {
- const text = this._texts.join("");
- this._texts.length = 0;
- this._texts.push(text);
- }
- return this._texts[0] || "";
- }
- /** @internal */
- _writeIndentingNewLines(text) {
- text = text || "";
- if (text.length === 0) {
- writeIndividual(this, "");
- return;
- }
- const items = text.split(CodeBlockWriter._newLineRegEx);
- items.forEach((s, i) => {
- if (i > 0) {
- this._baseWriteNewline();
- }
- if (s.length === 0) {
- return;
- }
- writeIndividual(this, s);
- });
- function writeIndividual(writer, s) {
- if (!writer.isInString()) {
- const isAtStartOfLine = writer.isLastNewLine() || writer.getLastChar() == null;
- if (isAtStartOfLine) {
- writer._writeIndentation();
- }
- }
- writer._updateInternalState(s);
- writer._internalWrite(s);
- }
- }
- /** @internal */
- _baseWriteNewline() {
- if (this._currentCommentChar === CommentChar.Line) {
- this._currentCommentChar = undefined;
- }
- const lastStringCharOnStack = this._stringCharStack[this._stringCharStack.length - 1];
- if ((lastStringCharOnStack === CHARS.DOUBLE_QUOTE || lastStringCharOnStack === CHARS.SINGLE_QUOTE) && this._getLastCharCodeWithOffset(0) !== CHARS.BACK_SLASH) {
- this._stringCharStack.pop();
- }
- this._internalWrite(this._newLine);
- this._isOnFirstLineOfBlock = false;
- this._dequeueQueuedIndentation();
- }
- /** @internal */
- _dequeueQueuedIndentation() {
- if (this._queuedIndentation == null) {
- return;
- }
- if (this._queuedOnlyIfNotBlock && wasLastBlock(this)) {
- this._queuedIndentation = undefined;
- this._queuedOnlyIfNotBlock = undefined;
- }
- else {
- this._currentIndentation = this._queuedIndentation;
- this._queuedIndentation = undefined;
- }
- function wasLastBlock(writer) {
- let foundNewLine = false;
- return writer.iterateLastCharCodes(charCode => {
- switch (charCode) {
- case CHARS.NEW_LINE:
- if (foundNewLine) {
- return false;
- }
- else {
- foundNewLine = true;
- }
- break;
- case CHARS.CARRIAGE_RETURN:
- return undefined;
- case CHARS.OPEN_BRACE:
- return true;
- default:
- return false;
- }
- });
- }
- }
- /** @internal */
- _updateInternalState(str) {
- for (let i = 0; i < str.length; i++) {
- const currentChar = str.charCodeAt(i);
- // This is a performance optimization to short circuit all the checks below. If the current char
- // is not in this set then it won't change any internal state so no need to continue and do
- // so many other checks (this made it 3x faster in one scenario I tested).
- if (!isCharToHandle.has(currentChar)) {
- continue;
- }
- const pastChar = i === 0 ? this._getLastCharCodeWithOffset(0) : str.charCodeAt(i - 1);
- const pastPastChar = i === 0 ? this._getLastCharCodeWithOffset(1) : i === 1 ? this._getLastCharCodeWithOffset(0) : str.charCodeAt(i - 2);
- // handle regex
- if (this._isInRegEx) {
- if (pastChar === CHARS.FORWARD_SLASH && pastPastChar !== CHARS.BACK_SLASH || pastChar === CHARS.NEW_LINE) {
- this._isInRegEx = false;
- }
- else {
- continue;
- }
- }
- else if (!this.isInString() && !this.isInComment() && isRegExStart(currentChar, pastChar, pastPastChar)) {
- this._isInRegEx = true;
- continue;
- }
- // handle comments
- if (!this.isInString()) {
- if (this._currentCommentChar == null && pastChar === CHARS.FORWARD_SLASH && currentChar === CHARS.FORWARD_SLASH) {
- this._currentCommentChar = CommentChar.Line;
- }
- else if (this._currentCommentChar == null && pastChar === CHARS.FORWARD_SLASH && currentChar === CHARS.ASTERISK) {
- this._currentCommentChar = CommentChar.Star;
- }
- else if (this._currentCommentChar === CommentChar.Star && pastChar === CHARS.ASTERISK && currentChar === CHARS.FORWARD_SLASH) {
- this._currentCommentChar = undefined;
- }
- }
- if (this.isInComment()) {
- continue;
- }
- // handle strings
- const lastStringCharOnStack = this._stringCharStack.length === 0 ? undefined : this._stringCharStack[this._stringCharStack.length - 1];
- if (pastChar !== CHARS.BACK_SLASH && (currentChar === CHARS.DOUBLE_QUOTE || currentChar === CHARS.SINGLE_QUOTE || currentChar === CHARS.BACK_TICK)) {
- if (lastStringCharOnStack === currentChar) {
- this._stringCharStack.pop();
- }
- else if (lastStringCharOnStack === CHARS.OPEN_BRACE || lastStringCharOnStack === undefined) {
- this._stringCharStack.push(currentChar);
- }
- }
- else if (pastPastChar !== CHARS.BACK_SLASH && pastChar === CHARS.DOLLAR_SIGN && currentChar === CHARS.OPEN_BRACE && lastStringCharOnStack === CHARS.BACK_TICK) {
- this._stringCharStack.push(currentChar);
- }
- else if (currentChar === CHARS.CLOSE_BRACE && lastStringCharOnStack === CHARS.OPEN_BRACE) {
- this._stringCharStack.pop();
- }
- }
- }
- /** @internal - This is private, but exposed for testing. */
- _getLastCharCodeWithOffset(offset) {
- if (offset >= this._length || offset < 0) {
- return undefined;
- }
- for (let i = this._texts.length - 1; i >= 0; i--) {
- const currentText = this._texts[i];
- if (offset >= currentText.length) {
- offset -= currentText.length;
- }
- else {
- return currentText.charCodeAt(currentText.length - 1 - offset);
- }
- }
- return undefined;
- }
- /** @internal */
- _writeIndentation() {
- const flooredIndentation = Math.floor(this._currentIndentation);
- this._internalWrite(this._indentationText.repeat(flooredIndentation));
- const overflow = this._currentIndentation - flooredIndentation;
- if (this._useTabs) {
- if (overflow > 0.5) {
- this._internalWrite(this._indentationText);
- }
- }
- else {
- const portion = Math.round(this._indentationText.length * overflow);
- // build up the string first, then append it for performance reasons
- let text = "";
- for (let i = 0; i < portion; i++) {
- text += this._indentationText[i];
- }
- this._internalWrite(text);
- }
- }
- /** @internal */
- _newLineIfNewLineOnNextWrite() {
- if (!this._newLineOnNextWrite) {
- return;
- }
- this._newLineOnNextWrite = false;
- this.newLine();
- }
- /** @internal */
- _internalWrite(text) {
- if (text.length === 0) {
- return;
- }
- this._texts.push(text);
- this._length += text.length;
- }
- /** @internal */
- _getIndentationLevelFromArg(countOrText) {
- if (typeof countOrText === "number") {
- if (countOrText < 0) {
- throw new Error("Passed in indentation level should be greater than or equal to 0.");
- }
- return countOrText;
- }
- else if (typeof countOrText === "string") {
- if (!CodeBlockWriter._spacesOrTabsRegEx.test(countOrText)) {
- throw new Error("Provided string must be empty or only contain spaces or tabs.");
- }
- const { spacesCount, tabsCount } = getSpacesAndTabsCount(countOrText);
- return tabsCount + spacesCount / this._indentNumberOfSpaces;
- }
- else {
- throw new Error("Argument provided must be a string or number.");
- }
- }
- /** @internal */
- _setIndentationState(state) {
- this._currentIndentation = state.current;
- this._queuedIndentation = state.queued;
- this._queuedOnlyIfNotBlock = state.queuedOnlyIfNotBlock;
- }
- /** @internal */
- _getIndentationState() {
- return {
- current: this._currentIndentation,
- queued: this._queuedIndentation,
- queuedOnlyIfNotBlock: this._queuedOnlyIfNotBlock,
- };
- }
- }
- /** @internal */
- Object.defineProperty(CodeBlockWriter, "_newLineRegEx", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: /\r?\n/
- });
- /** @internal */
- Object.defineProperty(CodeBlockWriter, "_spacesOrTabsRegEx", {
- enumerable: true,
- configurable: true,
- writable: true,
- value: /^[ \t]*$/
- });
- exports.default = CodeBlockWriter;
- function isRegExStart(currentChar, pastChar, pastPastChar) {
- return pastChar === CHARS.FORWARD_SLASH
- && currentChar !== CHARS.FORWARD_SLASH
- && currentChar !== CHARS.ASTERISK
- && pastPastChar !== CHARS.ASTERISK
- && pastPastChar !== CHARS.FORWARD_SLASH;
- }
- function getIndentationText(useTabs, numberSpaces) {
- if (useTabs) {
- return "\t";
- }
- return Array(numberSpaces + 1).join(" ");
- }
- function getSpacesAndTabsCount(str) {
- let spacesCount = 0;
- let tabsCount = 0;
- for (let i = 0; i < str.length; i++) {
- const charCode = str.charCodeAt(i);
- if (charCode === CHARS.SPACE) {
- spacesCount++;
- }
- else if (charCode === CHARS.TAB) {
- tabsCount++;
- }
- }
- return { spacesCount, tabsCount };
- }
|