Skip to content

Commit

Permalink
Add consistent option to vue/padding-line-between-tags (#1982)
Browse files Browse the repository at this point in the history
* Add consistent rule

* Make consistent only considers blocks with consistent rule

* Use first element as guide for rest of siblings

* Simplify logic

* Remove unused
  • Loading branch information
dev1437 committed Sep 30, 2022
1 parent f4f946a commit cae6d29
Show file tree
Hide file tree
Showing 2 changed files with 364 additions and 73 deletions.
168 changes: 95 additions & 73 deletions lib/rules/padding-line-between-tags.js
Expand Up @@ -24,44 +24,75 @@ function splitLines(text) {
* @param {RuleContext} context
* @param {VElement} tag
* @param {VElement} sibling
* @param {number} lineDifference
*/
function insertNewLine(context, tag, sibling) {
context.report({
messageId: 'always',
loc: sibling.loc,
// @ts-ignore
fix(fixer) {
return fixer.insertTextAfter(tag, '\n')
}
})
function insertNewLine(context, tag, sibling, lineDifference) {
const endTag = tag.endTag || tag.startTag

if (lineDifference === 1) {
context.report({
messageId: 'always',
loc: sibling.loc,
// @ts-ignore
fix(fixer) {
return fixer.insertTextAfter(tag, '\n')
}
})
} else if (lineDifference === 0) {
context.report({
messageId: 'always',
loc: sibling.loc,
// @ts-ignore
fix(fixer) {
const lastSpaces = /** @type {RegExpExecArray} */ (
/^\s*/.exec(context.getSourceCode().lines[endTag.loc.start.line - 1])
)[0]

return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`)
}
})
}
}

/**
* @param {RuleContext} context
* @param {VEndTag | VStartTag} endTag
* @param {VElement} sibling
* @param {number} lineDifference
*/
function removeExcessLines(context, endTag, sibling) {
context.report({
messageId: 'never',
loc: sibling.loc,
// @ts-ignore
fix(fixer) {
const start = endTag.range[1]
const end = sibling.range[0]
const paddingText = context.getSourceCode().text.slice(start, end)
const textBetween = splitLines(paddingText)
let newTextBetween = `\n${textBetween.pop()}`
for (let i = textBetween.length - 1; i >= 0; i--) {
if (!/^\s*$/.test(textBetween[i])) {
newTextBetween = `${i === 0 ? '' : '\n'}${
textBetween[i]
}${newTextBetween}`
function removeExcessLines(context, endTag, sibling, lineDifference) {
if (lineDifference > 1) {
let hasOnlyTextBetween = true
for (
let i = endTag.loc.start.line;
i < sibling.loc.start.line - 1 && hasOnlyTextBetween;
i++
) {
hasOnlyTextBetween = !/^\s*$/.test(context.getSourceCode().lines[i])
}
if (!hasOnlyTextBetween) {
context.report({
messageId: 'never',
loc: sibling.loc,
// @ts-ignore
fix(fixer) {
const start = endTag.range[1]
const end = sibling.range[0]
const paddingText = context.getSourceCode().text.slice(start, end)
const textBetween = splitLines(paddingText)
let newTextBetween = `\n${textBetween.pop()}`
for (let i = textBetween.length - 1; i >= 0; i--) {
if (!/^\s*$/.test(textBetween[i])) {
newTextBetween = `${i === 0 ? '' : '\n'}${
textBetween[i]
}${newTextBetween}`
}
}
return fixer.replaceTextRange([start, end], `${newTextBetween}`)
}
}
return fixer.replaceTextRange([start, end], `${newTextBetween}`)
})
}
})
}
}

// ------------------------------------------------------------------------------
Expand All @@ -72,11 +103,19 @@ function removeExcessLines(context, endTag, sibling) {
* @param {RuleContext} context
*/
function checkNewline(context) {
/** @type {Array<{blankLine: "always" | "never", prev: string, next: string}>} */
/** @type {Array<{blankLine: "always" | "never" | "consistent", prev: string, next: string}>} */
const configureList = context.options[0] || [
{ blankLine: 'always', prev: '*', next: '*' }
]

const reverseConfigureList = [...configureList].reverse()

/**
* It has the style of the first `blankLine="consistent"`.
* @type {Map<VElement, "always" | "never">}
*/
const firstConsistentBlankLines = new Map()

/**
* @param {VElement} block
*/
Expand All @@ -99,53 +138,36 @@ function checkNewline(context) {

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

for (let i = configureList.length - 1; i >= 0; --i) {
const configure = configureList[i]
const matched =
const configure = reverseConfigureList.find(
(configure) =>
(configure.prev === '*' || block.name === configure.prev) &&
(configure.next === '*' || closestSibling.name === configure.next)
)

if (matched) {
const lineDifference =
closestSibling.loc.start.line - endTag.loc.end.line
if (configure.blankLine === 'always') {
if (lineDifference === 1) {
insertNewLine(context, block, closestSibling)
} else if (lineDifference === 0) {
context.report({
messageId: 'always',
loc: closestSibling.loc,
// @ts-ignore
fix(fixer) {
const lastSpaces = /** @type {RegExpExecArray} */ (
/^\s*/.exec(
context.getSourceCode().lines[endTag.loc.start.line - 1]
)
)[0]

return fixer.insertTextAfter(endTag, `\n\n${lastSpaces}`)
}
})
}
} else {
if (lineDifference > 1) {
let hasOnlyTextBetween = true
for (
let i = endTag.loc.start.line;
i < closestSibling.loc.start.line - 1 && hasOnlyTextBetween;
i++
) {
hasOnlyTextBetween = !/^\s*$/.test(
context.getSourceCode().lines[i]
)
}
if (!hasOnlyTextBetween) {
removeExcessLines(context, endTag, closestSibling)
}
}
}
break
if (!configure) {
return
}
const lineDifference = closestSibling.loc.start.line - endTag.loc.end.line

let blankLine = configure.blankLine
if (blankLine === 'consistent') {
const firstConsistentBlankLine = firstConsistentBlankLines.get(
block.parent
)
if (firstConsistentBlankLine == null) {
firstConsistentBlankLines.set(
block.parent,
lineDifference > 1 ? 'always' : 'never'
)
return
}
blankLine = firstConsistentBlankLine
}

if (blankLine === 'always') {
insertNewLine(context, block, closestSibling, lineDifference)
} else {
removeExcessLines(context, endTag, closestSibling, lineDifference)
}
}
}
Expand All @@ -166,7 +188,7 @@ module.exports = {
items: {
type: 'object',
properties: {
blankLine: { enum: ['always', 'never'] },
blankLine: { enum: ['always', 'never', 'consistent'] },
prev: { type: 'string' },
next: { type: 'string' }
},
Expand Down

0 comments on commit cae6d29

Please sign in to comment.