Skip to content

Commit cae6d29

Browse files
authored
Add consistent option to vue/padding-line-between-tags (#1982)
* Add consistent rule * Make consistent only considers blocks with consistent rule * Use first element as guide for rest of siblings * Simplify logic * Remove unused
1 parent f4f946a commit cae6d29

File tree

2 files changed

+364
-73
lines changed

2 files changed

+364
-73
lines changed

lib/rules/padding-line-between-tags.js

+95-73
Original file line numberDiff line numberDiff line change
@@ -24,44 +24,75 @@ function splitLines(text) {
2424
* @param {RuleContext} context
2525
* @param {VElement} tag
2626
* @param {VElement} sibling
27+
* @param {number} lineDifference
2728
*/
28-
function insertNewLine(context, tag, sibling) {
29-
context.report({
30-
messageId: 'always',
31-
loc: sibling.loc,
32-
// @ts-ignore
33-
fix(fixer) {
34-
return fixer.insertTextAfter(tag, '\n')
35-
}
36-
})
29+
function insertNewLine(context, tag, sibling, lineDifference) {
30+
const endTag = tag.endTag || tag.startTag
31+
32+
if (lineDifference === 1) {
33+
context.report({
34+
messageId: 'always',
35+
loc: sibling.loc,
36+
// @ts-ignore
37+
fix(fixer) {
38+
return fixer.insertTextAfter(tag, '\n')
39+
}
40+
})
41+
} else if (lineDifference === 0) {
42+
context.report({
43+
messageId: 'always',
44+
loc: sibling.loc,
45+
// @ts-ignore
46+
fix(fixer) {
47+
const lastSpaces = /** @type {RegExpExecArray} */ (
48+
/^\s*/.exec(context.getSourceCode().lines[endTag.loc.start.line - 1])
49+
)[0]
50+
51+
return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`)
52+
}
53+
})
54+
}
3755
}
3856

3957
/**
4058
* @param {RuleContext} context
4159
* @param {VEndTag | VStartTag} endTag
4260
* @param {VElement} sibling
61+
* @param {number} lineDifference
4362
*/
44-
function removeExcessLines(context, endTag, sibling) {
45-
context.report({
46-
messageId: 'never',
47-
loc: sibling.loc,
48-
// @ts-ignore
49-
fix(fixer) {
50-
const start = endTag.range[1]
51-
const end = sibling.range[0]
52-
const paddingText = context.getSourceCode().text.slice(start, end)
53-
const textBetween = splitLines(paddingText)
54-
let newTextBetween = `\n${textBetween.pop()}`
55-
for (let i = textBetween.length - 1; i >= 0; i--) {
56-
if (!/^\s*$/.test(textBetween[i])) {
57-
newTextBetween = `${i === 0 ? '' : '\n'}${
58-
textBetween[i]
59-
}${newTextBetween}`
63+
function removeExcessLines(context, endTag, sibling, lineDifference) {
64+
if (lineDifference > 1) {
65+
let hasOnlyTextBetween = true
66+
for (
67+
let i = endTag.loc.start.line;
68+
i < sibling.loc.start.line - 1 && hasOnlyTextBetween;
69+
i++
70+
) {
71+
hasOnlyTextBetween = !/^\s*$/.test(context.getSourceCode().lines[i])
72+
}
73+
if (!hasOnlyTextBetween) {
74+
context.report({
75+
messageId: 'never',
76+
loc: sibling.loc,
77+
// @ts-ignore
78+
fix(fixer) {
79+
const start = endTag.range[1]
80+
const end = sibling.range[0]
81+
const paddingText = context.getSourceCode().text.slice(start, end)
82+
const textBetween = splitLines(paddingText)
83+
let newTextBetween = `\n${textBetween.pop()}`
84+
for (let i = textBetween.length - 1; i >= 0; i--) {
85+
if (!/^\s*$/.test(textBetween[i])) {
86+
newTextBetween = `${i === 0 ? '' : '\n'}${
87+
textBetween[i]
88+
}${newTextBetween}`
89+
}
90+
}
91+
return fixer.replaceTextRange([start, end], `${newTextBetween}`)
6092
}
61-
}
62-
return fixer.replaceTextRange([start, end], `${newTextBetween}`)
93+
})
6394
}
64-
})
95+
}
6596
}
6697

6798
// ------------------------------------------------------------------------------
@@ -72,11 +103,19 @@ function removeExcessLines(context, endTag, sibling) {
72103
* @param {RuleContext} context
73104
*/
74105
function checkNewline(context) {
75-
/** @type {Array<{blankLine: "always" | "never", prev: string, next: string}>} */
106+
/** @type {Array<{blankLine: "always" | "never" | "consistent", prev: string, next: string}>} */
76107
const configureList = context.options[0] || [
77108
{ blankLine: 'always', prev: '*', next: '*' }
78109
]
79110

111+
const reverseConfigureList = [...configureList].reverse()
112+
113+
/**
114+
* It has the style of the first `blankLine="consistent"`.
115+
* @type {Map<VElement, "always" | "never">}
116+
*/
117+
const firstConsistentBlankLines = new Map()
118+
80119
/**
81120
* @param {VElement} block
82121
*/
@@ -99,53 +138,36 @@ function checkNewline(context) {
99138

100139
const closestSibling = /** @type {VElement} */ (lowerSiblings[0])
101140

102-
for (let i = configureList.length - 1; i >= 0; --i) {
103-
const configure = configureList[i]
104-
const matched =
141+
const configure = reverseConfigureList.find(
142+
(configure) =>
105143
(configure.prev === '*' || block.name === configure.prev) &&
106144
(configure.next === '*' || closestSibling.name === configure.next)
145+
)
107146

108-
if (matched) {
109-
const lineDifference =
110-
closestSibling.loc.start.line - endTag.loc.end.line
111-
if (configure.blankLine === 'always') {
112-
if (lineDifference === 1) {
113-
insertNewLine(context, block, closestSibling)
114-
} else if (lineDifference === 0) {
115-
context.report({
116-
messageId: 'always',
117-
loc: closestSibling.loc,
118-
// @ts-ignore
119-
fix(fixer) {
120-
const lastSpaces = /** @type {RegExpExecArray} */ (
121-
/^\s*/.exec(
122-
context.getSourceCode().lines[endTag.loc.start.line - 1]
123-
)
124-
)[0]
125-
126-
return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`)
127-
}
128-
})
129-
}
130-
} else {
131-
if (lineDifference > 1) {
132-
let hasOnlyTextBetween = true
133-
for (
134-
let i = endTag.loc.start.line;
135-
i < closestSibling.loc.start.line - 1 && hasOnlyTextBetween;
136-
i++
137-
) {
138-
hasOnlyTextBetween = !/^\s*$/.test(
139-
context.getSourceCode().lines[i]
140-
)
141-
}
142-
if (!hasOnlyTextBetween) {
143-
removeExcessLines(context, endTag, closestSibling)
144-
}
145-
}
146-
}
147-
break
147+
if (!configure) {
148+
return
149+
}
150+
const lineDifference = closestSibling.loc.start.line - endTag.loc.end.line
151+
152+
let blankLine = configure.blankLine
153+
if (blankLine === 'consistent') {
154+
const firstConsistentBlankLine = firstConsistentBlankLines.get(
155+
block.parent
156+
)
157+
if (firstConsistentBlankLine == null) {
158+
firstConsistentBlankLines.set(
159+
block.parent,
160+
lineDifference > 1 ? 'always' : 'never'
161+
)
162+
return
148163
}
164+
blankLine = firstConsistentBlankLine
165+
}
166+
167+
if (blankLine === 'always') {
168+
insertNewLine(context, block, closestSibling, lineDifference)
169+
} else {
170+
removeExcessLines(context, endTag, closestSibling, lineDifference)
149171
}
150172
}
151173
}
@@ -166,7 +188,7 @@ module.exports = {
166188
items: {
167189
type: 'object',
168190
properties: {
169-
blankLine: { enum: ['always', 'never'] },
191+
blankLine: { enum: ['always', 'never', 'consistent'] },
170192
prev: { type: 'string' },
171193
next: { type: 'string' }
172194
},

0 commit comments

Comments
 (0)