code-block-writer
Advanced tools
Comparing version 5.0.0 to 6.0.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var CommentChar_1 = require("./CommentChar"); | ||
var CodeBlockWriter = (function () { | ||
@@ -7,7 +8,28 @@ function CodeBlockWriter(opts) { | ||
this._text = ""; | ||
this._isAtStartOfBlock = false; | ||
this._newLineOnNextWrite = false; | ||
this._currentCommentChar = undefined; | ||
this._stringCharStack = []; | ||
this._newLine = (opts && opts.newLine) || "\n"; | ||
this._indentationText = getIndentationText((opts && opts.useTabs) || false, (opts && opts.indentNumberOfSpaces) || 4); | ||
this._useTabs = (opts && opts.useTabs) || false; | ||
this._indentNumberOfSpaces = (opts && opts.indentNumberOfSpaces) || 4; | ||
this._indentationText = getIndentationText(this._useTabs, this._indentNumberOfSpaces); | ||
} | ||
CodeBlockWriter.prototype.setIndentationLevel = function (countOrText) { | ||
if (typeof countOrText === "number") { | ||
if (countOrText < 0) | ||
throw new Error("Passed in indentation level should be greater than or equal to 0."); | ||
this._currentIndentation = countOrText; | ||
return this; | ||
} | ||
else if (typeof countOrText === "string") { | ||
if (!/^[ \t]*$/.test(countOrText)) | ||
throw new Error("Provided string must be empty or only contain spaces or tabs."); | ||
var _a = getSpacesAndTabsCount(countOrText), spacesCount = _a.spacesCount, tabsCount = _a.tabsCount; | ||
this._currentIndentation = tabsCount + Math.round(Math.max(0, spacesCount - 1) / this._indentNumberOfSpaces); | ||
return this; | ||
} | ||
else { | ||
throw new Error("Argument provided must be a string or number."); | ||
} | ||
}; | ||
CodeBlockWriter.prototype.block = function (block) { | ||
@@ -25,5 +47,5 @@ this.newLineIfNewLineOnNextWrite(); | ||
this.newLine(); | ||
this._isAtStartOfBlock = true; | ||
block(); | ||
this._currentIndentation--; | ||
if (this._currentIndentation > 0) | ||
this._currentIndentation--; | ||
this.newLineIfLastNotNewLine().write("}"); | ||
@@ -65,3 +87,3 @@ return this; | ||
this._newLineOnNextWrite = false; | ||
this.baseWrite(this._newLine); | ||
this.baseWriteNewline(); | ||
return this; | ||
@@ -73,3 +95,3 @@ }; | ||
if (lastChar !== " ") | ||
this.baseWrite(" "); | ||
this.writeIndentingNewLines(" "); | ||
return this; | ||
@@ -90,4 +112,10 @@ }; | ||
}; | ||
CodeBlockWriter.prototype.isInComment = function () { | ||
return this._currentCommentChar !== undefined; | ||
}; | ||
CodeBlockWriter.prototype.isInString = function () { | ||
return this._stringCharStack.length > 0 && this._stringCharStack[this._stringCharStack.length - 1] !== "{"; | ||
}; | ||
CodeBlockWriter.prototype.toString = function () { | ||
return this.removeConsecutiveNewLineAtEndOfString(this._text); | ||
return this._text; | ||
}; | ||
@@ -99,23 +127,41 @@ CodeBlockWriter.prototype.writeIndentingNewLines = function (str) { | ||
if (i > 0) | ||
_this.baseWrite(_this._newLine); | ||
_this.baseWrite(s); | ||
if (i > 0 && i === items.length - 1 && s.length === 0) | ||
_this.baseWrite(_this._newLine); | ||
_this.baseWriteNewline(); | ||
if (s.length === 0) | ||
return; | ||
var isAtStartOfLine = _this.isLastCharANewLine() || _this._text.length === 0; | ||
if (isAtStartOfLine && !_this.isInString()) | ||
_this.writeIndentation(); | ||
_this.updateStringStack(s); | ||
_this._text += s; | ||
}); | ||
}; | ||
CodeBlockWriter.prototype.baseWrite = function (str) { | ||
this._isAtStartOfBlock = false; | ||
if (str == null || str.length === 0) | ||
return this; | ||
if (str !== this._newLine && this.isLastCharANewLine()) | ||
this.writeIndentation(); | ||
this._text += str; | ||
return this; | ||
CodeBlockWriter.prototype.baseWriteNewline = function () { | ||
if (this._currentCommentChar === CommentChar_1.CommentChar.Line) | ||
this._currentCommentChar = undefined; | ||
this._text += this._newLine; | ||
}; | ||
CodeBlockWriter.prototype.removeConsecutiveNewLineAtEndOfString = function (text) { | ||
var consecutiveNewline = this._newLine + this._newLine; | ||
var lastIndexOfConsecutiveNewLines = text.lastIndexOf(consecutiveNewline); | ||
if (lastIndexOfConsecutiveNewLines >= 0 && lastIndexOfConsecutiveNewLines === text.length - consecutiveNewline.length) | ||
text = text.substr(0, text.length - this._newLine.length); | ||
return text; | ||
CodeBlockWriter.prototype.updateStringStack = function (str) { | ||
for (var i = 0; i < str.length; i++) { | ||
var currentChar = str[i]; | ||
var pastChar = i === 0 ? this.getLastChar() : str[i - 1]; | ||
var lastCharOnStack = this._stringCharStack.length === 0 ? undefined : this._stringCharStack[this._stringCharStack.length - 1]; | ||
if (this._currentCommentChar == null && pastChar === "/" && currentChar === "/") | ||
this._currentCommentChar = CommentChar_1.CommentChar.Line; | ||
else if (this._currentCommentChar == null && pastChar === "/" && currentChar === "*") | ||
this._currentCommentChar = CommentChar_1.CommentChar.Star; | ||
else if (this._currentCommentChar === CommentChar_1.CommentChar.Star && pastChar === "*" && currentChar === "/") | ||
this._currentCommentChar = undefined; | ||
if (this.isInComment()) | ||
continue; | ||
else if (currentChar === "\"" || currentChar === "'" || currentChar === "`") { | ||
if (lastCharOnStack === currentChar) | ||
this._stringCharStack.pop(); | ||
else if (lastCharOnStack === "{" || lastCharOnStack === undefined) | ||
this._stringCharStack.push(currentChar); | ||
} | ||
else if (pastChar === "$" && currentChar === "{" && lastCharOnStack === "`") | ||
this._stringCharStack.push(currentChar); | ||
else if (currentChar === "}" && lastCharOnStack === "{") | ||
this._stringCharStack.pop(); | ||
} | ||
}; | ||
@@ -126,6 +172,5 @@ CodeBlockWriter.prototype.isLastCharANewLine = function () { | ||
CodeBlockWriter.prototype.getLastChar = function () { | ||
var lastChar = null; | ||
if (this._text.length > 0) | ||
lastChar = this._text[this._text.length - 1]; | ||
return lastChar; | ||
if (this._text.length === 0) | ||
return undefined; | ||
return this._text[this._text.length - 1]; | ||
}; | ||
@@ -149,3 +194,15 @@ CodeBlockWriter.prototype.writeIndentation = function () { | ||
} | ||
function getSpacesAndTabsCount(str) { | ||
var spacesCount = 0; | ||
var tabsCount = 0; | ||
for (var _i = 0, str_1 = str; _i < str_1.length; _i++) { | ||
var char = str_1[_i]; | ||
if (char === "\t") | ||
tabsCount++; | ||
else if (char === " ") | ||
spacesCount++; | ||
} | ||
return { spacesCount: spacesCount, tabsCount: tabsCount }; | ||
} | ||
//# sourceMappingURL=code-block-writer.js.map |
@@ -62,2 +62,9 @@ "use strict"; | ||
}); | ||
it("should not write indentation between newlines", function () { | ||
var expected = " test\n\n test"; | ||
doTest(expected, function (writer) { | ||
writer.setIndentationLevel(1); | ||
writer.write("test\n\ntest"); | ||
}); | ||
}); | ||
}); | ||
@@ -119,2 +126,18 @@ describe("#block()", function () { | ||
}); | ||
it("should not indent when in a string", function () { | ||
var expected = "block {\n const t = `\nt`;\n const u = 1;\n}"; | ||
doTest(expected, function (writer) { | ||
writer.write("block").block(function () { | ||
writer.write("const t = `\nt`;\nconst u = 1;"); | ||
}); | ||
}); | ||
}); | ||
it("should indent when in a comment", function () { | ||
var expected = "block {\n const t = /*\n const u = 1;*/\n}"; | ||
doTest(expected, function (writer) { | ||
writer.write("block").block(function () { | ||
writer.write("const t = /*\nconst u = 1;*/"); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -232,4 +255,4 @@ describe("#inlineBlock()", function () { | ||
}); | ||
it("should never have two newlines at the end of a file", function () { | ||
var expected = "text\n"; | ||
it("should be allowed to have two newlines at the end of a file", function () { | ||
var expected = "text\n\n"; | ||
doTest(expected, function (writer) { | ||
@@ -318,2 +341,154 @@ writer.write("text").newLine().newLine(); | ||
} | ||
describe("#setIdentationLevel", function () { | ||
it("should throw when providing a negative number", function () { | ||
var writer = new code_block_writer_1.default(); | ||
assert.throws(function () { return writer.setIndentationLevel(-1); }); | ||
}); | ||
it("should throw when not providing a number or string", function () { | ||
var writer = new code_block_writer_1.default(); | ||
assert.throws(function () { return writer.setIndentationLevel({}); }); | ||
}); | ||
it("should not throw when providing an empty string", function () { | ||
var writer = new code_block_writer_1.default(); | ||
assert.doesNotThrow(function () { return writer.setIndentationLevel(""); }); | ||
}); | ||
it("should throw when providing a string that doesn't contain only spaces and tabs", function () { | ||
var writer = new code_block_writer_1.default(); | ||
assert.throws(function () { return writer.setIndentationLevel(" \ta"); }); | ||
}); | ||
it("should be able to set the indentation level and it maintains it over newlines", function () { | ||
var writer = new code_block_writer_1.default(); | ||
writer.setIndentationLevel(2); | ||
writer.writeLine("t"); | ||
writer.writeLine("t"); | ||
assert.equal(writer.toString(), " t\n t\n"); | ||
}); | ||
it("should be able to set the indentation level to 0 within a block", function () { | ||
var writer = new code_block_writer_1.default(); | ||
writer.write("t").block(function () { | ||
writer.setIndentationLevel(0); | ||
writer.writeLine("t"); | ||
writer.writeLine("t"); | ||
}).write("t").block(function () { | ||
writer.write("t"); | ||
}); | ||
assert.equal(writer.toString(), "t {\nt\nt\n}\nt {\n t\n}"); | ||
}); | ||
function doSpacesTest(numberSpaces) { | ||
var writer = new code_block_writer_1.default({ indentNumberOfSpaces: numberSpaces }); | ||
var indent = Array(numberSpaces + 1).join(" "); | ||
writer.setIndentationLevel(indent + indent); | ||
writer.write("t").block(function () { return writer.write("t"); }); | ||
assert.equal(writer.toString(), indent + indent + "t {\n" + (indent + indent + indent) + "t\n" + (indent + indent) + "}"); | ||
} | ||
it("should be able to set the indentation level using a string with two spaces", function () { | ||
doSpacesTest(2); | ||
}); | ||
it("should be able to set the indentation level using a string with four spaces", function () { | ||
doSpacesTest(4); | ||
}); | ||
it("should be able to set the indentation level using a string with eight spaces", function () { | ||
doSpacesTest(8); | ||
}); | ||
it("should indent by the provided number of tabs", function () { | ||
var writer = new code_block_writer_1.default({ useTabs: true }); | ||
writer.setIndentationLevel("\t\t"); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), "\t\ts"); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round down)", function () { | ||
var writer = new code_block_writer_1.default({ useTabs: true }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), "\t\ts"); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round down, 2 spaces)", function () { | ||
var writer = new code_block_writer_1.default({ useTabs: true, indentNumberOfSpaces: 2 }); | ||
writer.setIndentationLevel("\t \t"); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), "\t\ts"); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round up)", function () { | ||
var writer = new code_block_writer_1.default({ useTabs: true }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), "\t\t\ts"); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (2 spaces)", function () { | ||
var writer = new code_block_writer_1.default({ useTabs: true, indentNumberOfSpaces: 2 }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), "\t\t\t\t\ts"); | ||
}); | ||
}); | ||
describe("#isInString", function () { | ||
function doTest(str, expectedValues) { | ||
assert.equal(str.length + 1, expectedValues.length); | ||
var writer = new code_block_writer_1.default(); | ||
assert.equal(writer.isInString(), expectedValues[0]); | ||
for (var i = 0; i < str.length; i++) { | ||
writer.write(str[i]); | ||
assert.equal(writer.isInString(), expectedValues[i + 1]); | ||
} | ||
} | ||
it("should be in a string while in double quotes", function () { | ||
doTest("s\"y\"", [false, false, true, true, false]); | ||
}); | ||
it("should be in a string while in single quotes", function () { | ||
doTest("s'y'", [false, false, true, true, false]); | ||
}); | ||
it("should be in a string while in backticks", function () { | ||
doTest("s`y`", [false, false, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in double quotes", function () { | ||
doTest("\"'`${}\"", [false, true, true, true, true, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in single quotes", function () { | ||
doTest("'\"`${}'", [false, true, true, true, true, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in back ticks", function () { | ||
doTest("`'\"`", [false, true, true, true, false]); | ||
}); | ||
it("should not be in a string while in backticks within braces", function () { | ||
doTest("`y${t}`", [false, true, true, true, false, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within a single quote string", function () { | ||
doTest("`${'t'}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within a double quote string", function () { | ||
doTest("`${\"t\"}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within back ticks", function () { | ||
doTest("`${`t`}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should not be in a string while in backticks within braces within back ticks within braces", function () { | ||
doTest("`${`${t}`}`", [false, true, true, false, true, true, false, false, true, false, true, false]); | ||
}); | ||
it("should not be in a string while comments", function () { | ||
doTest("//'t'", [false, false, false, false, false, false]); | ||
}); | ||
it("should be in a string while the previous line was a comment and now this is a string", function () { | ||
doTest("//t\n't'", [false, false, false, false, false, true, true, false]); | ||
}); | ||
it("should not be in a string for star comments", function () { | ||
doTest("/*\n't'\n*/'t'", [false, false, false, false, false, false, false, false, false, false, true, true, false]); | ||
}); | ||
}); | ||
describe("#isInComment", function () { | ||
function doTest(str, expectedValues) { | ||
assert.equal(str.length + 1, expectedValues.length); | ||
var writer = new code_block_writer_1.default(); | ||
assert.equal(writer.isInComment(), expectedValues[0]); | ||
for (var i = 0; i < str.length; i++) { | ||
writer.write(str[i]); | ||
assert.equal(writer.isInComment(), expectedValues[i + 1]); | ||
} | ||
} | ||
it("should be in a comment for star comments", function () { | ||
doTest("/*\nt\n*/", [false, false, true, true, true, true, true, false]); | ||
}); | ||
it("should be in a comment for line comments", function () { | ||
doTest("// t\nt", [false, false, true, true, true, false, false]); | ||
}); | ||
}); | ||
describe("indentNumberOfSpaces", function () { | ||
@@ -320,0 +495,0 @@ var writer = new code_block_writer_1.default({ indentNumberOfSpaces: 2 }); |
{ | ||
"name": "code-block-writer", | ||
"version": "5.0.0", | ||
"version": "6.0.0", | ||
"description": "A simple code writer that assists with formatting and visualizing blocks of code.", | ||
@@ -5,0 +5,0 @@ "main": "dist/code-block-writer.js", |
@@ -9,3 +9,3 @@ code-block-writer | ||
A simple code writer that assists with formatting and visualizing blocks of code. | ||
A simple code writer that assists with formatting and visualizing blocks of JavaScript or TypeScript code. | ||
@@ -65,2 +65,5 @@ ``` | ||
* `conditionalWriteLine(condition: boolean, str: string)` - Writes some text and adds a newline if the condition is matched. | ||
* `setIndentationLevel(indentationLevel: number)` - Sets the current indentation level. | ||
* `isInComment()` - Gets if the writer is currently in a comment. | ||
* `isInString()` - Gets if the writer is currently in a string. | ||
* `toString()` - Gets the string. |
@@ -0,14 +1,56 @@ | ||
import {CommentChar} from "./CommentChar"; | ||
export default class CodeBlockWriter { | ||
private readonly _indentationText: string; | ||
private readonly _newLine: string; | ||
private readonly _useTabs: boolean; | ||
private readonly _indentNumberOfSpaces: number; | ||
private _currentIndentation = 0; | ||
private _text = ""; | ||
private _isAtStartOfBlock = false; | ||
private _newLineOnNextWrite = false; | ||
private _currentCommentChar: CommentChar | undefined = undefined; | ||
private _stringCharStack: ("\"" | "'" | "`" | "{")[] = []; | ||
constructor(opts?: { newLine?: string; indentNumberOfSpaces?: number; useTabs?: boolean; }) { | ||
this._newLine = (opts && opts.newLine) || "\n"; | ||
this._indentationText = getIndentationText((opts && opts.useTabs) || false, (opts && opts.indentNumberOfSpaces) || 4); | ||
this._useTabs = (opts && opts.useTabs) || false; | ||
this._indentNumberOfSpaces = (opts && opts.indentNumberOfSpaces) || 4; | ||
this._indentationText = getIndentationText(this._useTabs, this._indentNumberOfSpaces); | ||
} | ||
/** | ||
* Sets the current indentation level. | ||
* @param indentationLevel - Indentation level to be at. | ||
*/ | ||
setIndentationLevel(indentationLevel: number): this; | ||
/** | ||
* Sets the current indentation using the provided indentation text. | ||
* @param indentationText - Gets the indentation level from the indentation text. | ||
*/ | ||
setIndentationLevel(indentationText: string): this; | ||
setIndentationLevel(countOrText: string | number) { | ||
if (typeof countOrText === "number") { | ||
if (countOrText < 0) | ||
throw new Error("Passed in indentation level should be greater than or equal to 0."); | ||
this._currentIndentation = countOrText; | ||
return this; | ||
} | ||
else if (typeof countOrText === "string") { | ||
if (!/^[ \t]*$/.test(countOrText)) | ||
throw new Error("Provided string must be empty or only contain spaces or tabs."); | ||
const {spacesCount, tabsCount} = getSpacesAndTabsCount(countOrText); | ||
this._currentIndentation = tabsCount + Math.round(Math.max(0, spacesCount - 1) / this._indentNumberOfSpaces); | ||
return this; | ||
} | ||
else { | ||
throw new Error("Argument provided must be a string or number."); | ||
} | ||
} | ||
/** | ||
* Writes a block using braces. | ||
* @param block - Write using the writer within this block. | ||
*/ | ||
block(block: () => void) { | ||
@@ -22,2 +64,6 @@ this.newLineIfNewLineOnNextWrite(); | ||
/** | ||
* Writes an inline block with braces. | ||
* @param block - Write using the writer within this block. | ||
*/ | ||
inlineBlock(block: () => void) { | ||
@@ -28,5 +74,5 @@ this.newLineIfNewLineOnNextWrite(); | ||
this.newLine(); | ||
this._isAtStartOfBlock = true; | ||
block(); | ||
this._currentIndentation--; | ||
if (this._currentIndentation > 0) | ||
this._currentIndentation--; | ||
this.newLineIfLastNotNewLine().write("}"); | ||
@@ -37,2 +83,7 @@ | ||
/** | ||
* Conditionally writes a line of text. | ||
* @param condition - Condition to evaluate. | ||
* @param str - String to write if the condition is true. | ||
*/ | ||
conditionalWriteLine(condition: boolean, str: string) { | ||
@@ -45,2 +96,6 @@ if (condition) | ||
/** | ||
* Writes a line of text. | ||
* @param str - String to write. | ||
*/ | ||
writeLine(str: string) { | ||
@@ -56,2 +111,5 @@ this.newLineIfNewLineOnNextWrite(); | ||
/** | ||
* Writes a newline if the last line was not a newline. | ||
*/ | ||
newLineIfLastNotNewLine() { | ||
@@ -66,2 +124,5 @@ this.newLineIfNewLineOnNextWrite(); | ||
/** | ||
* Writes a blank line. | ||
*/ | ||
blankLine() { | ||
@@ -71,2 +132,5 @@ return this.newLineIfLastNotNewLine().newLine(); | ||
/** | ||
* Indents the code one level for the current line. | ||
*/ | ||
indent() { | ||
@@ -77,2 +141,6 @@ this.newLineIfNewLineOnNextWrite(); | ||
/** | ||
* Writes a newline if the condition is true. | ||
* @param condition - Condition to evaluate. | ||
*/ | ||
conditionalNewLine(condition: boolean) { | ||
@@ -85,8 +153,14 @@ if (condition) | ||
/** | ||
* Writes a newline. | ||
*/ | ||
newLine() { | ||
this._newLineOnNextWrite = false; | ||
this.baseWrite(this._newLine); | ||
this.baseWriteNewline(); | ||
return this; | ||
} | ||
/** | ||
* Writes a space if the last character was not a space. | ||
*/ | ||
spaceIfLastNotSpace() { | ||
@@ -97,3 +171,3 @@ this.newLineIfNewLineOnNextWrite(); | ||
if (lastChar !== " ") | ||
this.baseWrite(" "); | ||
this.writeIndentingNewLines(" "); | ||
@@ -103,2 +177,7 @@ return this; | ||
/** | ||
* Writes the provided text if the condition is true. | ||
* @param condition - Condition to evaluate. | ||
* @param str - Text to write. | ||
*/ | ||
conditionalWrite(condition: boolean, str: string) { | ||
@@ -111,2 +190,6 @@ if (condition) | ||
/** | ||
* Writes the provided text. | ||
* @param str - Text to write. | ||
*/ | ||
write(str: string) { | ||
@@ -118,2 +201,5 @@ this.newLineIfNewLineOnNextWrite(); | ||
/** | ||
* Gets the length of the string in the writer. | ||
*/ | ||
getLength() { | ||
@@ -123,4 +209,21 @@ return this._text.length; | ||
/** | ||
* Gets if the writer is currently in a comment. | ||
*/ | ||
isInComment() { | ||
return this._currentCommentChar !== undefined; | ||
} | ||
/** | ||
* Gets if the writer is currently in a string. | ||
*/ | ||
isInString() { | ||
return this._stringCharStack.length > 0 && this._stringCharStack[this._stringCharStack.length - 1] !== "{"; | ||
} | ||
/** | ||
* Gets the writer's text. | ||
*/ | ||
toString() { | ||
return this.removeConsecutiveNewLineAtEndOfString(this._text); | ||
return this._text; | ||
} | ||
@@ -131,35 +234,49 @@ | ||
items.forEach((s, i) => { | ||
// don't use .newLine() here because we want to write out all the newlines the user requested | ||
if (i > 0) | ||
this.baseWrite(this._newLine); | ||
this.baseWriteNewline(); | ||
this.baseWrite(s); | ||
if (s.length === 0) | ||
return; | ||
if (i > 0 && i === items.length - 1 && s.length === 0) | ||
this.baseWrite(this._newLine); | ||
const isAtStartOfLine = this.isLastCharANewLine() || this._text.length === 0; | ||
if (isAtStartOfLine && !this.isInString()) | ||
this.writeIndentation(); | ||
this.updateStringStack(s); | ||
this._text += s; | ||
}); | ||
} | ||
private baseWrite(str: string) { | ||
this._isAtStartOfBlock = false; | ||
if (str == null || str.length === 0) | ||
return this; | ||
if (str !== this._newLine && this.isLastCharANewLine()) | ||
this.writeIndentation(); | ||
this._text += str; | ||
return this; | ||
private baseWriteNewline() { | ||
if (this._currentCommentChar === CommentChar.Line) | ||
this._currentCommentChar = undefined; | ||
this._text += this._newLine; | ||
} | ||
private removeConsecutiveNewLineAtEndOfString(text: string) { | ||
const consecutiveNewline = this._newLine + this._newLine; | ||
const lastIndexOfConsecutiveNewLines = text.lastIndexOf(consecutiveNewline); | ||
private updateStringStack(str: string) { | ||
for (let i = 0; i < str.length; i++) { | ||
const currentChar = str[i]; | ||
const pastChar = i === 0 ? this.getLastChar() : str[i - 1]; | ||
const lastCharOnStack = this._stringCharStack.length === 0 ? undefined : this._stringCharStack[this._stringCharStack.length - 1]; | ||
if (lastIndexOfConsecutiveNewLines >= 0 && lastIndexOfConsecutiveNewLines === text.length - consecutiveNewline.length) | ||
text = text.substr(0, text.length - this._newLine.length); | ||
if (this._currentCommentChar == null && pastChar === "/" && currentChar === "/") | ||
this._currentCommentChar = CommentChar.Line; | ||
else if (this._currentCommentChar == null && pastChar === "/" && currentChar === "*") | ||
this._currentCommentChar = CommentChar.Star; | ||
else if (this._currentCommentChar === CommentChar.Star && pastChar === "*" && currentChar === "/") | ||
this._currentCommentChar = undefined; | ||
return text; | ||
if (this.isInComment()) | ||
continue; | ||
else if (currentChar === "\"" || currentChar === "'" || currentChar === "`") { | ||
if (lastCharOnStack === currentChar) | ||
this._stringCharStack.pop(); | ||
else if (lastCharOnStack === "{" || lastCharOnStack === undefined) | ||
this._stringCharStack.push(currentChar); | ||
} | ||
else if (pastChar === "$" && currentChar === "{" && lastCharOnStack === "`") | ||
this._stringCharStack.push(currentChar); | ||
else if (currentChar === "}" && lastCharOnStack === "{") | ||
this._stringCharStack.pop(); | ||
} | ||
} | ||
@@ -172,8 +289,6 @@ | ||
private getLastChar() { | ||
let lastChar: string | null = null; | ||
if (this._text.length === 0) | ||
return undefined; | ||
if (this._text.length > 0) | ||
lastChar = this._text[this._text.length - 1]; | ||
return lastChar; | ||
return this._text[this._text.length - 1]; | ||
} | ||
@@ -198,1 +313,15 @@ | ||
} | ||
function getSpacesAndTabsCount(str: string) { | ||
let spacesCount = 0; | ||
let tabsCount = 0; | ||
for (const char of str) { | ||
if (char === "\t") | ||
tabsCount++; | ||
else if (char === " ") | ||
spacesCount++; | ||
} | ||
return {spacesCount, tabsCount}; | ||
} |
@@ -78,2 +78,11 @@ import * as assert from "assert"; | ||
}); | ||
it("should not write indentation between newlines", () => { | ||
const expected = " test\n\n test"; | ||
doTest(expected, writer => { | ||
writer.setIndentationLevel(1); | ||
writer.write("test\n\ntest"); | ||
}); | ||
}); | ||
}); | ||
@@ -169,2 +178,22 @@ | ||
}); | ||
it("should not indent when in a string", () => { | ||
const expected = "block {\n const t = `\nt`;\n const u = 1;\n}"; | ||
doTest(expected, writer => { | ||
writer.write("block").block(() => { | ||
writer.write("const t = `\nt`;\nconst u = 1;"); | ||
}); | ||
}); | ||
}); | ||
it("should indent when in a comment", () => { | ||
const expected = "block {\n const t = /*\n const u = 1;*/\n}"; | ||
doTest(expected, writer => { | ||
writer.write("block").block(() => { | ||
writer.write("const t = /*\nconst u = 1;*/"); | ||
}); | ||
}); | ||
}); | ||
}); | ||
@@ -315,4 +344,4 @@ | ||
it("should never have two newlines at the end of a file", () => { | ||
const expected = `text\n`; | ||
it("should be allowed to have two newlines at the end of a file", () => { | ||
const expected = `text\n\n`; | ||
@@ -423,2 +452,195 @@ doTest(expected, writer => { | ||
describe("#setIdentationLevel", () => { | ||
it("should throw when providing a negative number", () => { | ||
const writer = new CodeBlockWriter(); | ||
assert.throws(() => writer.setIndentationLevel(-1)); | ||
}); | ||
it("should throw when not providing a number or string", () => { | ||
const writer = new CodeBlockWriter(); | ||
assert.throws(() => writer.setIndentationLevel({} as any)); | ||
}); | ||
it("should not throw when providing an empty string", () => { | ||
const writer = new CodeBlockWriter(); | ||
assert.doesNotThrow(() => writer.setIndentationLevel("")); | ||
}); | ||
it("should throw when providing a string that doesn't contain only spaces and tabs", () => { | ||
const writer = new CodeBlockWriter(); | ||
assert.throws(() => writer.setIndentationLevel(" \ta")); | ||
}); | ||
it("should be able to set the indentation level and it maintains it over newlines", () => { | ||
const writer = new CodeBlockWriter(); | ||
writer.setIndentationLevel(2); | ||
writer.writeLine("t"); | ||
writer.writeLine("t"); | ||
assert.equal(writer.toString(), " t\n t\n"); | ||
}); | ||
it("should be able to set the indentation level to 0 within a block", () => { | ||
const writer = new CodeBlockWriter(); | ||
writer.write("t").block(() => { | ||
writer.setIndentationLevel(0); | ||
writer.writeLine("t"); | ||
writer.writeLine("t"); | ||
}).write("t").block(() => { | ||
writer.write("t"); | ||
}); | ||
assert.equal(writer.toString(), "t {\nt\nt\n}\nt {\n t\n}"); | ||
}); | ||
function doSpacesTest(numberSpaces: number) { | ||
const writer = new CodeBlockWriter({ indentNumberOfSpaces: numberSpaces }); | ||
const indent = Array(numberSpaces + 1).join(" "); | ||
writer.setIndentationLevel(indent + indent); | ||
writer.write("t").block(() => writer.write("t")); | ||
assert.equal(writer.toString(), `${indent + indent}t {\n${indent + indent + indent}t\n${indent + indent}}`); | ||
} | ||
it("should be able to set the indentation level using a string with two spaces", () => { | ||
doSpacesTest(2); | ||
}); | ||
it("should be able to set the indentation level using a string with four spaces", () => { | ||
doSpacesTest(4); | ||
}); | ||
it("should be able to set the indentation level using a string with eight spaces", () => { | ||
doSpacesTest(8); | ||
}); | ||
it("should indent by the provided number of tabs", () => { | ||
const writer = new CodeBlockWriter({ useTabs: true }); | ||
writer.setIndentationLevel("\t\t"); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), `\t\ts`); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round down)", () => { | ||
const writer = new CodeBlockWriter({ useTabs: true }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), `\t\ts`); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round down, 2 spaces)", () => { | ||
const writer = new CodeBlockWriter({ useTabs: true, indentNumberOfSpaces: 2 }); | ||
writer.setIndentationLevel("\t \t"); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), `\t\ts`); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (round up)", () => { | ||
const writer = new CodeBlockWriter({ useTabs: true }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), `\t\t\ts`); | ||
}); | ||
it("should indent to the nearest indent when mixing tabs and spaces (2 spaces)", () => { | ||
const writer = new CodeBlockWriter({ useTabs: true, indentNumberOfSpaces: 2 }); | ||
writer.setIndentationLevel("\t \t "); | ||
writer.write("s"); | ||
assert.equal(writer.toString(), `\t\t\t\t\ts`); | ||
}); | ||
}); | ||
describe("#isInString", () => { | ||
function doTest(str: string, expectedValues: boolean[]) { | ||
assert.equal(str.length + 1, expectedValues.length); | ||
const writer = new CodeBlockWriter(); | ||
assert.equal(writer.isInString(), expectedValues[0]); | ||
for (let i = 0; i < str.length; i++) { | ||
writer.write(str[i]); | ||
assert.equal(writer.isInString(), expectedValues[i + 1]); | ||
} | ||
} | ||
it("should be in a string while in double quotes", () => { | ||
doTest(`s"y"`, [false, false, true, true, false]); | ||
}); | ||
it("should be in a string while in single quotes", () => { | ||
doTest(`s'y'`, [false, false, true, true, false]); | ||
}); | ||
it("should be in a string while in backticks", () => { | ||
doTest("s`y`", [false, false, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in double quotes", () => { | ||
doTest(`"'\`\${}"`, [false, true, true, true, true, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in single quotes", () => { | ||
doTest(`'"\`\${}'`, [false, true, true, true, true, true, true, false]); | ||
}); | ||
it("should not be affected by other string quotes while in back ticks", () => { | ||
doTest(`\`'"\``, [false, true, true, true, false]); | ||
}); | ||
it("should not be in a string while in backticks within braces", () => { | ||
doTest("`y${t}`", [false, true, true, true, false, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within a single quote string", () => { | ||
doTest("`${'t'}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within a double quote string", () => { | ||
doTest("`${\"t\"}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should be in a string while in backticks within braces within back ticks", () => { | ||
doTest("`${`t`}`", [false, true, true, false, true, true, false, true, false]); | ||
}); | ||
it("should not be in a string while in backticks within braces within back ticks within braces", () => { | ||
doTest("`${`${t}`}`", [false, true, true, false, true, true, false, false, true, false, true, false]); | ||
}); | ||
it("should not be in a string while comments", () => { | ||
doTest("//'t'", [false, false, false, false, false, false]); | ||
}); | ||
it("should be in a string while the previous line was a comment and now this is a string", () => { | ||
doTest("//t\n't'", [false, false, false, false, false, true, true, false]); | ||
}); | ||
it("should not be in a string for star comments", () => { | ||
doTest("/*\n't'\n*/'t'", [false, false, false, false, false, false, false, false, false, false, true, true, false]); | ||
}); | ||
}); | ||
describe("#isInComment", () => { | ||
function doTest(str: string, expectedValues: boolean[]) { | ||
assert.equal(str.length + 1, expectedValues.length); | ||
const writer = new CodeBlockWriter(); | ||
assert.equal(writer.isInComment(), expectedValues[0]); | ||
for (let i = 0; i < str.length; i++) { | ||
writer.write(str[i]); | ||
assert.equal(writer.isInComment(), expectedValues[i + 1]); | ||
} | ||
} | ||
it("should be in a comment for star comments", () => { | ||
doTest("/*\nt\n*/", [false, false, true, true, true, true, true, false]); | ||
}); | ||
it("should be in a comment for line comments", () => { | ||
doTest("// t\nt", [false, false, true, true, true, false, false]); | ||
}); | ||
}); | ||
describe("indentNumberOfSpaces", () => { | ||
@@ -425,0 +647,0 @@ const writer = new CodeBlockWriter({ indentNumberOfSpaces: 2 }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
170565
22
3866
68