Skip to content

Commit e699f9c

Browse files
authored
[Fixes #293] Update indent rule (#301)
* Update indent rule to handle properly multiline attributes lists * Add test with errors * Add alignAttributesVertically option * Pass alignAttributesVertically explicitly to processNodeList * Remove default argument value for Node 4 compatibility
1 parent 5c4896c commit e699f9c

File tree

3 files changed

+225
-55
lines changed

3 files changed

+225
-55
lines changed

Diff for: docs/rules/html-indent.md

+80-51
Original file line numberDiff line numberDiff line change
@@ -23,94 +23,123 @@ This rule enforces a consistent indentation style in `<template>`. The default s
2323

2424
```html
2525
<template>
26-
<div class="foo">
27-
Hello.
28-
</div>
26+
<div class="foo">
27+
Hello.
28+
</div>
2929
</template>
3030
```
3131

3232
```html
3333
<template>
34-
<div class="foo">
35-
Hello.
36-
</div>
37-
<div
38-
id="a"
39-
class="b"
40-
:other-attr="{
41-
aaa: 1,
42-
bbb: 2
43-
}"
44-
@other-attr2="
45-
foo();
46-
bar();
47-
"
48-
>
49-
{{
50-
displayMessage
51-
}}
52-
</div>
34+
<div class="foo">
35+
Hello.
36+
</div>
37+
<div class="foo"
38+
:foo="bar"
39+
>
40+
World.
41+
</div>
42+
<div
43+
id="a"
44+
class="b"
45+
:other-attr="{
46+
aaa: 1,
47+
bbb: 2
48+
}"
49+
@other-attr2="
50+
foo();
51+
bar();
52+
"
53+
>
54+
{{
55+
displayMessage
56+
}}
57+
</div>
5358
</template>
5459
```
5560

5661
## :wrench: Options
5762

5863
```json
5964
{
60-
"vue/html-indent": ["error", type, {
61-
"attribute": 1,
62-
"closeBracket": 0,
63-
"ignores": []
64-
}]
65+
"vue/html-indent": ["error", type, {
66+
"attribute": 1,
67+
"closeBracket": 0,
68+
"alignAttributesVertically": true,
69+
"ignores": []
70+
}]
6571
}
6672
```
6773

6874
- `type` (`number | "tab"`) ... The type of indentation. Default is `2`. If this is a number, it's the number of spaces for one indent. If this is `"tab"`, it uses one tab for one indent.
6975
- `attribute` (`integer`) ... The multiplier of indentation for attributes. Default is `1`.
7076
- `closeBracket` (`integer`) ... The multiplier of indentation for right brackets. Default is `0`.
77+
- `alignAttributesVertically` (`boolean`) ... Condition for whether attributes should be vertically aligned to the first attribute in multiline case or not. Default is `true`
7178
- `ignores` (`string[]`) ... The selector to ignore nodes. The AST spec is [here](https://github.com/mysticatea/vue-eslint-parser/blob/master/docs/ast.md). You can use [esquery](https://github.com/estools/esquery#readme) to select nodes. Default is an empty array.
7279

7380
:+1: Examples of **correct** code for `{attribute: 1, closeBracket: 1}`:
7481

7582
```html
7683
<template>
77-
<div
78-
id="a"
79-
class="b"
80-
other-attr=
81-
"{longname: longvalue}"
82-
other-attr2
83-
="{longname: longvalue}"
84-
>
85-
Text
86-
</div>
84+
<div
85+
id="a"
86+
class="b"
87+
other-attr=
88+
"{longname: longvalue}"
89+
other-attr2
90+
="{longname: longvalue}"
91+
>
92+
Text
93+
</div>
8794
</template>
8895
```
8996

9097
:+1: Examples of **correct** code for `{attribute: 2, closeBracket: 1}`:
9198

9299
```html
93100
<template>
94-
<div
95-
id="a"
96-
class="b"
97-
other-attr=
98-
"{longname: longvalue}"
99-
other-attr2
100-
="{longname: longvalue}"
101-
>
102-
Text
103-
</div>
101+
<div
102+
id="a"
103+
class="b"
104+
other-attr=
105+
"{longname: longvalue}"
106+
other-attr2
107+
="{longname: longvalue}"
108+
>
109+
Text
110+
</div>
104111
</template>
105112
```
106113

107114
:+1: Examples of **correct** code for `{ignores: ["VAttribute"]}`:
108115

109116
```html
110117
<template>
111-
<div
112-
id=""
113-
class=""
114-
/>
118+
<div
119+
id=""
120+
class=""
121+
/>
122+
</template>
123+
```
124+
125+
:+1: Examples of **correct** code for `{alignAttributesVertically: true}`:
126+
127+
```html
128+
<template>
129+
<div id=""
130+
class=""
131+
some-attr=""
132+
/>
133+
</template>
134+
```
135+
136+
:+1: Examples of **correct** code for `{alignAttributesVertically: false}`:
137+
138+
```html
139+
<template>
140+
<div id=""
141+
class=""
142+
some-attr=""
143+
/>
115144
</template>
116145
```

Diff for: lib/rules/html-indent.js

+19-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ function parseOptions (type, options) {
3434
attribute: 1,
3535
closeBracket: 0,
3636
switchCase: 0,
37+
alignAttributesVertically: true,
3738
ignores: []
3839
}
3940

@@ -53,6 +54,9 @@ function parseOptions (type, options) {
5354
if (Number.isSafeInteger(options.switchCase)) {
5455
ret.switchCase = options.switchCase
5556
}
57+
if (options.alignAttributesVertically != null) {
58+
ret.alignAttributesVertically = options.alignAttributesVertically
59+
}
5660
if (options.ignores != null) {
5761
ret.ignores = options.ignores
5862
}
@@ -304,8 +308,9 @@ function create (context) {
304308
* @param {number} offset The offset to set.
305309
* @returns {void}
306310
*/
307-
function processNodeList (nodeList, leftToken, rightToken, offset) {
311+
function processNodeList (nodeList, leftToken, rightToken, offset, alignVertically) {
308312
let t
313+
alignVertically = alignVertically != null ? alignVertically : true
309314

310315
if (nodeList.length >= 1) {
311316
let lastToken = leftToken
@@ -349,11 +354,20 @@ function create (context) {
349354
setOffset(baseToken, offset, leftToken)
350355
}
351356

352-
// Align the rest tokens to the first token.
357+
// Set baseline
353358
if (nodeList.some(isBeginningOfLine)) {
354359
setBaseline(baseToken)
355360
}
356-
setOffset(alignTokens, 0, baseToken)
361+
362+
if (alignVertically) {
363+
// Align the rest tokens to the first token.
364+
setOffset(alignTokens, 0, baseToken)
365+
} else {
366+
// Align tokens relatively to passed root node
367+
// So it's possible to force proper position for VAttributes
368+
const rootNode = nodeList && nodeList[0].parent
369+
setOffset(alignTokens, offset, template.getFirstToken(rootNode))
370+
}
357371
}
358372
}
359373

@@ -760,7 +774,7 @@ function create (context) {
760774
const openToken = template.getFirstToken(node)
761775
const closeToken = template.getLastToken(node)
762776

763-
processNodeList(node.attributes, openToken, null, options.attribute)
777+
processNodeList(node.attributes, openToken, null, options.attribute, options.alignAttributesVertically)
764778
if (closeToken != null && closeToken.type.endsWith('TagClose')) {
765779
setOffset(closeToken, options.closeBracket, openToken)
766780
}
@@ -1294,6 +1308,7 @@ module.exports = {
12941308
'attribute': { type: 'integer', minimum: 0 },
12951309
'closeBracket': { type: 'integer', minimum: 0 },
12961310
'switchCase': { type: 'integer', minimum: 0 },
1311+
'alignAttributesVertically': { type: 'boolean' },
12971312
'ignores': {
12981313
type: 'array',
12991314
items: {

Diff for: tests/lib/rules/html-indent.js

+126
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,40 @@ tester.run('html-indent', rule, {
12691269
options: [4, { switchCase: 1 }]
12701270
},
12711271

1272+
// options.alignAttributesVertically
1273+
{
1274+
code: unIndent`
1275+
<template>
1276+
<div a="a"
1277+
b="b"
1278+
c=
1279+
"c"
1280+
d
1281+
="d"
1282+
e
1283+
f
1284+
=
1285+
></div>
1286+
</template>
1287+
`,
1288+
options: [2, {
1289+
alignAttributesVertically: false
1290+
}]
1291+
},
1292+
{
1293+
code: unIndent`
1294+
<template>
1295+
<div a="a"
1296+
:b="b"
1297+
c="c"
1298+
></div>
1299+
</template>
1300+
`,
1301+
options: [2, {
1302+
alignAttributesVertically: false
1303+
}]
1304+
},
1305+
12721306
// Comments
12731307
unIndent`
12741308
<template>
@@ -1467,6 +1501,28 @@ tester.run('html-indent', rule, {
14671501
"
14681502
/>
14691503
</template>
1504+
`,
1505+
unIndent`
1506+
<template>
1507+
<div a="a"
1508+
:b="b"
1509+
c="c"
1510+
></div>
1511+
</template>
1512+
`,
1513+
unIndent`
1514+
<template>
1515+
<div
1516+
a="a"
1517+
b="b"
1518+
></div>
1519+
</template>
1520+
`,
1521+
unIndent`
1522+
<template>
1523+
<div a="a" b="b">
1524+
</div>
1525+
</template>
14701526
`
14711527
],
14721528

@@ -1520,6 +1576,76 @@ tester.run('html-indent', rule, {
15201576
{ message: 'Expected indentation of 12 spaces but found 10 spaces.', line: 11 }
15211577
]
15221578
},
1579+
{
1580+
code: unIndent`
1581+
<template>
1582+
<div a="a"
1583+
b="b"
1584+
c=
1585+
"c"
1586+
>
1587+
Text
1588+
</div>
1589+
</template>
1590+
`,
1591+
output: unIndent`
1592+
<template>
1593+
<div a="a"
1594+
b="b"
1595+
c=
1596+
"c"
1597+
>
1598+
Text
1599+
</div>
1600+
</template>
1601+
`,
1602+
options: [2],
1603+
errors: [
1604+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 2 },
1605+
{ message: 'Expected indentation of 7 spaces but found 8 spaces.', line: 3 },
1606+
{ message: 'Expected indentation of 7 spaces but found 8 spaces.', line: 4 },
1607+
{ message: 'Expected indentation of 9 spaces but found 12 spaces.', line: 5 },
1608+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 6 },
1609+
{ message: 'Expected indentation of 4 spaces but found 8 spaces.', line: 7 },
1610+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 8 }
1611+
]
1612+
},
1613+
{
1614+
code: unIndent`
1615+
<template>
1616+
<div a="a"
1617+
b="b"
1618+
c=
1619+
"c"
1620+
>
1621+
Text
1622+
</div>
1623+
</template>
1624+
`,
1625+
output: unIndent`
1626+
<template>
1627+
<div a="a"
1628+
b="b"
1629+
c=
1630+
"c"
1631+
>
1632+
Text
1633+
</div>
1634+
</template>
1635+
`,
1636+
options: [2, {
1637+
alignAttributesVertically: false
1638+
}],
1639+
errors: [
1640+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 2 },
1641+
{ message: 'Expected indentation of 4 spaces but found 8 spaces.', line: 3 },
1642+
{ message: 'Expected indentation of 4 spaces but found 8 spaces.', line: 4 },
1643+
{ message: 'Expected indentation of 6 spaces but found 12 spaces.', line: 5 },
1644+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 6 },
1645+
{ message: 'Expected indentation of 4 spaces but found 8 spaces.', line: 7 },
1646+
{ message: 'Expected indentation of 2 spaces but found 4 spaces.', line: 8 }
1647+
]
1648+
},
15231649

15241650
// VEndTag
15251651
{

0 commit comments

Comments
 (0)