From e96f70743fa277a0caa027e13770f0f38d8e7e9d Mon Sep 17 00:00:00 2001 From: doing-art Date: Sat, 1 May 2021 22:50:37 +0300 Subject: [PATCH 01/12] Add autofix to function-calc-no-unspaced-operator --- .../index.js | 80 +++++++++++++++++-- 1 file changed, 74 insertions(+), 6 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index abcc9e8f70..7e51bd2373 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -18,7 +18,7 @@ const messages = ruleMessages(ruleName, { expectedOperatorBeforeSign: (operator) => `Expected an operator before sign "${operator}"`, }); -function rule(actual) { +function rule(actual, secondary, context) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }); @@ -31,6 +31,8 @@ function rule(actual) { } root.walkDecls((decl) => { + const invalidCases = []; + valueParser(decl.value).walk((node) => { if (node.type !== 'function' || node.value.toLowerCase() !== 'calc') { return; @@ -64,7 +66,10 @@ function rule(actual) { }; styleSearch(styleSearchOptions, (match) => { + // let addedSpacesNumber = 0; const index = match.startIndex; + // const symbolIndex = node.sourceIndex + parensMatch.start + index + 1 + addedSpacesNumber; + const symbolIndex = node.sourceIndex + parensMatch.start + index + 1; // Deal with signs. // (@ and $ are considered "digits" here to allow for variable syntaxes @@ -88,10 +93,34 @@ function rule(actual) { return; } - // And if not, complain - complain(messages.expectedOperatorBeforeSign(symbol), decl, expressionIndex + index); + // if (context.fix) { + // if (!checkIfBeforeOk(expression, index)) { + // invalidCases.push(symbolIndex); + // // fixDeclValue(decl, symbolIndex); + // // ++addedSpacesNumber; + // } + // + // if (!checkIfAfterOk(expression, index)) { + // invalidCases.push(symbolIndex + 1); + // // fixDeclValue(decl, symbolIndex + 2); + // // ++addedSpacesNumber; + // } + // } else { + // // And if not, complain + // complain(messages.expectedOperatorBeforeSign(symbol), decl, expressionIndex + index); + // return; + // } + + if (!context.fix) { + // And if not, complain + complain( + messages.expectedOperatorBeforeSign(symbol), + decl, + expressionIndex + index, + ); - return; + return; + } } const beforeOk = @@ -99,7 +128,14 @@ function rule(actual) { newlineBefore(expression, index - 1); if (!beforeOk) { - complain(messages.expectedBefore(symbol), decl, expressionIndex + index); + if (context.fix) { + invalidCases.push(symbolIndex); + // fixDeclValue(decl, symbolIndex); + // ++addedSpacesNumber; + // return; + } else { + complain(messages.expectedBefore(symbol), decl, expressionIndex + index); + } } const afterOk = @@ -108,15 +144,47 @@ function rule(actual) { expression.substr(index + 1, 2) === '\r\n'; if (!afterOk) { - complain(messages.expectedAfter(symbol), decl, expressionIndex + index); + if (context.fix) { + invalidCases.push(symbolIndex + 1); + // fixDeclValue(decl, symbolIndex + 1); + // ++addedSpacesNumber; + // return; + } else { + complain(messages.expectedAfter(symbol), decl, expressionIndex + index); + } } }); } }); + + if (context.fix) { + let insertedSpacesNumber = 0; + + decl.value = invalidCases + .sort((a, b) => a - b) + .reduce((fixedValue, index) => { + return insertSpace(fixedValue, index + insertedSpacesNumber++); + }, decl.value); + } }); }; } +// function checkIfBeforeOk(expression, index) { +// return (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || +// newlineBefore(expression, index - 1) +// } +// +// function checkIfAfterOk(expression, index) { +// return (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || +// expression[index + 1] === '\n' || +// expression.substr(index + 1, 2) === '\r\n' +// } + +function insertSpace(value, index) { + return `${value.substring(0, index) } ${ value.substr(index)}`; +} + function blurVariables(source) { return source.replace(/[$@][^)\s]+|#{.+?}/g, '0'); } From c51b752c08a2abe0d5a277d177104ebc20507921 Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 2 May 2021 09:16:44 +0300 Subject: [PATCH 02/12] Refactor autofix of function-calc-no-unspaced-operator --- .../index.js | 47 ++----------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 7e51bd2373..a0bfd4cab9 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -31,7 +31,7 @@ function rule(actual, secondary, context) { } root.walkDecls((decl) => { - const invalidCases = []; + const indexesToFix = []; valueParser(decl.value).walk((node) => { if (node.type !== 'function' || node.value.toLowerCase() !== 'calc') { @@ -66,9 +66,7 @@ function rule(actual, secondary, context) { }; styleSearch(styleSearchOptions, (match) => { - // let addedSpacesNumber = 0; const index = match.startIndex; - // const symbolIndex = node.sourceIndex + parensMatch.start + index + 1 + addedSpacesNumber; const symbolIndex = node.sourceIndex + parensMatch.start + index + 1; // Deal with signs. @@ -93,24 +91,6 @@ function rule(actual, secondary, context) { return; } - // if (context.fix) { - // if (!checkIfBeforeOk(expression, index)) { - // invalidCases.push(symbolIndex); - // // fixDeclValue(decl, symbolIndex); - // // ++addedSpacesNumber; - // } - // - // if (!checkIfAfterOk(expression, index)) { - // invalidCases.push(symbolIndex + 1); - // // fixDeclValue(decl, symbolIndex + 2); - // // ++addedSpacesNumber; - // } - // } else { - // // And if not, complain - // complain(messages.expectedOperatorBeforeSign(symbol), decl, expressionIndex + index); - // return; - // } - if (!context.fix) { // And if not, complain complain( @@ -129,10 +109,7 @@ function rule(actual, secondary, context) { if (!beforeOk) { if (context.fix) { - invalidCases.push(symbolIndex); - // fixDeclValue(decl, symbolIndex); - // ++addedSpacesNumber; - // return; + indexesToFix.push(symbolIndex); } else { complain(messages.expectedBefore(symbol), decl, expressionIndex + index); } @@ -145,10 +122,7 @@ function rule(actual, secondary, context) { if (!afterOk) { if (context.fix) { - invalidCases.push(symbolIndex + 1); - // fixDeclValue(decl, symbolIndex + 1); - // ++addedSpacesNumber; - // return; + indexesToFix.push(symbolIndex + 1); } else { complain(messages.expectedAfter(symbol), decl, expressionIndex + index); } @@ -160,7 +134,7 @@ function rule(actual, secondary, context) { if (context.fix) { let insertedSpacesNumber = 0; - decl.value = invalidCases + decl.value = indexesToFix .sort((a, b) => a - b) .reduce((fixedValue, index) => { return insertSpace(fixedValue, index + insertedSpacesNumber++); @@ -170,19 +144,8 @@ function rule(actual, secondary, context) { }; } -// function checkIfBeforeOk(expression, index) { -// return (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || -// newlineBefore(expression, index - 1) -// } -// -// function checkIfAfterOk(expression, index) { -// return (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || -// expression[index + 1] === '\n' || -// expression.substr(index + 1, 2) === '\r\n' -// } - function insertSpace(value, index) { - return `${value.substring(0, index) } ${ value.substr(index)}`; + return `${value.substring(0, index)} ${value.substr(index)}`; } function blurVariables(source) { From 93c7385dc3e93058a77accadf03a6773759a74d7 Mon Sep 17 00:00:00 2001 From: doing-art Date: Tue, 4 May 2021 11:44:38 +0300 Subject: [PATCH 03/12] Implement autofix for function-calc-no-unspaced-operator rule --- .../index.js | 110 ++++++++++++++++-- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index a0bfd4cab9..25ff8fe57c 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -31,7 +31,7 @@ function rule(actual, secondary, context) { } root.walkDecls((decl) => { - const indexesToFix = []; + const symbolsToFix = []; valueParser(decl.value).walk((node) => { if (node.type !== 'function' || node.value.toLowerCase() !== 'calc') { @@ -102,6 +102,12 @@ function rule(actual, secondary, context) { return; } } + // 1. Ниодного пробела перед (вставка). + // 2. Несколько пробелов без новой строки перед (удаление). + // 3. Пробельный символ не равный новой строке или пробелу перед (замена). + // 4. Ниодного пробела после (вставка). + // 5. Несколько пробелов без новой строки после (удаление). + // 6. Пробельный символ не равный новой строке или пробелу после (замена). const beforeOk = (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || @@ -109,12 +115,44 @@ function rule(actual, secondary, context) { if (!beforeOk) { if (context.fix) { - indexesToFix.push(symbolIndex); + let step = 1; + + // Remove all whitespace characters before the operator, e.g. `\t` + while (isWhitespace(expression[index - step])) { + symbolsToFix.push({ + index: symbolIndex - step, + insert: false, + }); + + step++; + } + + // Add only one space character + symbolsToFix.push({ + index: symbolIndex, + insert: true, + }); } else { complain(messages.expectedBefore(symbol), decl, expressionIndex + index); } } + // const beforeOk = + // (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || + // isNewlineBefore(expression, index - 1); + // + // if (!beforeOk) { + // if (context.fix) { + // indexesToFix.push(symbolIndex); + // } else { + // complain(messages.expectedBefore(symbol), decl, expressionIndex + index); + // } + // } + + // 1. +a + // 2. +\ta + // 3. + \ta + // 4. + \na const afterOk = (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || expression[index + 1] === '\n' || @@ -122,28 +160,84 @@ function rule(actual, secondary, context) { if (!afterOk) { if (context.fix) { - indexesToFix.push(symbolIndex + 1); + let step = 1; + let spaceNeeded = true; + + // Remove all whitespace characters before the operator or \n, e.g. \t + while (isWhitespace(expression[index + step])) { + if ( + expression[index + step] === '\n' || + expression.substr(index + 1, 2) === '\r\n' + ) { + spaceNeeded = false; + break; + } + + symbolsToFix.push({ + index: symbolIndex + step, + insert: false, + }); + step++; + } + + // Insert one space character if there is no \n + if (spaceNeeded) { + symbolsToFix.push({ + index: symbolIndex + 1, + insert: true, + }); + } } else { complain(messages.expectedAfter(symbol), decl, expressionIndex + index); } } + + // const afterOk = + // (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || + // expression[index + 1] === '\n' || + // expression.substr(index + 1, 2) === '\r\n'; + // + // if (!afterOk) { + // if (context.fix) { + // indexesToFix.push(symbolIndex + 1); + // } else { + // complain(messages.expectedAfter(symbol), decl, expressionIndex + index); + // } + // } }); } }); if (context.fix) { - let insertedSpacesNumber = 0; + // let valueLengthChanges = 0; + + decl.value = symbolsToFix + // .sort((a, b) => a.index - b.index) + .reduce((fixedValue, { insert, index }) => { + shiftIndexes(symbolsToFix, index, insert); - decl.value = indexesToFix - .sort((a, b) => a - b) - .reduce((fixedValue, index) => { - return insertSpace(fixedValue, index + insertedSpacesNumber++); + return insert ? insertSpace(fixedValue, index) : removeSpace(fixedValue, index); + // return insertSpace(fixedValue, index + valueLengthChanges++); + + // return removeSpace(fixedValue, index + valueLengthChanges--); }, decl.value); } }); }; } +function shiftIndexes(symbolsToFix, index, insert) { + symbolsToFix.forEach((symbol) => { + if (symbol.index > index) { + symbol.index += insert ? 1 : -1; + } + }); +} + +function removeSpace(value, index) { + return value.slice(0, index) + value.slice(index + 1, value.length); +} + function insertSpace(value, index) { return `${value.substring(0, index)} ${value.substr(index)}`; } From 7633208d8eebea70a373aaf179354f0a90cabb46 Mon Sep 17 00:00:00 2001 From: doing-art Date: Tue, 4 May 2021 11:59:46 +0300 Subject: [PATCH 04/12] Refactor autofix for function-calc-no-unspaced-operator rule --- .../index.js | 50 ++----------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 25ff8fe57c..2e42bff429 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -102,12 +102,6 @@ function rule(actual, secondary, context) { return; } } - // 1. Ниодного пробела перед (вставка). - // 2. Несколько пробелов без новой строки перед (удаление). - // 3. Пробельный символ не равный новой строке или пробелу перед (замена). - // 4. Ниодного пробела после (вставка). - // 5. Несколько пробелов без новой строки после (удаление). - // 6. Пробельный символ не равный новой строке или пробелу после (замена). const beforeOk = (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || @@ -137,22 +131,6 @@ function rule(actual, secondary, context) { } } - // const beforeOk = - // (expression[index - 1] === ' ' && !isWhitespace(expression[index - 2])) || - // isNewlineBefore(expression, index - 1); - // - // if (!beforeOk) { - // if (context.fix) { - // indexesToFix.push(symbolIndex); - // } else { - // complain(messages.expectedBefore(symbol), decl, expressionIndex + index); - // } - // } - - // 1. +a - // 2. +\ta - // 3. + \ta - // 4. + \na const afterOk = (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || expression[index + 1] === '\n' || @@ -191,36 +169,16 @@ function rule(actual, secondary, context) { complain(messages.expectedAfter(symbol), decl, expressionIndex + index); } } - - // const afterOk = - // (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || - // expression[index + 1] === '\n' || - // expression.substr(index + 1, 2) === '\r\n'; - // - // if (!afterOk) { - // if (context.fix) { - // indexesToFix.push(symbolIndex + 1); - // } else { - // complain(messages.expectedAfter(symbol), decl, expressionIndex + index); - // } - // } }); } }); if (context.fix) { - // let valueLengthChanges = 0; - - decl.value = symbolsToFix - // .sort((a, b) => a.index - b.index) - .reduce((fixedValue, { insert, index }) => { - shiftIndexes(symbolsToFix, index, insert); - - return insert ? insertSpace(fixedValue, index) : removeSpace(fixedValue, index); - // return insertSpace(fixedValue, index + valueLengthChanges++); + decl.value = symbolsToFix.reduce((fixedValue, { insert, index }) => { + shiftIndexes(symbolsToFix, index, insert); - // return removeSpace(fixedValue, index + valueLengthChanges--); - }, decl.value); + return insert ? insertSpace(fixedValue, index) : removeSpace(fixedValue, index); + }, decl.value); } }); }; From b4ac03704c55c3ca6a6c638ca48601d4bfeefcb6 Mon Sep 17 00:00:00 2001 From: doing-art Date: Tue, 4 May 2021 13:01:03 +0300 Subject: [PATCH 05/12] Fix tests of function-calc-no-unspaced-operator rule --- .../__tests__/index.js | 103 ++++++++++++++++++ .../index.js | 2 +- 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js index 67c8ef1979..287814f6a8 100644 --- a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js @@ -5,6 +5,7 @@ const { messages, ruleName } = require('..'); testRule({ ruleName, config: [true], + fix: true, accept: [ { @@ -153,6 +154,7 @@ testRule({ reject: [ { code: 'a { top: calc(1px +\t-1px)}', + fixed: 'a { top: calc(1px + -1px)}', description: 'tab before sign after operator', message: messages.expectedAfter('+'), line: 1, @@ -160,6 +162,7 @@ testRule({ }, { code: 'a { top: calc(1px + -1px)}', + fixed: 'a { top: calc(1px + -1px)}', description: 'multiple spaces before sign after operator', message: messages.expectedAfter('+'), line: 1, @@ -167,102 +170,119 @@ testRule({ }, { code: 'a { top: calc(1px+ 2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 18, }, { code: 'a { top: cAlC(1px+ 2px); }', + fixed: 'a { top: cAlC(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 18, }, { code: 'a { top: CALC(1px+ 2px); }', + fixed: 'a { top: CALC(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 18, }, { code: 'a { top: calc(1px + 2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 20, }, { code: 'a { top: calc(1px\t+ 2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 19, }, { code: 'a { top: calc(1px + 2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedAfter('+'), line: 1, column: 19, }, { code: 'a { top: calc(1px +\t2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedAfter('+'), line: 1, column: 19, }, { code: 'a { top: calc(1px- 2px); }', + fixed: 'a { top: calc(1px - 2px); }', message: messages.expectedBefore('-'), line: 1, column: 18, }, { code: 'a { top: calc(1px* 2); }', + fixed: 'a { top: calc(1px * 2); }', message: messages.expectedBefore('*'), line: 1, column: 18, }, { code: 'a { top: calc(1px *2); }', + fixed: 'a { top: calc(1px * 2); }', message: messages.expectedAfter('*'), line: 1, column: 19, }, { code: 'a { top: calc(1px/ 2); }', + fixed: 'a { top: calc(1px / 2); }', message: messages.expectedBefore('/'), line: 1, column: 18, }, { code: 'a { top: calc(1px /2); }', + fixed: 'a { top: calc(1px / 2); }', message: messages.expectedAfter('/'), line: 1, column: 19, }, { code: 'a { top: calc(calc(1px* 2px) + 3px); }', + fixed: 'a { top: calc(calc(1px * 2px) + 3px); }', message: messages.expectedBefore('*'), line: 1, column: 23, }, { code: 'a { top: calc(calc(1px + 2px)* 3px); }', + fixed: 'a { top: calc(calc(1px + 2px) * 3px); }', message: messages.expectedBefore('*'), line: 1, column: 30, }, { code: 'a { top: calc(1px +2px); }', + fixed: 'a { top: calc(1px + 2px); }', message: messages.expectedOperatorBeforeSign('+'), line: 1, column: 19, }, { code: 'a { top: calc(1px -2px); }', + fixed: 'a { top: calc(1px - 2px); }', message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 19, }, { code: 'a { padding: 10px calc(1px +\t-1px)}', + fixed: 'a { padding: 10px calc(1px + -1px)}', description: 'tab before sign after operator', message: messages.expectedAfter('+'), line: 1, @@ -270,6 +290,7 @@ testRule({ }, { code: 'a { padding: 10px calc(1px + -1px)}', + fixed: 'a { padding: 10px calc(1px + -1px)}', description: 'multiple spaces before sign after operator', message: messages.expectedAfter('+'), line: 1, @@ -277,88 +298,166 @@ testRule({ }, { code: 'a { padding: 10px calc(1px+ 2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 27, }, { code: 'a { padding: 10px calc(1px + 2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 29, }, { code: 'a { padding: 10px calc(1px\t+ 2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedBefore('+'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(1px + 2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedAfter('+'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(1px +\t2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedAfter('+'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(1px- 2px); }', + fixed: 'a { padding: 10px calc(1px - 2px); }', message: messages.expectedBefore('-'), line: 1, column: 27, }, { code: 'a { padding: 10px calc(1px* 2); }', + fixed: 'a { padding: 10px calc(1px * 2); }', message: messages.expectedBefore('*'), line: 1, column: 27, }, { code: 'a { padding: 10px calc(1px *2); }', + fixed: 'a { padding: 10px calc(1px * 2); }', message: messages.expectedAfter('*'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(1px/ 2); }', + fixed: 'a { padding: 10px calc(1px / 2); }', message: messages.expectedBefore('/'), line: 1, column: 27, }, { code: 'a { padding: 10px calc(1px /2); }', + fixed: 'a { padding: 10px calc(1px / 2); }', message: messages.expectedAfter('/'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(calc(1px* 2px) + 3px); }', + fixed: 'a { padding: 10px calc(calc(1px * 2px) + 3px); }', message: messages.expectedBefore('*'), line: 1, column: 32, }, { code: 'a { padding: 10px calc(calc(1px + 2px)* 3px); }', + fixed: 'a { padding: 10px calc(calc(1px + 2px) * 3px); }', message: messages.expectedBefore('*'), line: 1, column: 39, }, { code: 'a { padding: 10px calc(1px +2px); }', + fixed: 'a { padding: 10px calc(1px + 2px); }', message: messages.expectedOperatorBeforeSign('+'), line: 1, column: 28, }, { code: 'a { padding: 10px calc(1px -2px); }', + fixed: 'a { padding: 10px calc(1px - 2px); }', message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 28, }, + { + code: 'a { padding: calc(1rem\t + 1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several whitespace characters before operator starting from space', + message: messages.expectedBefore('+'), + line: 1, + column: 25, + }, + { + code: 'a { padding: calc(1rem \t+ 1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several whitespace characters before operator starting from tab', + message: messages.expectedBefore('+'), + line: 1, + column: 26, + }, + { + code: 'a { padding: calc(1rem\t\f\r\t+ 1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several incorrect whitespace characters before operator', + message: messages.expectedBefore('+'), + line: 1, + column: 27, + }, + { + code: 'a { padding: calc(1rem + \t1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several whitespace characters after operator starting from space', + message: messages.expectedAfter('+'), + line: 1, + column: 24, + }, + { + code: 'a { padding: calc(1rem +\t \t1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several whitespace characters after operator starting from tab', + message: messages.expectedAfter('+'), + line: 1, + column: 24, + }, + { + code: 'a { padding: calc(1rem +\t\r\f\t1em)}', + fixed: 'a { padding: calc(1rem + 1em)}', + description: 'several incorrect whitespace characters after operator', + message: messages.expectedAfter('+'), + line: 1, + column: 24, + }, + { + code: 'a { padding: calc(1rem +\t \t\f\n\f\t1em)}', + fixed: 'a { padding: calc(1rem +\n\f\t1em)}', + description: 'several whitespace characters after operator but before the \\n', + message: messages.expectedAfter('+'), + line: 1, + column: 24, + }, + { + code: 'a { padding: calc(1rem + \t\r\n 1em)}', + fixed: 'a { padding: calc(1rem +\r\n 1em)}', + description: 'several whitespace characters after operator but before the \\r\\n', + message: messages.expectedAfter('+'), + line: 1, + column: 24, + }, ], }); @@ -366,22 +465,26 @@ testRule({ ruleName, config: [true], syntax: 'scss', + fix: true, reject: [ { code: 'a { top: calc(100%- #{$foo}); }', + fixed: 'a { top: calc(100% - #{$foo}); }', message: messages.expectedBefore('-'), line: 1, column: 19, }, { code: 'a { top: calc(100% *#{$foo}); }', + fixed: 'a { top: calc(100% * #{$foo}); }', message: messages.expectedAfter('*'), line: 1, column: 20, }, { code: 'a { top: calc(100% -#{$foo}); }', + fixed: 'a { top: calc(100% - #{$foo}); }', message: messages.expectedOperatorBeforeSign('-'), line: 1, column: 20, diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 2e42bff429..0ec5896cf1 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -145,7 +145,7 @@ function rule(actual, secondary, context) { while (isWhitespace(expression[index + step])) { if ( expression[index + step] === '\n' || - expression.substr(index + 1, 2) === '\r\n' + expression.substr(index + step, 2) === '\r\n' ) { spaceNeeded = false; break; From 1573ef495cb8a785dfaf965a3c12b2a572fe434e Mon Sep 17 00:00:00 2001 From: doing-art Date: Tue, 4 May 2021 13:05:39 +0300 Subject: [PATCH 06/12] Update documentation of function-calc-no-unspaced-operator rule --- lib/rules/function-calc-no-unspaced-operator/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/rules/function-calc-no-unspaced-operator/README.md b/lib/rules/function-calc-no-unspaced-operator/README.md index 6a68f443cb..d6d00e8f39 100644 --- a/lib/rules/function-calc-no-unspaced-operator/README.md +++ b/lib/rules/function-calc-no-unspaced-operator/README.md @@ -11,6 +11,8 @@ a { top: calc(1px + 2px); } Before the operator, there must be a single whitespace or a newline plus indentation. After the operator, there must be a single whitespace or a newline. +The [`fix` option](../../../docs/user-guide/usage/options.md#fix) can automatically fix all of the problems reported by this rule. + ## Options ### `true` From 2e59c2658017f913a95673e4423db328931607e7 Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 16 May 2021 20:58:24 +0300 Subject: [PATCH 07/12] Update lib/rules/function-calc-no-unspaced-operator/index.js Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- lib/rules/function-calc-no-unspaced-operator/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 0ec5896cf1..781fbc2af0 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -177,7 +177,9 @@ function rule(actual, secondary, context) { decl.value = symbolsToFix.reduce((fixedValue, { insert, index }) => { shiftIndexes(symbolsToFix, index, insert); - return insert ? insertSpace(fixedValue, index) : removeSpace(fixedValue, index); + return insert + ? insertCharAtIndex(fixedValue, index, ' ') + : removeCharAtIndex(fixedValue, index); }, decl.value); } }); From 6d3494abd7a87e6b8f30d4be74d3b079a377339b Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 16 May 2021 20:58:32 +0300 Subject: [PATCH 08/12] Update lib/rules/function-calc-no-unspaced-operator/index.js Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- lib/rules/function-calc-no-unspaced-operator/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 781fbc2af0..4a99332618 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -194,12 +194,12 @@ function shiftIndexes(symbolsToFix, index, insert) { }); } -function removeSpace(value, index) { - return value.slice(0, index) + value.slice(index + 1, value.length); +function removeCharAtIndex(str, index) { + return str.slice(0, index) + str.slice(index + 1, str.length); } -function insertSpace(value, index) { - return `${value.substring(0, index)} ${value.substr(index)}`; +function insertCharAtIndex(str, index, char) { + return str.slice(0, index) + char + str.slice(index, str.length); } function blurVariables(source) { From c97021b542911696b335ca58b06d07cc86208924 Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 16 May 2021 21:01:03 +0300 Subject: [PATCH 09/12] Update lib/rules/function-calc-no-unspaced-operator/index.js Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- lib/rules/function-calc-no-unspaced-operator/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index 4a99332618..e50562287d 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -186,6 +186,10 @@ function rule(actual, secondary, context) { }; } +function isNewlineAtIndex(str, index) { + return str[index] === '\n' || str.slice(index, index + 2) === '\r\n'; +} + function shiftIndexes(symbolsToFix, index, insert) { symbolsToFix.forEach((symbol) => { if (symbol.index > index) { From d5a479c228b810b2b8f5e641e61b489d8f679024 Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 16 May 2021 21:01:33 +0300 Subject: [PATCH 10/12] Update lib/rules/function-calc-no-unspaced-operator/index.js Co-authored-by: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> --- lib/rules/function-calc-no-unspaced-operator/index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index e50562287d..c38e18295a 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -143,10 +143,7 @@ function rule(actual, secondary, context) { // Remove all whitespace characters before the operator or \n, e.g. \t while (isWhitespace(expression[index + step])) { - if ( - expression[index + step] === '\n' || - expression.substr(index + step, 2) === '\r\n' - ) { + if (isNewlineAtIndex(expression, index + step)) { spaceNeeded = false; break; } From 117768566c9a70c934ebc05b5443c9afec26603e Mon Sep 17 00:00:00 2001 From: doing-art Date: Sun, 16 May 2021 21:15:08 +0300 Subject: [PATCH 11/12] Refactor autofix of function-calc-no-unspaced-operator --- lib/rules/function-calc-no-unspaced-operator/index.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js index c38e18295a..e8ec9106e8 100644 --- a/lib/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -74,7 +74,7 @@ function rule(actual, secondary, context) { // that permit signs in front of variables, e.g. `-$number`) // As is "." to deal with fractional numbers without a leading zero if ((symbol === '+' || symbol === '-') && /[\d@$.]/.test(expression[index + 1])) { - const expressionBeforeSign = expression.substr(0, index); + const expressionBeforeSign = expression.slice(0, index); // Ignore signs that directly follow a opening bracket if (expressionBeforeSign[expressionBeforeSign.length - 1] === '(') { @@ -133,8 +133,7 @@ function rule(actual, secondary, context) { const afterOk = (expression[index + 1] === ' ' && !isWhitespace(expression[index + 2])) || - expression[index + 1] === '\n' || - expression.substr(index + 1, 2) === '\r\n'; + isNewlineAtIndex(expression, index + 1); if (!afterOk) { if (context.fix) { From e5c45be60ae0bf28a71bf9db6dbe51f83a9583db Mon Sep 17 00:00:00 2001 From: doing-art Date: Mon, 17 May 2021 15:59:42 +0300 Subject: [PATCH 12/12] Fix ESLint issue --- lib/rules/function-calc-no-unspaced-operator/__tests__/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js index 2c833f8f03..2bf9083171 100644 --- a/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js @@ -467,7 +467,7 @@ testRule({ ruleName, config: [true], customSyntax: postcssScss, - fix: true, + fix: true, reject: [ {