1 | import { CommentChar } from "./comment_char.js";
|
2 | import { escapeForWithinString, getStringFromStrOrFunc } from "./utils/string_utils.js";
|
3 |
|
4 | const CHARS = {
|
5 | BACK_SLASH: "\\".charCodeAt(0),
|
6 | FORWARD_SLASH: "/".charCodeAt(0),
|
7 | NEW_LINE: "\n".charCodeAt(0),
|
8 | CARRIAGE_RETURN: "\r".charCodeAt(0),
|
9 | ASTERISK: "*".charCodeAt(0),
|
10 | DOUBLE_QUOTE: "\"".charCodeAt(0),
|
11 | SINGLE_QUOTE: "'".charCodeAt(0),
|
12 | BACK_TICK: "`".charCodeAt(0),
|
13 | OPEN_BRACE: "{".charCodeAt(0),
|
14 | CLOSE_BRACE: "}".charCodeAt(0),
|
15 | DOLLAR_SIGN: "$".charCodeAt(0),
|
16 | SPACE: " ".charCodeAt(0),
|
17 | TAB: "\t".charCodeAt(0),
|
18 | };
|
19 | const isCharToHandle = new Set([
|
20 | CHARS.BACK_SLASH,
|
21 | CHARS.FORWARD_SLASH,
|
22 | CHARS.NEW_LINE,
|
23 | CHARS.CARRIAGE_RETURN,
|
24 | CHARS.ASTERISK,
|
25 | CHARS.DOUBLE_QUOTE,
|
26 | CHARS.SINGLE_QUOTE,
|
27 | CHARS.BACK_TICK,
|
28 | CHARS.OPEN_BRACE,
|
29 | CHARS.CLOSE_BRACE,
|
30 | ]);
|
31 |
|
32 |
|
33 |
|
34 | export default class CodeBlockWriter {
|
35 | |
36 |
|
37 |
|
38 |
|
39 | constructor(opts = {}) {
|
40 |
|
41 | Object.defineProperty(this, "_indentationText", {
|
42 | enumerable: true,
|
43 | configurable: true,
|
44 | writable: true,
|
45 | value: void 0
|
46 | });
|
47 |
|
48 | Object.defineProperty(this, "_newLine", {
|
49 | enumerable: true,
|
50 | configurable: true,
|
51 | writable: true,
|
52 | value: void 0
|
53 | });
|
54 |
|
55 | Object.defineProperty(this, "_useTabs", {
|
56 | enumerable: true,
|
57 | configurable: true,
|
58 | writable: true,
|
59 | value: void 0
|
60 | });
|
61 |
|
62 | Object.defineProperty(this, "_quoteChar", {
|
63 | enumerable: true,
|
64 | configurable: true,
|
65 | writable: true,
|
66 | value: void 0
|
67 | });
|
68 |
|
69 | Object.defineProperty(this, "_indentNumberOfSpaces", {
|
70 | enumerable: true,
|
71 | configurable: true,
|
72 | writable: true,
|
73 | value: void 0
|
74 | });
|
75 |
|
76 | Object.defineProperty(this, "_currentIndentation", {
|
77 | enumerable: true,
|
78 | configurable: true,
|
79 | writable: true,
|
80 | value: 0
|
81 | });
|
82 |
|
83 | Object.defineProperty(this, "_queuedIndentation", {
|
84 | enumerable: true,
|
85 | configurable: true,
|
86 | writable: true,
|
87 | value: void 0
|
88 | });
|
89 |
|
90 | Object.defineProperty(this, "_queuedOnlyIfNotBlock", {
|
91 | enumerable: true,
|
92 | configurable: true,
|
93 | writable: true,
|
94 | value: void 0
|
95 | });
|
96 |
|
97 | Object.defineProperty(this, "_length", {
|
98 | enumerable: true,
|
99 | configurable: true,
|
100 | writable: true,
|
101 | value: 0
|
102 | });
|
103 |
|
104 | Object.defineProperty(this, "_newLineOnNextWrite", {
|
105 | enumerable: true,
|
106 | configurable: true,
|
107 | writable: true,
|
108 | value: false
|
109 | });
|
110 |
|
111 | Object.defineProperty(this, "_currentCommentChar", {
|
112 | enumerable: true,
|
113 | configurable: true,
|
114 | writable: true,
|
115 | value: undefined
|
116 | });
|
117 |
|
118 | Object.defineProperty(this, "_stringCharStack", {
|
119 | enumerable: true,
|
120 | configurable: true,
|
121 | writable: true,
|
122 | value: []
|
123 | });
|
124 |
|
125 | Object.defineProperty(this, "_isInRegEx", {
|
126 | enumerable: true,
|
127 | configurable: true,
|
128 | writable: true,
|
129 | value: false
|
130 | });
|
131 |
|
132 | Object.defineProperty(this, "_isOnFirstLineOfBlock", {
|
133 | enumerable: true,
|
134 | configurable: true,
|
135 | writable: true,
|
136 | value: true
|
137 | });
|
138 |
|
139 |
|
140 |
|
141 | Object.defineProperty(this, "_texts", {
|
142 | enumerable: true,
|
143 | configurable: true,
|
144 | writable: true,
|
145 | value: []
|
146 | });
|
147 | this._newLine = opts.newLine || "\n";
|
148 | this._useTabs = opts.useTabs || false;
|
149 | this._indentNumberOfSpaces = opts.indentNumberOfSpaces || 4;
|
150 | this._indentationText = getIndentationText(this._useTabs, this._indentNumberOfSpaces);
|
151 | this._quoteChar = opts.useSingleQuote ? "'" : `"`;
|
152 | }
|
153 | |
154 |
|
155 |
|
156 | getOptions() {
|
157 | return {
|
158 | indentNumberOfSpaces: this._indentNumberOfSpaces,
|
159 | newLine: this._newLine,
|
160 | useTabs: this._useTabs,
|
161 | useSingleQuote: this._quoteChar === "'",
|
162 | };
|
163 | }
|
164 | queueIndentationLevel(countOrText) {
|
165 | this._queuedIndentation = this._getIndentationLevelFromArg(countOrText);
|
166 | this._queuedOnlyIfNotBlock = undefined;
|
167 | return this;
|
168 | }
|
169 | |
170 |
|
171 |
|
172 |
|
173 | hangingIndent(action) {
|
174 | return this._withResetIndentation(() => this.queueIndentationLevel(this.getIndentationLevel() + 1), action);
|
175 | }
|
176 | |
177 |
|
178 |
|
179 |
|
180 | hangingIndentUnlessBlock(action) {
|
181 | return this._withResetIndentation(() => {
|
182 | this.queueIndentationLevel(this.getIndentationLevel() + 1);
|
183 | this._queuedOnlyIfNotBlock = true;
|
184 | }, action);
|
185 | }
|
186 | setIndentationLevel(countOrText) {
|
187 | this._currentIndentation = this._getIndentationLevelFromArg(countOrText);
|
188 | return this;
|
189 | }
|
190 | withIndentationLevel(countOrText, action) {
|
191 | return this._withResetIndentation(() => this.setIndentationLevel(countOrText), action);
|
192 | }
|
193 |
|
194 | _withResetIndentation(setStateAction, writeAction) {
|
195 | const previousState = this._getIndentationState();
|
196 | setStateAction();
|
197 | try {
|
198 | writeAction();
|
199 | }
|
200 | finally {
|
201 | this._setIndentationState(previousState);
|
202 | }
|
203 | return this;
|
204 | }
|
205 | |
206 |
|
207 |
|
208 | getIndentationLevel() {
|
209 | return this._currentIndentation;
|
210 | }
|
211 | |
212 |
|
213 |
|
214 |
|
215 | block(block) {
|
216 | this._newLineIfNewLineOnNextWrite();
|
217 | if (this.getLength() > 0 && !this.isLastNewLine()) {
|
218 | this.spaceIfLastNot();
|
219 | }
|
220 | this.inlineBlock(block);
|
221 | this._newLineOnNextWrite = true;
|
222 | return this;
|
223 | }
|
224 | |
225 |
|
226 |
|
227 |
|
228 | inlineBlock(block) {
|
229 | this._newLineIfNewLineOnNextWrite();
|
230 | this.write("{");
|
231 | this._indentBlockInternal(block);
|
232 | this.newLineIfLastNot().write("}");
|
233 | return this;
|
234 | }
|
235 | indent(timesOrBlock = 1) {
|
236 | if (typeof timesOrBlock === "number") {
|
237 | this._newLineIfNewLineOnNextWrite();
|
238 | return this.write(this._indentationText.repeat(timesOrBlock));
|
239 | }
|
240 | else {
|
241 | this._indentBlockInternal(timesOrBlock);
|
242 | if (!this.isLastNewLine()) {
|
243 | this._newLineOnNextWrite = true;
|
244 | }
|
245 | return this;
|
246 | }
|
247 | }
|
248 |
|
249 | _indentBlockInternal(block) {
|
250 | if (this.getLastChar() != null) {
|
251 | this.newLineIfLastNot();
|
252 | }
|
253 | this._currentIndentation++;
|
254 | this._isOnFirstLineOfBlock = true;
|
255 | if (block != null) {
|
256 | block();
|
257 | }
|
258 | this._isOnFirstLineOfBlock = false;
|
259 | this._currentIndentation = Math.max(0, this._currentIndentation - 1);
|
260 | }
|
261 | conditionalWriteLine(condition, strOrFunc) {
|
262 | if (condition) {
|
263 | this.writeLine(getStringFromStrOrFunc(strOrFunc));
|
264 | }
|
265 | return this;
|
266 | }
|
267 | |
268 |
|
269 |
|
270 |
|
271 | writeLine(text) {
|
272 | this._newLineIfNewLineOnNextWrite();
|
273 | if (this.getLastChar() != null) {
|
274 | this.newLineIfLastNot();
|
275 | }
|
276 | this._writeIndentingNewLines(text);
|
277 | this.newLine();
|
278 | return this;
|
279 | }
|
280 | |
281 |
|
282 |
|
283 | newLineIfLastNot() {
|
284 | this._newLineIfNewLineOnNextWrite();
|
285 | if (!this.isLastNewLine()) {
|
286 | this.newLine();
|
287 | }
|
288 | return this;
|
289 | }
|
290 | |
291 |
|
292 |
|
293 | blankLineIfLastNot() {
|
294 | if (!this.isLastBlankLine()) {
|
295 | this.blankLine();
|
296 | }
|
297 | return this;
|
298 | }
|
299 | |
300 |
|
301 |
|
302 |
|
303 | conditionalBlankLine(condition) {
|
304 | if (condition) {
|
305 | this.blankLine();
|
306 | }
|
307 | return this;
|
308 | }
|
309 | |
310 |
|
311 |
|
312 | blankLine() {
|
313 | return this.newLineIfLastNot().newLine();
|
314 | }
|
315 | |
316 |
|
317 |
|
318 |
|
319 | conditionalNewLine(condition) {
|
320 | if (condition) {
|
321 | this.newLine();
|
322 | }
|
323 | return this;
|
324 | }
|
325 | |
326 |
|
327 |
|
328 | newLine() {
|
329 | this._newLineOnNextWrite = false;
|
330 | this._baseWriteNewline();
|
331 | return this;
|
332 | }
|
333 | quote(text) {
|
334 | this._newLineIfNewLineOnNextWrite();
|
335 | this._writeIndentingNewLines(text == null ? this._quoteChar : this._quoteChar + escapeForWithinString(text, this._quoteChar) + this._quoteChar);
|
336 | return this;
|
337 | }
|
338 | |
339 |
|
340 |
|
341 | spaceIfLastNot() {
|
342 | this._newLineIfNewLineOnNextWrite();
|
343 | if (!this.isLastSpace()) {
|
344 | this._writeIndentingNewLines(" ");
|
345 | }
|
346 | return this;
|
347 | }
|
348 | |
349 |
|
350 |
|
351 |
|
352 | space(times = 1) {
|
353 | this._newLineIfNewLineOnNextWrite();
|
354 | this._writeIndentingNewLines(" ".repeat(times));
|
355 | return this;
|
356 | }
|
357 | |
358 |
|
359 |
|
360 | tabIfLastNot() {
|
361 | this._newLineIfNewLineOnNextWrite();
|
362 | if (!this.isLastTab()) {
|
363 | this._writeIndentingNewLines("\t");
|
364 | }
|
365 | return this;
|
366 | }
|
367 | |
368 |
|
369 |
|
370 |
|
371 | tab(times = 1) {
|
372 | this._newLineIfNewLineOnNextWrite();
|
373 | this._writeIndentingNewLines("\t".repeat(times));
|
374 | return this;
|
375 | }
|
376 | conditionalWrite(condition, textOrFunc) {
|
377 | if (condition) {
|
378 | this.write(getStringFromStrOrFunc(textOrFunc));
|
379 | }
|
380 | return this;
|
381 | }
|
382 | |
383 |
|
384 |
|
385 |
|
386 | write(text) {
|
387 | this._newLineIfNewLineOnNextWrite();
|
388 | this._writeIndentingNewLines(text);
|
389 | return this;
|
390 | }
|
391 | |
392 |
|
393 |
|
394 | closeComment() {
|
395 | const commentChar = this._currentCommentChar;
|
396 | switch (commentChar) {
|
397 | case CommentChar.Line:
|
398 | this.newLine();
|
399 | break;
|
400 | case CommentChar.Star:
|
401 | if (!this.isLastNewLine()) {
|
402 | this.spaceIfLastNot();
|
403 | }
|
404 | this.write("*/");
|
405 | break;
|
406 | default: {
|
407 | const _assertUndefined = commentChar;
|
408 | break;
|
409 | }
|
410 | }
|
411 | return this;
|
412 | }
|
413 | |
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 |
|
420 |
|
421 |
|
422 |
|
423 | unsafeInsert(pos, text) {
|
424 | const textLength = this._length;
|
425 | const texts = this._texts;
|
426 | verifyInput();
|
427 | if (pos === textLength) {
|
428 | return this.write(text);
|
429 | }
|
430 | updateInternalArray();
|
431 | this._length += text.length;
|
432 | return this;
|
433 | function verifyInput() {
|
434 | if (pos < 0) {
|
435 | throw new Error(`Provided position of '${pos}' was less than zero.`);
|
436 | }
|
437 | if (pos > textLength) {
|
438 | throw new Error(`Provided position of '${pos}' was greater than the text length of '${textLength}'.`);
|
439 | }
|
440 | }
|
441 | function updateInternalArray() {
|
442 | const { index, localIndex } = getArrayIndexAndLocalIndex();
|
443 | if (localIndex === 0) {
|
444 | texts.splice(index, 0, text);
|
445 | }
|
446 | else if (localIndex === texts[index].length) {
|
447 | texts.splice(index + 1, 0, text);
|
448 | }
|
449 | else {
|
450 | const textItem = texts[index];
|
451 | const startText = textItem.substring(0, localIndex);
|
452 | const endText = textItem.substring(localIndex);
|
453 | texts.splice(index, 1, startText, text, endText);
|
454 | }
|
455 | }
|
456 | function getArrayIndexAndLocalIndex() {
|
457 | if (pos < textLength / 2) {
|
458 |
|
459 | let endPos = 0;
|
460 | for (let i = 0; i < texts.length; i++) {
|
461 | const textItem = texts[i];
|
462 | const startPos = endPos;
|
463 | endPos += textItem.length;
|
464 | if (endPos >= pos) {
|
465 | return { index: i, localIndex: pos - startPos };
|
466 | }
|
467 | }
|
468 | }
|
469 | else {
|
470 |
|
471 | let startPos = textLength;
|
472 | for (let i = texts.length - 1; i >= 0; i--) {
|
473 | const textItem = texts[i];
|
474 | startPos -= textItem.length;
|
475 | if (startPos <= pos) {
|
476 | return { index: i, localIndex: pos - startPos };
|
477 | }
|
478 | }
|
479 | }
|
480 | throw new Error("Unhandled situation inserting. This should never happen.");
|
481 | }
|
482 | }
|
483 | |
484 |
|
485 |
|
486 | getLength() {
|
487 | return this._length;
|
488 | }
|
489 | |
490 |
|
491 |
|
492 | isInComment() {
|
493 | return this._currentCommentChar !== undefined;
|
494 | }
|
495 | |
496 |
|
497 |
|
498 | isAtStartOfFirstLineOfBlock() {
|
499 | return this.isOnFirstLineOfBlock() && (this.isLastNewLine() || this.getLastChar() == null);
|
500 | }
|
501 | |
502 |
|
503 |
|
504 | isOnFirstLineOfBlock() {
|
505 | return this._isOnFirstLineOfBlock;
|
506 | }
|
507 | |
508 |
|
509 |
|
510 | isInString() {
|
511 | return this._stringCharStack.length > 0 && this._stringCharStack[this._stringCharStack.length - 1] !== CHARS.OPEN_BRACE;
|
512 | }
|
513 | |
514 |
|
515 |
|
516 | isLastNewLine() {
|
517 | const lastChar = this.getLastChar();
|
518 | return lastChar === "\n" || lastChar === "\r";
|
519 | }
|
520 | |
521 |
|
522 |
|
523 | isLastBlankLine() {
|
524 | let foundCount = 0;
|
525 |
|
526 |
|
527 | for (let i = this._texts.length - 1; i >= 0; i--) {
|
528 | const currentText = this._texts[i];
|
529 | for (let j = currentText.length - 1; j >= 0; j--) {
|
530 | const currentChar = currentText.charCodeAt(j);
|
531 | if (currentChar === CHARS.NEW_LINE) {
|
532 | foundCount++;
|
533 | if (foundCount === 2) {
|
534 | return true;
|
535 | }
|
536 | }
|
537 | else if (currentChar !== CHARS.CARRIAGE_RETURN) {
|
538 | return false;
|
539 | }
|
540 | }
|
541 | }
|
542 | return false;
|
543 | }
|
544 | |
545 |
|
546 |
|
547 | isLastSpace() {
|
548 | return this.getLastChar() === " ";
|
549 | }
|
550 | |
551 |
|
552 |
|
553 | isLastTab() {
|
554 | return this.getLastChar() === "\t";
|
555 | }
|
556 | |
557 |
|
558 |
|
559 | getLastChar() {
|
560 | const charCode = this._getLastCharCodeWithOffset(0);
|
561 | return charCode == null ? undefined : String.fromCharCode(charCode);
|
562 | }
|
563 | |
564 |
|
565 |
|
566 |
|
567 | endsWith(text) {
|
568 | const length = this._length;
|
569 | return this.iterateLastCharCodes((charCode, index) => {
|
570 | const offset = length - index;
|
571 | const textIndex = text.length - offset;
|
572 | if (text.charCodeAt(textIndex) !== charCode) {
|
573 | return false;
|
574 | }
|
575 | return textIndex === 0 ? true : undefined;
|
576 | }) || false;
|
577 | }
|
578 | |
579 |
|
580 |
|
581 |
|
582 |
|
583 |
|
584 |
|
585 | iterateLastChars(action) {
|
586 | return this.iterateLastCharCodes((charCode, index) => action(String.fromCharCode(charCode), index));
|
587 | }
|
588 | |
589 |
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 | iterateLastCharCodes(action) {
|
597 | let index = this._length;
|
598 | for (let i = this._texts.length - 1; i >= 0; i--) {
|
599 | const currentText = this._texts[i];
|
600 | for (let j = currentText.length - 1; j >= 0; j--) {
|
601 | index--;
|
602 | const result = action(currentText.charCodeAt(j), index);
|
603 | if (result != null) {
|
604 | return result;
|
605 | }
|
606 | }
|
607 | }
|
608 | return undefined;
|
609 | }
|
610 | |
611 |
|
612 |
|
613 | toString() {
|
614 | if (this._texts.length > 1) {
|
615 | const text = this._texts.join("");
|
616 | this._texts.length = 0;
|
617 | this._texts.push(text);
|
618 | }
|
619 | return this._texts[0] || "";
|
620 | }
|
621 |
|
622 | _writeIndentingNewLines(text) {
|
623 | text = text || "";
|
624 | if (text.length === 0) {
|
625 | writeIndividual(this, "");
|
626 | return;
|
627 | }
|
628 | const items = text.split(CodeBlockWriter._newLineRegEx);
|
629 | items.forEach((s, i) => {
|
630 | if (i > 0) {
|
631 | this._baseWriteNewline();
|
632 | }
|
633 | if (s.length === 0) {
|
634 | return;
|
635 | }
|
636 | writeIndividual(this, s);
|
637 | });
|
638 | function writeIndividual(writer, s) {
|
639 | if (!writer.isInString()) {
|
640 | const isAtStartOfLine = writer.isLastNewLine() || writer.getLastChar() == null;
|
641 | if (isAtStartOfLine) {
|
642 | writer._writeIndentation();
|
643 | }
|
644 | }
|
645 | writer._updateInternalState(s);
|
646 | writer._internalWrite(s);
|
647 | }
|
648 | }
|
649 |
|
650 | _baseWriteNewline() {
|
651 | if (this._currentCommentChar === CommentChar.Line) {
|
652 | this._currentCommentChar = undefined;
|
653 | }
|
654 | const lastStringCharOnStack = this._stringCharStack[this._stringCharStack.length - 1];
|
655 | if ((lastStringCharOnStack === CHARS.DOUBLE_QUOTE || lastStringCharOnStack === CHARS.SINGLE_QUOTE) && this._getLastCharCodeWithOffset(0) !== CHARS.BACK_SLASH) {
|
656 | this._stringCharStack.pop();
|
657 | }
|
658 | this._internalWrite(this._newLine);
|
659 | this._isOnFirstLineOfBlock = false;
|
660 | this._dequeueQueuedIndentation();
|
661 | }
|
662 |
|
663 | _dequeueQueuedIndentation() {
|
664 | if (this._queuedIndentation == null) {
|
665 | return;
|
666 | }
|
667 | if (this._queuedOnlyIfNotBlock && wasLastBlock(this)) {
|
668 | this._queuedIndentation = undefined;
|
669 | this._queuedOnlyIfNotBlock = undefined;
|
670 | }
|
671 | else {
|
672 | this._currentIndentation = this._queuedIndentation;
|
673 | this._queuedIndentation = undefined;
|
674 | }
|
675 | function wasLastBlock(writer) {
|
676 | let foundNewLine = false;
|
677 | return writer.iterateLastCharCodes(charCode => {
|
678 | switch (charCode) {
|
679 | case CHARS.NEW_LINE:
|
680 | if (foundNewLine) {
|
681 | return false;
|
682 | }
|
683 | else {
|
684 | foundNewLine = true;
|
685 | }
|
686 | break;
|
687 | case CHARS.CARRIAGE_RETURN:
|
688 | return undefined;
|
689 | case CHARS.OPEN_BRACE:
|
690 | return true;
|
691 | default:
|
692 | return false;
|
693 | }
|
694 | });
|
695 | }
|
696 | }
|
697 |
|
698 | _updateInternalState(str) {
|
699 | for (let i = 0; i < str.length; i++) {
|
700 | const currentChar = str.charCodeAt(i);
|
701 |
|
702 |
|
703 |
|
704 | if (!isCharToHandle.has(currentChar)) {
|
705 | continue;
|
706 | }
|
707 | const pastChar = i === 0 ? this._getLastCharCodeWithOffset(0) : str.charCodeAt(i - 1);
|
708 | const pastPastChar = i === 0 ? this._getLastCharCodeWithOffset(1) : i === 1 ? this._getLastCharCodeWithOffset(0) : str.charCodeAt(i - 2);
|
709 |
|
710 | if (this._isInRegEx) {
|
711 | if (pastChar === CHARS.FORWARD_SLASH && pastPastChar !== CHARS.BACK_SLASH || pastChar === CHARS.NEW_LINE) {
|
712 | this._isInRegEx = false;
|
713 | }
|
714 | else {
|
715 | continue;
|
716 | }
|
717 | }
|
718 | else if (!this.isInString() && !this.isInComment() && isRegExStart(currentChar, pastChar, pastPastChar)) {
|
719 | this._isInRegEx = true;
|
720 | continue;
|
721 | }
|
722 |
|
723 | if (this._currentCommentChar == null && pastChar === CHARS.FORWARD_SLASH && currentChar === CHARS.FORWARD_SLASH) {
|
724 | this._currentCommentChar = CommentChar.Line;
|
725 | }
|
726 | else if (this._currentCommentChar == null && pastChar === CHARS.FORWARD_SLASH && currentChar === CHARS.ASTERISK) {
|
727 | this._currentCommentChar = CommentChar.Star;
|
728 | }
|
729 | else if (this._currentCommentChar === CommentChar.Star && pastChar === CHARS.ASTERISK && currentChar === CHARS.FORWARD_SLASH) {
|
730 | this._currentCommentChar = undefined;
|
731 | }
|
732 | if (this.isInComment()) {
|
733 | continue;
|
734 | }
|
735 |
|
736 | const lastStringCharOnStack = this._stringCharStack.length === 0 ? undefined : this._stringCharStack[this._stringCharStack.length - 1];
|
737 | if (pastChar !== CHARS.BACK_SLASH && (currentChar === CHARS.DOUBLE_QUOTE || currentChar === CHARS.SINGLE_QUOTE || currentChar === CHARS.BACK_TICK)) {
|
738 | if (lastStringCharOnStack === currentChar) {
|
739 | this._stringCharStack.pop();
|
740 | }
|
741 | else if (lastStringCharOnStack === CHARS.OPEN_BRACE || lastStringCharOnStack === undefined) {
|
742 | this._stringCharStack.push(currentChar);
|
743 | }
|
744 | }
|
745 | else if (pastPastChar !== CHARS.BACK_SLASH && pastChar === CHARS.DOLLAR_SIGN && currentChar === CHARS.OPEN_BRACE && lastStringCharOnStack === CHARS.BACK_TICK) {
|
746 | this._stringCharStack.push(currentChar);
|
747 | }
|
748 | else if (currentChar === CHARS.CLOSE_BRACE && lastStringCharOnStack === CHARS.OPEN_BRACE) {
|
749 | this._stringCharStack.pop();
|
750 | }
|
751 | }
|
752 | }
|
753 |
|
754 | _getLastCharCodeWithOffset(offset) {
|
755 | if (offset >= this._length || offset < 0) {
|
756 | return undefined;
|
757 | }
|
758 | for (let i = this._texts.length - 1; i >= 0; i--) {
|
759 | const currentText = this._texts[i];
|
760 | if (offset >= currentText.length) {
|
761 | offset -= currentText.length;
|
762 | }
|
763 | else {
|
764 | return currentText.charCodeAt(currentText.length - 1 - offset);
|
765 | }
|
766 | }
|
767 | return undefined;
|
768 | }
|
769 |
|
770 | _writeIndentation() {
|
771 | const flooredIndentation = Math.floor(this._currentIndentation);
|
772 | this._internalWrite(this._indentationText.repeat(flooredIndentation));
|
773 | const overflow = this._currentIndentation - flooredIndentation;
|
774 | if (this._useTabs) {
|
775 | if (overflow > 0.5) {
|
776 | this._internalWrite(this._indentationText);
|
777 | }
|
778 | }
|
779 | else {
|
780 | const portion = Math.round(this._indentationText.length * overflow);
|
781 |
|
782 | let text = "";
|
783 | for (let i = 0; i < portion; i++) {
|
784 | text += this._indentationText[i];
|
785 | }
|
786 | this._internalWrite(text);
|
787 | }
|
788 | }
|
789 |
|
790 | _newLineIfNewLineOnNextWrite() {
|
791 | if (!this._newLineOnNextWrite) {
|
792 | return;
|
793 | }
|
794 | this._newLineOnNextWrite = false;
|
795 | this.newLine();
|
796 | }
|
797 |
|
798 | _internalWrite(text) {
|
799 | if (text.length === 0) {
|
800 | return;
|
801 | }
|
802 | this._texts.push(text);
|
803 | this._length += text.length;
|
804 | }
|
805 |
|
806 | _getIndentationLevelFromArg(countOrText) {
|
807 | if (typeof countOrText === "number") {
|
808 | if (countOrText < 0) {
|
809 | throw new Error("Passed in indentation level should be greater than or equal to 0.");
|
810 | }
|
811 | return countOrText;
|
812 | }
|
813 | else if (typeof countOrText === "string") {
|
814 | if (!CodeBlockWriter._spacesOrTabsRegEx.test(countOrText)) {
|
815 | throw new Error("Provided string must be empty or only contain spaces or tabs.");
|
816 | }
|
817 | const { spacesCount, tabsCount } = getSpacesAndTabsCount(countOrText);
|
818 | return tabsCount + spacesCount / this._indentNumberOfSpaces;
|
819 | }
|
820 | else {
|
821 | throw new Error("Argument provided must be a string or number.");
|
822 | }
|
823 | }
|
824 |
|
825 | _setIndentationState(state) {
|
826 | this._currentIndentation = state.current;
|
827 | this._queuedIndentation = state.queued;
|
828 | this._queuedOnlyIfNotBlock = state.queuedOnlyIfNotBlock;
|
829 | }
|
830 |
|
831 | _getIndentationState() {
|
832 | return {
|
833 | current: this._currentIndentation,
|
834 | queued: this._queuedIndentation,
|
835 | queuedOnlyIfNotBlock: this._queuedOnlyIfNotBlock,
|
836 | };
|
837 | }
|
838 | }
|
839 |
|
840 | Object.defineProperty(CodeBlockWriter, "_newLineRegEx", {
|
841 | enumerable: true,
|
842 | configurable: true,
|
843 | writable: true,
|
844 | value: /\r?\n/
|
845 | });
|
846 |
|
847 | Object.defineProperty(CodeBlockWriter, "_spacesOrTabsRegEx", {
|
848 | enumerable: true,
|
849 | configurable: true,
|
850 | writable: true,
|
851 | value: /^[ \t]*$/
|
852 | });
|
853 | function isRegExStart(currentChar, pastChar, pastPastChar) {
|
854 | return pastChar === CHARS.FORWARD_SLASH
|
855 | && currentChar !== CHARS.FORWARD_SLASH
|
856 | && currentChar !== CHARS.ASTERISK
|
857 | && pastPastChar !== CHARS.ASTERISK
|
858 | && pastPastChar !== CHARS.FORWARD_SLASH;
|
859 | }
|
860 | function getIndentationText(useTabs, numberSpaces) {
|
861 | if (useTabs) {
|
862 | return "\t";
|
863 | }
|
864 | return Array(numberSpaces + 1).join(" ");
|
865 | }
|
866 | function getSpacesAndTabsCount(str) {
|
867 | let spacesCount = 0;
|
868 | let tabsCount = 0;
|
869 | for (let i = 0; i < str.length; i++) {
|
870 | const charCode = str.charCodeAt(i);
|
871 | if (charCode === CHARS.SPACE) {
|
872 | spacesCount++;
|
873 | }
|
874 | else if (charCode === CHARS.TAB) {
|
875 | tabsCount++;
|
876 | }
|
877 | }
|
878 | return { spacesCount, tabsCount };
|
879 | }
|