Skip to content

Commit

Permalink
Improved closing bracket indentation for vue/html-indent rule. (#1162)
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
ota-meshi committed May 30, 2020
1 parent e004975 commit d708da6
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 8 deletions.
6 changes: 5 additions & 1 deletion docs/rules/html-indent.md
Expand Up @@ -76,7 +76,11 @@ This rule enforces a consistent indentation style in `<template>`. The default s
- `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.
- `attribute` (`integer`) ... The multiplier of indentation for attributes. Default is `1`.
- `baseIndent` (`integer`) ... The multiplier of indentation for top-level statements. Default is `1`.
- `closeBracket` (`integer`) ... The multiplier of indentation for right brackets. Default is `0`.
- `closeBracket` (`integer | object`) ... The multiplier of indentation for right brackets. Default is `0`.
You can apply all of the following by setting a number value.
- `closeBracket.startTag` (`integer`) ... The multiplier of indentation for right brackets of start tags (`<div>`). Default is `0`.
- `closeBracket.endTag` (`integer`) ... The multiplier of indentation for right brackets of end tags (`</div>`). Default is `0`.
- `closeBracket.selfClosingTag` (`integer`) ... The multiplier of indentation for right brackets of start tags (`<div/>`). Default is `0`.
- `alignAttributesVertically` (`boolean`) ... Condition for whether attributes should be vertically aligned to the first attribute in multiline case or not. Default is `true`
- `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.

Expand Down
15 changes: 14 additions & 1 deletion lib/rules/html-indent.js
Expand Up @@ -44,7 +44,20 @@ module.exports = {
properties: {
attribute: { type: 'integer', minimum: 0 },
baseIndent: { type: 'integer', minimum: 0 },
closeBracket: { type: 'integer', minimum: 0 },
closeBracket: {
anyOf: [
{ type: 'integer', minimum: 0 },
{
type: 'object',
properties: {
startTag: { type: 'integer', minimum: 0 },
endTag: { type: 'integer', minimum: 0 },
selfClosingTag: { type: 'integer', minimum: 0 }
},
additionalProperties: false
}
]
},
switchCase: { type: 'integer', minimum: 0 },
alignAttributesVertically: { type: 'boolean' },
ignores: {
Expand Down
48 changes: 42 additions & 6 deletions lib/utils/indent-common.js
Expand Up @@ -21,20 +21,38 @@ const BLOCK_COMMENT_PREFIX = /^\s*\*/
const ITERATION_OPTS = Object.freeze({ includeComments: true, filter: isNotWhitespace })
const PREFORMATTED_ELEMENT_NAMES = ['pre', 'textarea']

/**
* @typedef {object} IndentOptions
* @property { " " | "\t" } IndentOptions.indentChar
* @property {number} IndentOptions.indentSize
* @property {number} IndentOptions.baseIndent
* @property {number} IndentOptions.attribute
* @property {object} IndentOptions.closeBracket
* @property {number} IndentOptions.closeBracket.startTag
* @property {number} IndentOptions.closeBracket.endTag
* @property {number} IndentOptions.closeBracket.selfClosingTag
* @property {number} IndentOptions.switchCase
* @property {boolean} IndentOptions.alignAttributesVertically
* @property {string[]} IndentOptions.ignores
*/
/**
* Normalize options.
* @param {number|"tab"|undefined} type The type of indentation.
* @param {Object} options Other options.
* @param {Object} defaultOptions The default value of options.
* @returns {{indentChar:" "|"\t",indentSize:number,baseIndent:number,attribute:number,closeBracket:number,switchCase:number,alignAttributesVertically:boolean,ignores:string[]}} Normalized options.
* @returns {IndentOptions} Normalized options.
*/
function parseOptions (type, options, defaultOptions) {
const ret = Object.assign({
indentChar: ' ',
indentSize: 2,
baseIndent: 0,
attribute: 1,
closeBracket: 0,
closeBracket: {
startTag: 0,
endTag: 0,
selfClosingTag: 0
},
switchCase: 0,
alignAttributesVertically: true,
ignores: []
Expand All @@ -54,7 +72,21 @@ function parseOptions (type, options, defaultOptions) {
ret.attribute = options.attribute
}
if (Number.isSafeInteger(options.closeBracket)) {
ret.closeBracket = options.closeBracket
const num = options.closeBracket
ret.closeBracket = {
startTag: num,
endTag: num,
selfClosingTag: num
}
} else if (options.closeBracket) {
ret.closeBracket = Object.assign(
{
startTag: 0,
endTag: 0,
selfClosingTag: 0
},
options.closeBracket
)
}
if (Number.isSafeInteger(options.switchCase)) {
ret.switchCase = options.switchCase
Expand Down Expand Up @@ -919,11 +951,12 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
},

VEndTag (node) {
const openToken = tokenStore.getFirstToken(node)
const element = node.parent
const startTagOpenToken = tokenStore.getFirstToken(element.startTag)
const closeToken = tokenStore.getLastToken(node)

if (closeToken.type.endsWith('TagClose')) {
setOffset(closeToken, options.closeBracket, openToken)
setOffset(closeToken, options.closeBracket.endTag, startTagOpenToken)
}
},

Expand Down Expand Up @@ -996,7 +1029,10 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
options.alignAttributesVertically
)
if (closeToken != null && closeToken.type.endsWith('TagClose')) {
setOffset(closeToken, options.closeBracket, openToken)
const offset = closeToken.type !== 'HTMLSelfClosingTagClose'
? options.closeBracket.startTag
: options.closeBracket.selfClosingTag
setOffset(closeToken, offset, openToken)
}
},

Expand Down
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-01.vue
@@ -0,0 +1,16 @@
<!--{}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-02.vue
@@ -0,0 +1,16 @@
<!--{"options":[2, { "closeBracket": 1 }]}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-03.vue
@@ -0,0 +1,16 @@
<!--{"options":[2, { "closeBracket": { "startTag": 1 } }]}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-04.vue
@@ -0,0 +1,16 @@
<!--{"options":[2, { "closeBracket": { "endTag": 1 } }]}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-05.vue
@@ -0,0 +1,16 @@
<!--{"options":[2, { "closeBracket": { "selfClosingTag": 1 } }]}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>
16 changes: 16 additions & 0 deletions tests/fixtures/html-indent/close-bracket-06.vue
@@ -0,0 +1,16 @@
<!--{"options":[2, { "closeBracket": { "startTag": 1, "endTag": 2, "selfClosingTag": 3 } }]}-->
<template>
<a
href="#"
>Link 1</a
>
<a
href="#"
>
Link 2</a
>
<div>Content</div
>
<div
/>
</template>

0 comments on commit d708da6

Please sign in to comment.