Skip to content

Commit

Permalink
Fix counting of current line-length (#121)
Browse files Browse the repository at this point in the history
* Fix counting of current line-length

So it works correctly with the various settings of attributeSeparator

* Move separator logic to shared function

* Add comments about printWidth: 80

* Apply suggestions from code review

* Make code that counts current line length clearer

Add comments explaining the handling of  literal attributes and normal ones

* Remove note about pugPrintWidth being inaccurate
  • Loading branch information
lehni authored Oct 4, 2020
1 parent 7cdeafc commit 1be2bdd
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 30 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ By using versions of these standard options prefixed with `pug`, you can overrid
Print spaces between brackets in object literals.
- `pugPrintWidth`
Specify the line length that the printer will wrap on.
_Currently this is not very accurate, but works._
- `pugSemi`
Print semicolons at the ends of code statements.
- `pugSingleQuote`
Expand Down
61 changes: 32 additions & 29 deletions src/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,11 @@ export class PugPrinter {
private result: string = '';

private currentIndex: number = 0;
private currentLineLength: number = 0;

private readonly indentString: string;
private indentLevel: number = 0;

/**
* The line length starts by 1, it's not an zero based index
*/
private currentLineLength: number = 1;

private readonly quotes: "'" | '"';
private readonly otherQuotes: "'" | '"';

Expand Down Expand Up @@ -234,6 +230,12 @@ export class PugPrinter {
return !!token && possibilities.includes(token.type) !== invert;
}

private tokenNeedsSeparator(token: AttributeToken): boolean {
return this.neverUseAttributeSeparator
? false
: this.alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name);
}

private formatDelegatePrettier(
val: string,
parser: '__vue_expression' | '__ng_binding' | '__ng_action' | '__ng_directive'
Expand Down Expand Up @@ -414,21 +416,24 @@ export class PugPrinter {
this.possibleClassPosition = this.result.length;
result = '(';
logger.debug(this.currentLineLength);
this.currentLineLength += 1;
let tempToken: AttributeToken | EndAttributesToken = this.nextToken;
let tempIndex: number = this.currentIndex + 1;
let nonPrefixAttributes: number = 0;
let hasPrefixAttribute: boolean = false;
let numAttributes: number = 0;
// In pug, tags can have two kind of attributes: normal attributes that appear between parantheses,
// and literals for ids and classes, prefixing the paranthesis, e.g.: `#id.class(attribute="value")`
// https://pugjs.org/language/attributes.html#class-literal
// https://pugjs.org/language/attributes.html#id-literal
// In the stream of attribute tokens, distinguish those that can be converted to literals,
// and count those that cannot (normal attributes) to determine the resulting line length correctly.
let hasLiteralAttributes: boolean = false;
let numNormalAttributes: number = 0;
while (tempToken.type === 'attribute') {
numAttributes++;
if (!this.wrapAttributes && this.wrapAttributesPattern?.test(tempToken.name)) {
this.wrapAttributes = true;
}
switch (tempToken.name) {
case 'class':
case 'id': {
hasPrefixAttribute = true;
hasLiteralAttributes = true;
const val: string = tempToken.val.toString();
if (isQuoted(val)) {
this.currentLineLength -= 2;
Expand All @@ -441,8 +446,15 @@ export class PugPrinter {
break;
}
default: {
nonPrefixAttributes += 1;
this.currentLineLength += tempToken.name.length;
if (numNormalAttributes > 0) {
// This isn't the first normal attribute that will appear between parantheses,
// add space and separator
this.currentLineLength += 1;
if (this.tokenNeedsSeparator(tempToken)) {
this.currentLineLength += 1;
}
}
logger.debug(
{ tokenName: tempToken.name, length: tempToken.name.length },
this.currentLineLength
Expand All @@ -452,35 +464,29 @@ export class PugPrinter {
this.currentLineLength += 1 + val.length;
logger.debug({ tokenVal: val, length: val.length }, this.currentLineLength);
}
numNormalAttributes++;
break;
}
}
tempToken = this.tokens[++tempIndex] as AttributeToken | EndAttributesToken;
}
logger.debug('after token', this.currentLineLength);
if (hasPrefixAttribute) {
// Remove div
if (hasLiteralAttributes) {
// Remove div as it will be replaced with the literal for id and/or class
if (this.previousToken?.type === 'tag' && this.previousToken.val === 'div') {
this.currentLineLength -= 3;
}
}
const hasPrefixAttributes: boolean = nonPrefixAttributes > 0;
if (!hasPrefixAttributes) {
// Remove leading brace
this.currentLineLength -= 1;
} else {
// Attributes are separated by commas: ', '.length === 2
this.currentLineLength += 2 * (nonPrefixAttributes - 1);

// Add trailing brace
this.currentLineLength += 1;
if (numNormalAttributes > 0) {
// Add leading and trailing parantheses
this.currentLineLength += 2;
}
logger.debug(this.currentLineLength);
if (
!this.wrapAttributes &&
(this.currentLineLength > this.options.pugPrintWidth ||
(this.options.pugWrapAttributesThreshold >= 0 &&
numAttributes > this.options.pugWrapAttributesThreshold))
numNormalAttributes > this.options.pugWrapAttributesThreshold))
) {
this.wrapAttributes = true;
}
Expand Down Expand Up @@ -584,10 +590,7 @@ export class PugPrinter {
this.currentIndex
);
if (this.previousToken?.type === 'attribute' && (!this.previousAttributeRemapped || hasNormalPreviousToken)) {
if (
!this.neverUseAttributeSeparator &&
(this.alwaysUseAttributeSeparator || /^(\(|\[|:).*/.test(token.name))
) {
if (this.tokenNeedsSeparator(token)) {
this.result += ',';
}
if (!this.wrapAttributes) {
Expand Down
2 changes: 2 additions & 0 deletions tests/options/attributeSeparator/always/always.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ describe('Options', () => {
const actual: string = format(code, {
parser: 'pug' as any,
plugins: [plugin],
// The `.length-test` elements are tested against a `printWidth` of 80 (currently also the default):
printWidth: 80,
// @ts-expect-error
attributeSeparator: 'always'
});
Expand Down
7 changes: 7 additions & 0 deletions tests/options/attributeSeparator/always/formatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.length-test(attributes1="foo", :attribute2="bar", attribute3="filler.........")
.length-test(
attributes1="foo",
:attribute2="bar",
attribute3="filler.........."
)

button(type="submit", (click)="play()", disabled)
hello-framework(
[name]="name",
Expand Down
3 changes: 3 additions & 0 deletions tests/options/attributeSeparator/always/unformatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.length-test(attributes1="foo", :attribute2="bar", attribute3="filler.........")
.length-test(attributes1="foo", :attribute2="bar", attribute3="filler..........")

button(type="submit", (click)="play()" disabled)
hello-framework([name]="name", [version]="version", (release)="onVersionRelease()")

Expand Down
2 changes: 2 additions & 0 deletions tests/options/attributeSeparator/as-needed/as-needed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ describe('Options', () => {
const actual: string = format(code, {
parser: 'pug' as any,
plugins: [plugin],
// The `.length-test` elements are tested against a `printWidth` of 80 (currently also the default):
printWidth: 80,
// @ts-expect-error
attributeSeparator: 'as-needed'
});
Expand Down
7 changes: 7 additions & 0 deletions tests/options/attributeSeparator/as-needed/formatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.length-test(attributes1="foo", :attribute2="bar" attribute3="filler..........")
.length-test(
attributes1="foo",
:attribute2="bar"
attribute3="filler..........."
)

button(type="submit", (click)="play()" disabled)
hello-framework(
[name]="name",
Expand Down
3 changes: 3 additions & 0 deletions tests/options/attributeSeparator/as-needed/unformatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.length-test(attributes1="foo", :attribute2="bar" attribute3="filler..........")
.length-test(attributes1="foo", :attribute2="bar" attribute3="filler...........")

button(type="submit", (click)="play()", disabled)
hello-framework([name]="name", [version]="version", (release)="onVersionRelease()")

Expand Down
7 changes: 7 additions & 0 deletions tests/options/attributeSeparator/none/formatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.length-test(attributes1="foo" :attribute2="bar" attribute3="filler...........")
.length-test(
attributes1="foo"
:attribute2="bar"
attribute3="filler............"
)

button(type="submit" :style="style" @click="play()" disabled)

nav-component(locale-relative-redirect="true" highlight="home" pin="false")
Expand Down
2 changes: 2 additions & 0 deletions tests/options/attributeSeparator/none/none.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ describe('Options', () => {
const actual: string = format(code, {
parser: 'pug' as any,
plugins: [plugin],
// The `.length-test` elements are tested against a `printWidth` of 80 (currently also the default):
printWidth: 80,
// @ts-expect-error
attributeSeparator: 'none'
});
Expand Down
3 changes: 3 additions & 0 deletions tests/options/attributeSeparator/none/unformatted.pug
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.length-test(attributes1="foo" :attribute2="bar" attribute3="filler...........")
.length-test(attributes1="foo" :attribute2="bar" attribute3="filler............")

button(type="submit", :style="style", @click="play()", disabled)

nav-component(locale-relative-redirect="true", highlight="home", pin="false")
Expand Down

0 comments on commit 1be2bdd

Please sign in to comment.