Skip to content

Commit d708da6

Browse files
authored
Improved closing bracket indentation for vue/html-indent rule. (#1162)
- Added `closeBracket.startTag`, `closeBracket.endTag` and `closeBracket.selfClosingTag` options to `vue/html-indent` rule. So that the closeBracket offset value can be set for each tag type. - Changed `vue/html-indent` rule to calculate the base point of the indent offset of the closing bracket of the end tag by the start tag.
1 parent e004975 commit d708da6

9 files changed

+157
-8
lines changed

docs/rules/html-indent.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,11 @@ This rule enforces a consistent indentation style in `<template>`. The default s
7676
- `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.
7777
- `attribute` (`integer`) ... The multiplier of indentation for attributes. Default is `1`.
7878
- `baseIndent` (`integer`) ... The multiplier of indentation for top-level statements. Default is `1`.
79-
- `closeBracket` (`integer`) ... The multiplier of indentation for right brackets. Default is `0`.
79+
- `closeBracket` (`integer | object`) ... The multiplier of indentation for right brackets. Default is `0`.
80+
You can apply all of the following by setting a number value.
81+
- `closeBracket.startTag` (`integer`) ... The multiplier of indentation for right brackets of start tags (`<div>`). Default is `0`.
82+
- `closeBracket.endTag` (`integer`) ... The multiplier of indentation for right brackets of end tags (`</div>`). Default is `0`.
83+
- `closeBracket.selfClosingTag` (`integer`) ... The multiplier of indentation for right brackets of start tags (`<div/>`). Default is `0`.
8084
- `alignAttributesVertically` (`boolean`) ... Condition for whether attributes should be vertically aligned to the first attribute in multiline case or not. Default is `true`
8185
- `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.
8286

lib/rules/html-indent.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,20 @@ module.exports = {
4444
properties: {
4545
attribute: { type: 'integer', minimum: 0 },
4646
baseIndent: { type: 'integer', minimum: 0 },
47-
closeBracket: { type: 'integer', minimum: 0 },
47+
closeBracket: {
48+
anyOf: [
49+
{ type: 'integer', minimum: 0 },
50+
{
51+
type: 'object',
52+
properties: {
53+
startTag: { type: 'integer', minimum: 0 },
54+
endTag: { type: 'integer', minimum: 0 },
55+
selfClosingTag: { type: 'integer', minimum: 0 }
56+
},
57+
additionalProperties: false
58+
}
59+
]
60+
},
4861
switchCase: { type: 'integer', minimum: 0 },
4962
alignAttributesVertically: { type: 'boolean' },
5063
ignores: {

lib/utils/indent-common.js

+42-6
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,38 @@ const BLOCK_COMMENT_PREFIX = /^\s*\*/
2121
const ITERATION_OPTS = Object.freeze({ includeComments: true, filter: isNotWhitespace })
2222
const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea']
2323

24+
/**
25+
* @typedef {object} IndentOptions
26+
* @property { " " | "\t" } IndentOptions.indentChar
27+
* @property {number} IndentOptions.indentSize
28+
* @property {number} IndentOptions.baseIndent
29+
* @property {number} IndentOptions.attribute
30+
* @property {object} IndentOptions.closeBracket
31+
* @property {number} IndentOptions.closeBracket.startTag
32+
* @property {number} IndentOptions.closeBracket.endTag
33+
* @property {number} IndentOptions.closeBracket.selfClosingTag
34+
* @property {number} IndentOptions.switchCase
35+
* @property {boolean} IndentOptions.alignAttributesVertically
36+
* @property {string[]} IndentOptions.ignores
37+
*/
2438
/**
2539
* Normalize options.
2640
* @param {number|"tab"|undefined} type The type of indentation.
2741
* @param {Object} options Other options.
2842
* @param {Object} defaultOptions The default value of options.
29-
* @returns {{indentChar:" "|"\t",indentSize:number,baseIndent:number,attribute:number,closeBracket:number,switchCase:number,alignAttributesVertically:boolean,ignores:string[]}} Normalized options.
43+
* @returns {IndentOptions} Normalized options.
3044
*/
3145
function parseOptions (type, options, defaultOptions) {
3246
const ret = Object.assign({
3347
indentChar: ' ',
3448
indentSize: 2,
3549
baseIndent: 0,
3650
attribute: 1,
37-
closeBracket: 0,
51+
closeBracket: {
52+
startTag: 0,
53+
endTag: 0,
54+
selfClosingTag: 0
55+
},
3856
switchCase: 0,
3957
alignAttributesVertically: true,
4058
ignores: []
@@ -54,7 +72,21 @@ function parseOptions (type, options, defaultOptions) {
5472
ret.attribute = options.attribute
5573
}
5674
if (Number.isSafeInteger(options.closeBracket)) {
57-
ret.closeBracket = options.closeBracket
75+
const num = options.closeBracket
76+
ret.closeBracket = {
77+
startTag: num,
78+
endTag: num,
79+
selfClosingTag: num
80+
}
81+
} else if (options.closeBracket) {
82+
ret.closeBracket = Object.assign(
83+
{
84+
startTag: 0,
85+
endTag: 0,
86+
selfClosingTag: 0
87+
},
88+
options.closeBracket
89+
)
5890
}
5991
if (Number.isSafeInteger(options.switchCase)) {
6092
ret.switchCase = options.switchCase
@@ -919,11 +951,12 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
919951
},
920952

921953
VEndTag (node) {
922-
const openToken = tokenStore.getFirstToken(node)
954+
const element = node.parent
955+
const startTagOpenToken = tokenStore.getFirstToken(element.startTag)
923956
const closeToken = tokenStore.getLastToken(node)
924957

925958
if (closeToken.type.endsWith('TagClose')) {
926-
setOffset(closeToken, options.closeBracket, openToken)
959+
setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken)
927960
}
928961
},
929962

@@ -996,7 +1029,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
9961029
options.alignAttributesVertically
9971030
)
9981031
if (closeToken != null && closeToken.type.endsWith('TagClose')) {
999-
setOffset(closeToken, options.closeBracket, openToken)
1032+
const offset = closeToken.type !== 'HTMLSelfClosingTagClose'
1033+
? options.closeBracket.startTag
1034+
: options.closeBracket.selfClosingTag
1035+
setOffset(closeToken, offset, openToken)
10001036
}
10011037
},
10021038

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{"options":[2, { "closeBracket": 1 }]}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{"options":[2, { "closeBracket": { "startTag": 1 } }]}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{"options":[2, { "closeBracket": { "endTag": 1 } }]}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{"options":[2, { "closeBracket": { "selfClosingTag": 1 } }]}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<!--{"options":[2, { "closeBracket": { "startTag": 1, "endTag": 2, "selfClosingTag": 3 } }]}-->
2+
<template>
3+
<a
4+
href="#"
5+
>Link 1</a
6+
>
7+
<a
8+
href="#"
9+
>
10+
Link 2</a
11+
>
12+
<div>Content</div
13+
>
14+
<div
15+
/>
16+
</template>

0 commit comments

Comments
 (0)