Skip to content

Commit

Permalink
Merge pull request #8 from Shinigami92/feature/wrap-attributes
Browse files Browse the repository at this point in the history
Support wrap attributes
  • Loading branch information
Shinigami92 authored Jul 1, 2019
2 parents 25df8a4 + 1feb666 commit 0b9e7c7
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 46 deletions.
108 changes: 75 additions & 33 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AST, Doc, FastPath, Options, Parser, ParserOptions, Plugin, util } from
// @ts-ignore
import * as lex from 'pug-lexer';
import { createLogger, Logger, LogLevel } from './logger';
import { Token } from './pug-token';
import { AttributeToken, EndAttributesToken, Token } from './pug-token';

const { makeString } = util;

Expand Down Expand Up @@ -31,6 +31,21 @@ function quotationType(code: string): QuotationType | undefined {
return;
}

function previousNormalAttributeToken(tokens: Token[], index: number): AttributeToken | undefined {
for (let i: number = index - 1; i > 0; i--) {
const token: Token = tokens[i];
if (token.type === 'start-attributes') {
return;
}
if (token.type === 'attribute') {
if (token.name !== 'class' && token.name !== 'id') {
return token;
}
}
}
return;
}

export const plugin: Plugin = {
languages: [
{
Expand Down Expand Up @@ -77,7 +92,7 @@ export const plugin: Plugin = {
'pug-ast': {
print(
path: FastPath,
{ singleQuote, tabWidth, useTabs }: ParserOptions,
{ printWidth, singleQuote, tabWidth, useTabs }: ParserOptions,
print: (path: FastPath) => Doc
): Doc {
const tokens: Token[] = path.stack[0];
Expand All @@ -90,10 +105,15 @@ export const plugin: Plugin = {
}
let pipelessText: boolean = false;

let startTagPosition: number = 0;
let startAttributePosition: number = 0;
let previousAttributeRemapped: boolean = false;
let wrapAttributes: boolean = false;

for (let index: number = 0; index < tokens.length; index++) {
const token: Token = tokens[index];
const previousToken = tokens[index - 1];
const nextToken = tokens[index + 1];
const previousToken: Token | undefined = tokens[index - 1];
const nextToken: Token | undefined = tokens[index + 1];
logger.debug('[printers:pug-ast:print]:', JSON.stringify(token));
switch (token.type) {
case 'tag':
Expand All @@ -111,10 +131,26 @@ export const plugin: Plugin = {
if (!(token.val === 'div' && (nextToken.type === 'class' || nextToken.type === 'id'))) {
result += token.val;
}
startTagPosition = result.length;
break;
case 'start-attributes':
if (nextToken && nextToken.type === 'attribute') {
previousAttributeRemapped = false;
startAttributePosition = result.length;
result += '(';
const start: number = result.lastIndexOf('\n') + 1;
let lineLength: number = result.substring(start).length;
console.info(lineLength, printWidth);
let tempToken: AttributeToken | EndAttributesToken = nextToken;
let tempIndex: number = index + 1;
while (tempToken.type === 'attribute') {
lineLength += tempToken.name.length + 1 + tempToken.val.toString().length;
console.info(lineLength, printWidth);
tempToken = tokens[++tempIndex] as AttributeToken | EndAttributesToken;
}
if (lineLength > printWidth) {
wrapAttributes = true;
}
}
break;
case 'attribute':
Expand All @@ -137,14 +173,17 @@ export const plugin: Plugin = {
continue;
}
// Write css-class in front of attributes
const position = result.lastIndexOf('(');
const position: number = startAttributePosition;
result = [result.slice(0, position), `.${className}`, result.slice(position)].join(
''
);
startAttributePosition += 1 + className.length;
}
if (specialClasses.length > 0) {
token.val = makeString(specialClasses.join(' '), singleQuote ? "'" : '"', false);
previousAttributeRemapped = false;
} else {
previousAttributeRemapped = true;
break;
}
} else if (
Expand All @@ -157,29 +196,35 @@ export const plugin: Plugin = {
val = val.substring(1, val.length - 1);
val = val.trim();
// Write css-id in front of css-classes
let lastPositionOfNewline = result.lastIndexOf('\n');
if (lastPositionOfNewline === -1) {
// If no newline was found, set position to zero
lastPositionOfNewline = 0;
}
let position: number = result.indexOf('.', lastPositionOfNewline);
const firstPositionOfStartAttributes: number = result.indexOf(
'(',
lastPositionOfNewline
);
if (
position === -1 ||
(firstPositionOfStartAttributes !== -1 && position > firstPositionOfStartAttributes)
) {
position = firstPositionOfStartAttributes;
}
const position: number = startTagPosition;
result = [result.slice(0, position), `#${val}`, result.slice(position)].join('');
startAttributePosition += 1 + val.length;
result = result.replace(/div#/, '#');
if (previousToken.type === 'attribute' && previousToken.name !== 'class') {
previousAttributeRemapped = true;
}
break;
}

if (previousToken && previousToken.type === 'attribute') {
result += ', ';
const hasNormalPreviousToken: AttributeToken | undefined = previousNormalAttributeToken(
tokens,
index
);
if (
previousToken &&
previousToken.type === 'attribute' &&
(!previousAttributeRemapped || hasNormalPreviousToken)
) {
result += ',';
if (!wrapAttributes) {
result += ' ';
}
}
previousAttributeRemapped = false;

if (wrapAttributes) {
result += '\n';
result += indent.repeat(indentLevel + 1);
}

result += `${token.name}`;
Expand All @@ -202,16 +247,8 @@ export const plugin: Plugin = {
// Swap single and double quotes
val = val.replace(/['"]/g, (match) => (match === '"' ? "'" : '"'));
}
} else if (val.startsWith("'")) {
if (!singleQuote) {
// Swap single and double quotes
val = val.replace(/['"]/g, (match) => (match === '"' ? "'" : '"'));
}
} else if (val.startsWith('"')) {
if (singleQuote) {
// Swap single and double quotes
val = val.replace(/['"]/g, (match) => (match === '"' ? "'" : '"'));
}
} else if (/^["'](.*)["']$/.test(val)) {
val = makeString(val.slice(1, -1), singleQuote ? "'" : '"', false);
} else if (val === 'true') {
// The value is exactly true and is not quoted
break;
Expand All @@ -226,6 +263,11 @@ export const plugin: Plugin = {
}
break;
case 'end-attributes':
if (wrapAttributes) {
result += '\n';
result += indent.repeat(indentLevel);
}
wrapAttributes = false;
if (result.endsWith('(')) {
// There were no attributes
result = result.substring(0, result.length - 1);
Expand Down
4 changes: 2 additions & 2 deletions src/pug-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface StartAttributesToken {
loc: Loc;
}

export interface Attribute {
export interface AttributeToken {
type: 'attribute';
loc: Loc;
name: string;
Expand Down Expand Up @@ -152,7 +152,7 @@ export interface FilterToken {
export type Token =
| TagToken
| StartAttributesToken
| Attribute
| AttributeToken
| EndAttributesToken
| IndentToken
| ClassToken
Expand Down
16 changes: 14 additions & 2 deletions test/indents/formatted-2-spaces.pug
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ html
.flex-box.header {{ $t('mylangkey.things') }}

.flex-box.select(v-if="things[0].visible")
v-autocomplete(name="things[0].name", v-model="things[0].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[0].name",
v-model="things[0].value",
item-text="name",
item-value="id",
:items="mythings"
)

.flex-box.select(v-if="things[1].visible")
v-autocomplete(name="things[1].name", v-model="things[1].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[1].name",
v-model="things[1].value",
item-text="name",
item-value="id",
:items="mythings"
)

#use-of-id(test)
span Hello
Expand Down
16 changes: 14 additions & 2 deletions test/indents/formatted-3-spaces.pug
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ html
.flex-box.header {{ $t('mylangkey.things') }}

.flex-box.select(v-if="things[0].visible")
v-autocomplete(name="things[0].name", v-model="things[0].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[0].name",
v-model="things[0].value",
item-text="name",
item-value="id",
:items="mythings"
)

.flex-box.select(v-if="things[1].visible")
v-autocomplete(name="things[1].name", v-model="things[1].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[1].name",
v-model="things[1].value",
item-text="name",
item-value="id",
:items="mythings"
)

#use-of-id(test)
span Hello
Expand Down
16 changes: 14 additions & 2 deletions test/indents/formatted-4-spaces.pug
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ html
.flex-box.header {{ $t('mylangkey.things') }}

.flex-box.select(v-if="things[0].visible")
v-autocomplete(name="things[0].name", v-model="things[0].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[0].name",
v-model="things[0].value",
item-text="name",
item-value="id",
:items="mythings"
)

.flex-box.select(v-if="things[1].visible")
v-autocomplete(name="things[1].name", v-model="things[1].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[1].name",
v-model="things[1].value",
item-text="name",
item-value="id",
:items="mythings"
)

#use-of-id(test)
span Hello
Expand Down
16 changes: 14 additions & 2 deletions test/indents/formatted-tabs.pug
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,22 @@ html
.flex-box.header {{ $t('mylangkey.things') }}

.flex-box.select(v-if="things[0].visible")
v-autocomplete(name="things[0].name", v-model="things[0].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[0].name",
v-model="things[0].value",
item-text="name",
item-value="id",
:items="mythings"
)

.flex-box.select(v-if="things[1].visible")
v-autocomplete(name="things[1].name", v-model="things[1].value", item-text="name", item-value="id", :items="mythings")
v-autocomplete(
name="things[1].name",
v-model="things[1].value",
item-text="name",
item-value="id",
:items="mythings"
)

#use-of-id(test)
span Hello
Expand Down
26 changes: 26 additions & 0 deletions test/literals/class-literals/formatted.pug
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,33 @@ div
.content.text-success
.content.text-success.bg-dark
.content.text-success.bg-dark
.content.aaa.ad.text-success(:with="code()")
.content.aaa.ad.text-success.bg-dark(class="#ad")
.content.aaa.ad.text-success.bg-dark(
other,
attributes="with many long text",
class="#ad",
so="it",
will="wrap",
the="line"
)
.content.aaa.ad.text-success.bg-dark(
another="long line",
:but="withCode()",
class="#ad"
)
- const classes = ['foo', 'bar', 'baz']
a.bing.fizz(class=classes)
a.bang.fizz.bib(class=classes, class=['bing'])

v-text-field.search(
:label="$t('search.label')",
prepend-inner-icon="search",
v-model="search",
single-line
)

span#testid.testclass.ab.cd.ef.hi.jk(
other="attribute",
other2="attribute2"
)
7 changes: 7 additions & 0 deletions test/literals/class-literals/unformatted.pug
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ div.content(class="text-success bg-dark")
.content(class="text-success")
.content(class="text-success bg-dark")
.content(class="text-success bg-dark")
.content.aaa.ad(:with="code()", class="text-success")
.content.aaa.ad(class="text-success #ad bg-dark")
.content.aaa.ad(other, attributes="with many long text", class="text-success #ad bg-dark", so="it", will="wrap", the="line")
.content.aaa.ad(another="long line", :but="withCode()", class="text-success #ad bg-dark")
- const classes = ['foo', 'bar', 'baz']
a(class=classes, class="bing", class="fizz")
a.bang(class=classes, class=['bing'], class="fizz bib")

v-text-field(:label="$t('search.label')", class="search", prepend-inner-icon="search", v-model="search", single-line)

span.testclass(class="ab", id="testid", class="cd", other="attribute", class="ef", class="hi", class="jk", other2="attribute2")
16 changes: 15 additions & 1 deletion test/literals/id-literals/formatted.pug
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,22 @@ a#main-link
#content

span#testid.testclass
span#testid.testclass.ab.cd
span#testid.testclass.ab.cd(other="attribute")
span#testid.testclass.ab.cd(other="attribute")
span#testid.testclass.ab.cd(
other="attribute",
with="many",
many="attributes",
in="this line"
)
span#testid.testclass
span#testid.testclass
span#testid.testclass.testclass2

v-text-field#search(:label="$t('search.label')", prepend-inner-icon="search", v-model="search", single-line)
v-text-field#search(
:label="$t('search.label')",
prepend-inner-icon="search",
v-model="search",
single-line
)
4 changes: 4 additions & 0 deletions test/literals/id-literals/unformatted.pug
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ a#main-link
div#content

span.testclass(id="testid")
span.testclass(id="testid", class="ab cd")
span.testclass(id="testid", class="ab cd", other="attribute")
span.testclass(class="ab", id="testid", class="cd", other="attribute")
span.testclass(id="testid", class="ab cd", other="attribute" with="many", many="attributes", in="this line")
span#testid.testclass
span.testclass#testid
span.testclass#testid(class="testclass2")
Expand Down
Loading

0 comments on commit 0b9e7c7

Please sign in to comment.