Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix conflicts with arrow-body-style(solution 2) #381

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 46 additions & 12 deletions eslint-plugin-prettier.js
Expand Up @@ -31,22 +31,35 @@ let prettier;
// Rule Definition
// ------------------------------------------------------------------------------

/**
* Extend fix range to prevent changes from other rules.
* @param {ruleFixer} fixer - The fixer to fix.
* @param {int[]} range - The extended range node.
*/
function* extendFixRange(fixer, range) {
yield fixer.insertTextBeforeRange(range, '');
yield fixer.insertTextAfterRange(range, '');
}

/**
* Reports an "Insert ..." issue where text must be inserted.
* @param {RuleContext} context - The ESLint rule context.
* @param {number} offset - The source offset where to insert text.
* @param {string} text - The text to be inserted.
* @param {boolean} experimentalFix - Use the new way to fix code.
* @returns {void}
*/
function reportInsert(context, offset, text) {
function reportInsert(context, offset, text, experimentalFix) {
const pos = context.getSourceCode().getLocFromIndex(offset);
const range = [offset, offset];
context.report({
message: 'Insert `{{ code }}`',
data: { code: showInvisibles(text) },
loc: { start: pos, end: pos },
fix(fixer) {
return fixer.insertTextAfterRange(range, text);
*fix(fixer) {
yield fixer.insertTextAfterRange(range, text);
if (experimentalFix)
yield* extendFixRange(fixer, [0, context.getSourceCode().text.length]);
}
});
}
Expand All @@ -56,18 +69,21 @@ function reportInsert(context, offset, text) {
* @param {RuleContext} context - The ESLint rule context.
* @param {number} offset - The source offset where to delete text.
* @param {string} text - The text to be deleted.
* @param {boolean} experimentalFix - Use the new way to fix code.
* @returns {void}
*/
function reportDelete(context, offset, text) {
function reportDelete(context, offset, text, experimentalFix) {
const start = context.getSourceCode().getLocFromIndex(offset);
const end = context.getSourceCode().getLocFromIndex(offset + text.length);
const range = [offset, offset + text.length];
context.report({
message: 'Delete `{{ code }}`',
data: { code: showInvisibles(text) },
loc: { start, end },
fix(fixer) {
return fixer.removeRange(range);
*fix(fixer) {
yield fixer.removeRange(range);
if (experimentalFix)
yield* extendFixRange(fixer, [0, context.getSourceCode().text.length]);
}
});
}
Expand All @@ -79,9 +95,16 @@ function reportDelete(context, offset, text) {
with inserted text.
* @param {string} deleteText - The text to be deleted.
* @param {string} insertText - The text to be inserted.
* @param {boolean} experimentalFix - Use the new way to fix code.
* @returns {void}
*/
function reportReplace(context, offset, deleteText, insertText) {
function reportReplace(
context,
offset,
deleteText,
insertText,
experimentalFix
) {
const start = context.getSourceCode().getLocFromIndex(offset);
const end = context
.getSourceCode()
Expand All @@ -94,8 +117,10 @@ function reportReplace(context, offset, deleteText, insertText) {
insertCode: showInvisibles(insertText)
},
loc: { start, end },
fix(fixer) {
return fixer.replaceTextRange(range, insertText);
*fix(fixer) {
yield fixer.replaceTextRange(range, insertText);
if (experimentalFix)
yield* extendFixRange(fixer, [0, context.getSourceCode().text.length]);
}
});
}
Expand Down Expand Up @@ -139,6 +164,10 @@ module.exports = {
type: 'object',
properties: {},
additionalProperties: true
},
experimentalFix: {
type: 'boolean',
default: false
}
},
additionalProperties: true
Expand All @@ -150,6 +179,8 @@ module.exports = {
!context.options[1] || context.options[1].usePrettierrc !== false;
const eslintFileInfoOptions =
(context.options[1] && context.options[1].fileInfoOptions) || {};
const experimentalFix =
context.options[1] && context.options[1].experimentalFix === true;
const sourceCode = context.getSourceCode();
const filepath = context.getFilename();
const source = sourceCode.text;
Expand Down Expand Up @@ -269,22 +300,25 @@ module.exports = {
reportInsert(
context,
difference.offset,
difference.insertText
difference.insertText,
experimentalFix
);
break;
case DELETE:
reportDelete(
context,
difference.offset,
difference.deleteText
difference.deleteText,
experimentalFix
);
break;
case REPLACE:
reportReplace(
context,
difference.offset,
difference.deleteText,
difference.insertText
difference.insertText,
experimentalFix
);
break;
}
Expand Down
27 changes: 27 additions & 0 deletions test/invalid/19.txt
@@ -0,0 +1,27 @@
CODE:
a = 1.0000;
b = 2.00000000;

OUTPUT:
a = 1.0;
b = 2.00000000;

OPTIONS:
[{}, {experimentalFix: true}]

ERRORS:
[
{
message: 'Delete `000`',
line: 1, column: 8, endLine: 1, endColumn: 11
},
{
message: 'Delete `0000000`',
line: 2, column: 7, endLine: 2, endColumn: 14
},
]


FILENAME:
experimental-fix.js

22 changes: 22 additions & 0 deletions test/invalid/20.txt
@@ -0,0 +1,22 @@
CODE:
a = 1.0;
b = 2.00000000;

OUTPUT:
a = 1.0;
b = 2.0;

OPTIONS:
[{}, {experimentalFix: true}]

ERRORS:
[
{
message: 'Delete `0000000`',
line: 2, column: 8, endLine: 2, endColumn: 15
},
]


FILENAME:
experimental-fix.js
62 changes: 60 additions & 2 deletions test/prettier.js
Expand Up @@ -14,11 +14,13 @@

const fs = require('fs');
const path = require('path');
const assert = require('assert');

const eslintPluginPrettier = require('..');

const rule = eslintPluginPrettier.rules.prettier;
const RuleTester = require('eslint').RuleTester;
const eslint = require('eslint');
const RuleTester = eslint.RuleTester;

// ------------------------------------------------------------------------------
// Tests
Expand Down Expand Up @@ -87,7 +89,9 @@ ruleTester.run('prettier', rule, {
'15',
'16',
'17',
'18'
'18',
'19',
'20'
].map(loadInvalidFixture)
});

Expand All @@ -112,6 +116,60 @@ vueRuleTester.run('prettier', rule, {
]
});

it('Should fix conflicts with `arrow-body-style` rule', () => {
const linter = new eslint.Linter();
linter.defineRules({
'arrow-body-style': require('eslint/lib/rules/arrow-body-style'),
'prettier/prettier': rule
});
const code = `
const foo = a => a.map(b => {
return b.map(
c => {
return {};
}
);
});
`;
const output =
`
const foo = a =>
a.map(b =>
b.map(c => ({})
);
`.trim() + '\n';
const outputWithExperimentalFix =
`
const foo = a => a.map(b => b.map(c => ({})));
`.trim() + '\n';
const [result, resultWithExperimentalFix] = [false, true].map(
experimentalFix =>
linter.verifyAndFix(code, {
parserOptions: { ecmaVersion: 2015 },
rules: {
'arrow-body-style': ['error'],
'prettier/prettier': ['error', {}, { experimentalFix }]
}
})
);
assert.strictEqual(result.output, output);
assert.deepStrictEqual(result.messages, [
{
column: 4,
fatal: true,
line: 4,
message: 'Parsing error: Unexpected token ;',
ruleId: null,
severity: 2
}
]);
assert.strictEqual(
resultWithExperimentalFix.output,
outputWithExperimentalFix
);
assert.deepStrictEqual(resultWithExperimentalFix.messages, []);
});

// ------------------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------------------
Expand Down