From a851dfb7bad5547a8caf05cd8a1e69df5b6b6da7 Mon Sep 17 00:00:00 2001 From: dev1437 Date: Mon, 19 Sep 2022 20:47:46 +0800 Subject: [PATCH 1/5] Add consistent rule --- lib/rules/padding-line-between-tags.js | 151 +++++++++++-------- tests/lib/rules/padding-line-between-tags.js | 115 ++++++++++++++ 2 files changed, 205 insertions(+), 61 deletions(-) diff --git a/lib/rules/padding-line-between-tags.js b/lib/rules/padding-line-between-tags.js index 3e0994a12..86472bf50 100644 --- a/lib/rules/padding-line-between-tags.js +++ b/lib/rules/padding-line-between-tags.js @@ -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}`) + }) } - }) + } } // ------------------------------------------------------------------------------ @@ -72,7 +103,7 @@ 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: '*' } ] @@ -109,40 +140,38 @@ function checkNewline(context) { 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] - ) + insertNewLine(context, block, closestSibling, lineDifference) + } else if (configure.blankLine === 'consistent') { + let newlineCount = 0 + let siblingLineDifference = 0 + /** @type {VElement | null} */ + let prevChild = null + const siblingElements = block.parent.children.filter( + (element) => element.type === 'VElement' + ) + // get parent + for (const child of siblingElements) { + if (!prevChild) { + prevChild = /** @type {VElement} */ (child) + continue } - if (!hasOnlyTextBetween) { - removeExcessLines(context, endTag, closestSibling) + siblingLineDifference = + child.loc.start.line - prevChild.loc.end.line + + if (siblingLineDifference > 1) { + newlineCount++ } + prevChild = /** @type {VElement} */ (child) + } + const ratio = newlineCount / (siblingElements.length - 1) + + if (0.5 - ratio < 0.005) { + insertNewLine(context, block, closestSibling, lineDifference) + } else { + removeExcessLines(context, endTag, closestSibling, lineDifference) } + } else { + removeExcessLines(context, endTag, closestSibling, lineDifference) } break } @@ -166,7 +195,7 @@ module.exports = { items: { type: 'object', properties: { - blankLine: { enum: ['always', 'never'] }, + blankLine: { enum: ['always', 'never', 'consistent'] }, prev: { type: 'string' }, next: { type: 'string' } }, diff --git a/tests/lib/rules/padding-line-between-tags.js b/tests/lib/rules/padding-line-between-tags.js index 18d6743a0..b5fb99c2d 100644 --- a/tests/lib/rules/padding-line-between-tags.js +++ b/tests/lib/rules/padding-line-between-tags.js @@ -17,6 +17,43 @@ const tester = new RuleTester({ tester.run('padding-line-between-tags', rule, { valid: [ + { + filename: 'test.vue', + code: ` + + `, + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] + }, { filename: 'test.vue', code: ` @@ -1027,6 +1064,84 @@ tester.run('padding-line-between-tags', rule, { } ], options: [[{ blankLine: 'never', prev: '*', next: '*' }]] + }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + errors: [ + { + message: 'Expected blank line before this tag.', + line: 7, + column: 11 + } + ], + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] + }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + errors: [ + { + message: 'Unexpected blank line before this tag.', + line: 6, + column: 11 + } + ], + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] } ] }) From 313fa311ab1ac571f43cecb0ed7db63d903feadd Mon Sep 17 00:00:00 2001 From: dev1437 Date: Fri, 23 Sep 2022 21:55:49 +0800 Subject: [PATCH 2/5] Make consistent only considers blocks with consistent rule --- lib/rules/padding-line-between-tags.js | 16 +++- tests/lib/rules/padding-line-between-tags.js | 82 ++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) diff --git a/lib/rules/padding-line-between-tags.js b/lib/rules/padding-line-between-tags.js index 86472bf50..c643d2ae9 100644 --- a/lib/rules/padding-line-between-tags.js +++ b/lib/rules/padding-line-between-tags.js @@ -95,6 +95,20 @@ function removeExcessLines(context, endTag, sibling, lineDifference) { } } +/** + * @param {VElement} block + * @param {Array<{blankLine: "always" | "never" | "consistent", prev: string, next: string}>} configureList + */ +function hasConsistentConfiguration(block, configureList) { + for (let i = configureList.length - 1; i >= 0; --i) { + const configure = configureList[i]; + if (configure.blankLine !== 'consistent' && configure.prev === "*" || configure.prev === block.name) { + return false; + } + } + return true +} + // ------------------------------------------------------------------------------ // Rule Definition // ------------------------------------------------------------------------------ @@ -147,7 +161,7 @@ function checkNewline(context) { /** @type {VElement | null} */ let prevChild = null const siblingElements = block.parent.children.filter( - (element) => element.type === 'VElement' + (element) => element.type === 'VElement' && hasConsistentConfiguration(element, configureList) ) // get parent for (const child of siblingElements) { diff --git a/tests/lib/rules/padding-line-between-tags.js b/tests/lib/rules/padding-line-between-tags.js index b5fb99c2d..9090e1ae0 100644 --- a/tests/lib/rules/padding-line-between-tags.js +++ b/tests/lib/rules/padding-line-between-tags.js @@ -17,6 +17,33 @@ const tester = new RuleTester({ tester.run('padding-line-between-tags', rule, { valid: [ + { + filename: 'test.vue', + code: ` + + `, + options: [[ + { "blankLine": "consistent", "prev": "*", "next": "*" }, + { "blankLine": "never", "prev": "br", "next": "br" } + ]] + }, { filename: 'test.vue', code: ` @@ -1142,6 +1169,61 @@ tester.run('padding-line-between-tags', rule, { } ], options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] + }, + { + filename: 'test.vue', + code: ` + + `, + output: ` + + `, + errors: [ + { + message: 'Unexpected blank line before this tag.', + line: 6, + column: 11 + }, + { + message: 'Unexpected blank line before this tag.', + line: 8, + column: 11 + }, + { + message: 'Unexpected blank line before this tag.', + line: 11, + column: 11 + } + ], + options: [[ + { "blankLine": "consistent", "prev": "*", "next": "*" }, + { "blankLine": "never", "prev": "br", "next": "br" } + ]] } ] }) From 49ee31143d10bb8683dd83032c0c48c0365be093 Mon Sep 17 00:00:00 2001 From: dev1437 Date: Wed, 28 Sep 2022 21:16:20 +0800 Subject: [PATCH 3/5] Use first element as guide for rest of siblings --- lib/rules/padding-line-between-tags.js | 36 +++---- tests/lib/rules/padding-line-between-tags.js | 100 ++++++++++++++++--- 2 files changed, 98 insertions(+), 38 deletions(-) diff --git a/lib/rules/padding-line-between-tags.js b/lib/rules/padding-line-between-tags.js index c643d2ae9..7f4038f14 100644 --- a/lib/rules/padding-line-between-tags.js +++ b/lib/rules/padding-line-between-tags.js @@ -101,9 +101,12 @@ function removeExcessLines(context, endTag, sibling, lineDifference) { */ function hasConsistentConfiguration(block, configureList) { for (let i = configureList.length - 1; i >= 0; --i) { - const configure = configureList[i]; - if (configure.blankLine !== 'consistent' && configure.prev === "*" || configure.prev === block.name) { - return false; + const configure = configureList[i] + if ( + (configure.blankLine !== 'consistent' && configure.prev === '*') || + configure.prev === block.name + ) { + return false } } return true @@ -156,30 +159,15 @@ function checkNewline(context) { if (configure.blankLine === 'always') { insertNewLine(context, block, closestSibling, lineDifference) } else if (configure.blankLine === 'consistent') { - let newlineCount = 0 - let siblingLineDifference = 0 - /** @type {VElement | null} */ - let prevChild = null const siblingElements = block.parent.children.filter( - (element) => element.type === 'VElement' && hasConsistentConfiguration(element, configureList) + (element) => + element.type === 'VElement' && + hasConsistentConfiguration(element, configureList) ) - // get parent - for (const child of siblingElements) { - if (!prevChild) { - prevChild = /** @type {VElement} */ (child) - continue - } - siblingLineDifference = - child.loc.start.line - prevChild.loc.end.line - - if (siblingLineDifference > 1) { - newlineCount++ - } - prevChild = /** @type {VElement} */ (child) - } - const ratio = newlineCount / (siblingElements.length - 1) - if (0.5 - ratio < 0.005) { + const siblingLineDifference = + siblingElements[1].loc.start.line - siblingElements[0].loc.end.line + if (siblingLineDifference > 1) { insertNewLine(context, block, closestSibling, lineDifference) } else { removeExcessLines(context, endTag, closestSibling, lineDifference) diff --git a/tests/lib/rules/padding-line-between-tags.js b/tests/lib/rules/padding-line-between-tags.js index 9090e1ae0..67aa6b228 100644 --- a/tests/lib/rules/padding-line-between-tags.js +++ b/tests/lib/rules/padding-line-between-tags.js @@ -39,10 +39,36 @@ tester.run('padding-line-between-tags', rule, { `, - options: [[ - { "blankLine": "consistent", "prev": "*", "next": "*" }, - { "blankLine": "never", "prev": "br", "next": "br" } - ]] + options: [ + [ + { blankLine: 'consistent', prev: '*', next: '*' }, + { blankLine: 'never', prev: 'br', next: 'br' } + ] + ] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] + }, + { + filename: 'test.vue', + code: ` + + `, + options: [[{ blankLine: 'consistent', prev: '*', next: '*' }]] }, { filename: 'test.vue', @@ -1151,9 +1177,13 @@ tester.run('padding-line-between-tags', rule, {