diff --git a/src/printer.ts b/src/printer.ts index e5e210be..90d68938 100644 --- a/src/printer.ts +++ b/src/printer.ts @@ -393,6 +393,19 @@ export class PugPrinter { return results.join(''); } + /** + * Visual width of the indentation for the given level. + * + * A tab is counted as `pugTabWidth` columns, so that line length calculations + * are the same regardless of `pugUseTabs`. + * + * @param indentLevel The indentation level. + * @returns The visual width in columns. + */ + private indentWidth(indentLevel: number): number { + return indentLevel * this.options.pugTabWidth; + } + private getNextToken(): Token | null { this.currentIndex++; return this.tokens[this.currentIndex] ?? null; @@ -1309,7 +1322,7 @@ export class PugPrinter { private indent(token: IndentToken): string { const result: string = `\n${this.indentString.repeat(this.indentLevel)}`; this.indentLevel++; - this.currentLineLength = result.length - 1 + 1 + this.options.pugTabWidth; // -1 for \n, +1 for non zero based + this.currentLineLength = 1 + this.indentWidth(this.indentLevel); // +1 for non zero based logger.debug( 'indent', { @@ -1334,8 +1347,7 @@ export class PugPrinter { } this.indentLevel--; - this.currentLineLength = - 1 + this.indentString.repeat(this.indentLevel).length; // -1 for \n, +1 for non zero based + this.currentLineLength = 1 + this.indentWidth(this.indentLevel); // +1 for non zero based logger.debug( 'outdent', { result, indentLevel: this.indentLevel }, @@ -1639,8 +1651,7 @@ export class PugPrinter { } result += '\n'; - this.currentLineLength = - 1 + this.indentString.repeat(this.indentLevel).length; // -1 for \n, +1 for non zero based + this.currentLineLength = 1 + this.indentWidth(this.indentLevel); // +1 for non zero based logger.debug( 'newline', { result, indentLevel: this.indentLevel }, @@ -2034,6 +2045,10 @@ export class PugPrinter { result = await format(rawText, { parser, ...this.codeInterpolationOptions, + // The content will be indented one level deeper than the tag + printWidth: + this.options.pugPrintWidth - + this.indentWidth(this.indentLevel + 1), }); } catch (error: unknown) { if (!usedInterpolatedCode) { diff --git a/tests/issues/issue-268/formatted-nested.pug b/tests/issues/issue-268/formatted-nested.pug new file mode 100644 index 00000000..ca0a1a59 --- /dev/null +++ b/tests/issues/issue-268/formatted-nested.pug @@ -0,0 +1,13 @@ +p + input#some-very-long-id-for-example-sake( + type="input", + placeeholder="example" + ) + div + input#name( + type="text", + name="name", + placeholder="Name *", + required + ) + input#name(type="text", name="name", placeholder="Name *", required) diff --git a/tests/issues/issue-268/formatted-script-spaces.pug b/tests/issues/issue-268/formatted-script-spaces.pug new file mode 100644 index 00000000..27e2595d --- /dev/null +++ b/tests/issues/issue-268/formatted-script-spaces.pug @@ -0,0 +1,22 @@ +html + head + script. + const config = { + apiEndpoint: "https://example.com/api/v1", + timeout: 5000, + retries: 3, + }; + function initialize(options) { + return fetchData(options.apiEndpoint, { + timeout: options.timeout, + }); + } + track("page_view", { path: location.pathname }); + body + main + section + div + script. + track("page_view", { + path: location.pathname, + }); diff --git a/tests/issues/issue-268/formatted-script-tabs.pug b/tests/issues/issue-268/formatted-script-tabs.pug new file mode 100644 index 00000000..19e268e4 --- /dev/null +++ b/tests/issues/issue-268/formatted-script-tabs.pug @@ -0,0 +1,22 @@ +html + head + script. + const config = { + apiEndpoint: "https://example.com/api/v1", + timeout: 5000, + retries: 3, + }; + function initialize(options) { + return fetchData(options.apiEndpoint, { + timeout: options.timeout, + }); + } + track("page_view", { path: location.pathname }); + body + main + section + div + script. + track("page_view", { + path: location.pathname, + }); diff --git a/tests/issues/issue-268/formatted.pug b/tests/issues/issue-268/formatted.pug index 920e8524..13d72b9a 100644 --- a/tests/issues/issue-268/formatted.pug +++ b/tests/issues/issue-268/formatted.pug @@ -10,7 +10,11 @@ html color="red" ). There is surely more I could write here - .short-again(style="display: none", color="red", font="Sans Serif") + .short-again( + style="display: none", + color="red", + font="Sans Serif" + ) | I could surely more I could write here .hi(data-wrap="false") This should be wrapped @@ -51,6 +55,12 @@ html .col-12 ul.actions li - input.primary(type="submit", value="Send Message") + input.primary( + type="submit", + value="Send Message" + ) li - input(type="reset", value="Reset Form") + input( + type="reset", + value="Reset Form" + ) diff --git a/tests/issues/issue-268/issue-268.test.ts b/tests/issues/issue-268/issue-268.test.ts index 1532ec74..171f4bb4 100644 --- a/tests/issues/issue-268/issue-268.test.ts +++ b/tests/issues/issue-268/issue-268.test.ts @@ -11,4 +11,40 @@ describe('Issues', () => { }); expect(actual).toBe(expected); }); + + it('should consider tabWidth for every indentation level when using useTabs', async () => { + const { actual, expected } = await compareFiles(import.meta.url, { + source: 'unformatted-nested.pug', + target: 'formatted-nested.pug', + formatOptions: { + useTabs: true, + tabWidth: 8, + }, + }); + expect(actual).toBe(expected); + }); + + it('should wrap script content considering tabWidth when using useTabs', async () => { + const { actual, expected } = await compareFiles(import.meta.url, { + source: 'unformatted-script.pug', + target: 'formatted-script-tabs.pug', + formatOptions: { + useTabs: true, + tabWidth: 8, + }, + }); + expect(actual).toBe(expected); + }); + + it('should wrap script content considering indentation when using spaces', async () => { + const { actual, expected } = await compareFiles(import.meta.url, { + source: 'unformatted-script.pug', + target: 'formatted-script-spaces.pug', + formatOptions: { + useTabs: false, + tabWidth: 8, + }, + }); + expect(actual).toBe(expected); + }); }); diff --git a/tests/issues/issue-268/unformatted-nested.pug b/tests/issues/issue-268/unformatted-nested.pug new file mode 100644 index 00000000..06eeb1a2 --- /dev/null +++ b/tests/issues/issue-268/unformatted-nested.pug @@ -0,0 +1,5 @@ +p + input#some-very-long-id-for-example-sake(type="input", placeeholder="example") + div + input#name(type="text", name="name", placeholder="Name *", required) + input#name(type="text", name="name", placeholder="Name *", required) diff --git a/tests/issues/issue-268/unformatted-script.pug b/tests/issues/issue-268/unformatted-script.pug new file mode 100644 index 00000000..920e20f9 --- /dev/null +++ b/tests/issues/issue-268/unformatted-script.pug @@ -0,0 +1,12 @@ +html + head + script. + const config = { apiEndpoint: 'https://example.com/api/v1', timeout: 5000, retries: 3 }; + function initialize(options) { return fetchData(options.apiEndpoint, { timeout: options.timeout }); } + track('page_view', { path: location.pathname }); + body + main + section + div + script. + track('page_view', { path: location.pathname });