Exclude node at rangeStart (#9704)

This commit is contained in:
fisker Cheung authored and GitHub committed 2020-11-30 12:29:48 +08:00
1 parent 902d524d2f
commit 05d20a63a4
11 files changed
+245 -29

No files matched your search

+19
View File
@@ -0,0 +1,19 @@
#### Fix node ends at `rangeStart` was formatted (#9704 by @fisker)
Previously a node ends at `rangeStart` in considered in range, now it's excluded.
<!-- prettier-ignore -->
```js
// Prettier stable
foo = 1.0000;bar = 1.0000;baz=1.0000;
^^^^^^^^^^^^^ Range
// Prettier stable
foo = 1.0;
bar = 1.0;baz=1.0000;
// Prettier master
foo = 1.0000;bar = 1.0;baz=1.0000;
```
_This effect other languages support range format too._
+7
View File
@@ -278,6 +278,13 @@ function format(originalText, originalOptions) {
normalizeOptions(originalOptions) normalizeOptions(originalOptions)
); );
if (options.rangeStart >= options.rangeEnd && text !== "") {
return {
formatted: originalText,
cursorOffset: originalOptions.cursorOffset,
};
}
const selectedParser = parser.resolveParser(options); const selectedParser = parser.resolveParser(options);
const hasPragma = !selectedParser.hasPragma || selectedParser.hasPragma(text); const hasPragma = !selectedParser.hasPragma || selectedParser.hasPragma(text);
if (options.requirePragma && !hasPragma) { if (options.requirePragma && !hasPragma) {
+49 -27
View File
@@ -1,5 +1,6 @@
"use strict"; "use strict";
const assert = require("assert");
const comments = require("./comments"); const comments = require("./comments");
function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) { function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) {
@@ -43,8 +44,24 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) {
}; };
} }
function findNodeAtOffset(node, offset, options, predicate, parentNodes = []) { function findNodeAtOffset(
if (offset < options.locStart(node) || offset > options.locEnd(node)) { node,
offset,
options,
predicate,
parentNodes = [],
type
) {
const { locStart, locEnd } = options;
const start = locStart(node);
const end = locEnd(node);
if (
offset > end ||
offset < start ||
(type === "rangeEnd" && offset === start) ||
(type === "rangeStart" && offset === end)
) {
return; return;
} }
@@ -54,7 +71,8 @@ function findNodeAtOffset(node, offset, options, predicate, parentNodes = []) {
offset, offset,
options, options,
predicate, predicate,
[node, ...parentNodes] [node, ...parentNodes],
type
); );
if (childResult) { if (childResult) {
return childResult; return childResult;
@@ -133,37 +151,41 @@ function isSourceElement(opts, node) {
} }
function calculateRange(text, opts, ast) { function calculateRange(text, opts, ast) {
let { rangeStart: start, rangeEnd: end, locStart, locEnd } = opts;
assert.ok(end > start);
// Contract the range so that it has non-whitespace characters at its endpoints. // Contract the range so that it has non-whitespace characters at its endpoints.
// This ensures we can format a range that doesn't end on a node. // This ensures we can format a range that doesn't end on a node.
const rangeStringOrig = text.slice(opts.rangeStart, opts.rangeEnd); const firstNonWhitespaceCharacterIndex = text.slice(start, end).search(/\S/);
const startNonWhitespace = Math.max( const isAllWhitespace = firstNonWhitespaceCharacterIndex === -1;
opts.rangeStart + rangeStringOrig.search(/\S/), if (!isAllWhitespace) {
opts.rangeStart start += firstNonWhitespaceCharacterIndex;
); for (; end > start; --end) {
let endNonWhitespace; if (/\S/.test(text[end - 1])) {
for ( break;
endNonWhitespace = opts.rangeEnd; }
endNonWhitespace > opts.rangeStart;
--endNonWhitespace
) {
if (/\S/.test(text[endNonWhitespace - 1])) {
break;
} }
} }
const startNodeAndParents = findNodeAtOffset( const startNodeAndParents = findNodeAtOffset(
ast, ast,
startNonWhitespace, start,
opts, opts,
(node) => isSourceElement(opts, node) (node) => isSourceElement(opts, node),
[],
"rangeStart"
); );
const endNodeAndParents = findNodeAtOffset( const endNodeAndParents =
ast, // No need find Node at `end`, it will be the same as `startNodeAndParents`
endNonWhitespace, isAllWhitespace
opts, ? startNodeAndParents
(node) => isSourceElement(opts, node) : findNodeAtOffset(
); ast,
end,
opts,
(node) => isSourceElement(opts, node),
[],
"rangeEnd"
);
if (!startNodeAndParents || !endNodeAndParents) { if (!startNodeAndParents || !endNodeAndParents) {
return { return {
rangeStart: 0, rangeStart: 0,
@@ -178,8 +200,8 @@ function calculateRange(text, opts, ast) {
); );
return { return {
rangeStart: Math.min(opts.locStart(startNode), opts.locStart(endNode)), rangeStart: Math.min(locStart(startNode), locStart(endNode)),
rangeEnd: Math.max(opts.locEnd(startNode), opts.locEnd(endNode)), rangeEnd: Math.max(locEnd(startNode), locEnd(endNode)),
}; };
} }
@@ -26,6 +26,88 @@ a = [, , , , , , , a];
================================================================================ ================================================================================
`; `;
exports[`boundary.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
rangeEnd: 26
rangeStart: 13
| | printWidth
=====================================input======================================
> 1 | foo = 1.0000;bar = 1.0000;baz=1.0000;
| ^^^^^^^^^^^^^
2 | // The range will be 13~26
3 | // \`foo\` ends at 13, should not format
4 | // \`bar\` ends at 26, should format
5 |
=====================================output=====================================
foo = 1.0000;bar = 1.0;baz=1.0000;
// The range will be 13~26
// \`foo\` ends at 13, should not format
// \`bar\` ends at 26, should format
================================================================================
`;
exports[`boundary-2.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
rangeEnd: 45
rangeStart: 28
| | printWidth
=====================================input======================================
1 | function a(
2 | ){
3 | a (
4 | );
5 | b (
> 6 | ); c (
| ^^^^^^^^^^^^^^^^^
7 | ); d(
8 | );
9 |
10 | }
11 |
=====================================output=====================================
function a(
){
a();
b();
c();
d();
}
================================================================================
`;
exports[`boundary-3.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
rangeEnd: 30
rangeStart: 13
| | printWidth
=====================================input======================================
1 | a (
2 | );
3 | b (
> 4 | ); c (
| ^^^^^^^^^^^^^^^^^
5 | ); d(
6 | );
7 |
=====================================output=====================================
a (
);
b (
); c (
); d(
);
================================================================================
`;
exports[`class-declaration.js format 1`] = ` exports[`class-declaration.js format 1`] = `
====================================options===================================== ====================================options=====================================
parsers: ["babel", "flow", "typescript"] parsers: ["babel", "flow", "typescript"]
@@ -851,6 +933,44 @@ call(
================================================================================ ================================================================================
`; `;
exports[`reversed-range.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
rangeEnd: 13
rangeStart: 26
| | printWidth
=====================================input======================================
> 1 | foo = 1.0000;bar = 1.0000;baz=1.0000;
| ^^^^^^^^^^^^^ [Reversed range]
2 | // The range will be 26~13, should not format anything
3 |
=====================================output=====================================
foo = 1.0000;bar = 1.0000;baz=1.0000;
// The range will be 26~13, should not format anything
================================================================================
`;
exports[`start-equals-end.js format 1`] = `
====================================options=====================================
parsers: ["babel", "flow", "typescript"]
printWidth: 80
rangeEnd: 13
rangeStart: 13
| | printWidth
=====================================input======================================
> 1 | foo = 1.0000;bar = 1.0000;baz=1.0000;
| ^
2 | // The range will be 13~13, should not format anything
3 |
=====================================output=====================================
foo = 1.0000;bar = 1.0000;baz=1.0000;
// The range will be 13~13, should not format anything
================================================================================
`;
exports[`try-catch.js format 1`] = ` exports[`try-catch.js format 1`] = `
====================================options===================================== ====================================options=====================================
parsers: ["babel", "flow", "typescript"] parsers: ["babel", "flow", "typescript"]
+10
View File
@@ -0,0 +1,10 @@
function a(
){
a (
);
b (
);<<<PRETTIER_RANGE_START>>> <<<PRETTIER_RANGE_END>>>c (
); d(
);
}
+6
View File
@@ -0,0 +1,6 @@
a (
);
b (
);<<<PRETTIER_RANGE_START>>> <<<PRETTIER_RANGE_END>>>c (
); d(
);
+4
View File
@@ -0,0 +1,4 @@
foo = 1.0000;<<<PRETTIER_RANGE_START>>>bar = 1.0000;<<<PRETTIER_RANGE_END>>>baz=1.0000;
// The range will be 13~26
// `foo` ends at 13, should not format
// `bar` ends at 26, should format
+2
View File
@@ -0,0 +1,2 @@
foo = 1.0000;<<<PRETTIER_RANGE_END>>>bar = 1.0000;<<<PRETTIER_RANGE_START>>>baz=1.0000;
// The range will be 26~13, should not format anything
+2
View File
@@ -0,0 +1,2 @@
foo = 1.0000;<<<PRETTIER_RANGE_START>>><<<PRETTIER_RANGE_END>>>bar = 1.0000;baz=1.0000;
// The range will be 13~13, should not format anything
+20 -1
View File
@@ -334,7 +334,7 @@ function runTest({
}); });
} }
if (!code.includes("\r") && !formatOptions.requirePragma) { if (!shouldSkipEolTest(code, formatResult.options)) {
for (const eol of ["\r\n", "\r"]) { for (const eol of ["\r\n", "\r"]) {
test(`[${parser}] EOL ${JSON.stringify(eol)}`, () => { test(`[${parser}] EOL ${JSON.stringify(eol)}`, () => {
const output = format(code.replace(/\n/g, eol), formatOptions) const output = format(code.replace(/\n/g, eol), formatOptions)
@@ -361,6 +361,25 @@ function runTest({
} }
} }
function shouldSkipEolTest(code, options) {
if (code.includes("\r")) {
return true;
}
const { requirePragma, rangeStart, rangeEnd } = options;
if (requirePragma) {
return true;
}
if (
typeof rangeStart === "number" &&
typeof rangeEnd === "number" &&
rangeStart >= rangeEnd
) {
return true;
}
return false;
}
function parse(source, options) { function parse(source, options) {
return prettier.__debug.parse(source, options, /* massage */ true).ast; return prettier.__debug.parse(source, options, /* massage */ true).ast;
} }
+6 -1
View File
@@ -5,6 +5,9 @@ const { codeFrameColumns } = require("@babel/code-frame");
const codeFrameColumnsOptions = { linesAbove: Infinity, linesBelow: Infinity }; const codeFrameColumnsOptions = { linesAbove: Infinity, linesBelow: Infinity };
const locationForRange = (text, rangeStart, rangeEnd) => { const locationForRange = (text, rangeStart, rangeEnd) => {
if (rangeStart > rangeEnd) {
[rangeStart, rangeEnd] = [rangeEnd, rangeStart];
}
const lines = new LinesAndColumns(text); const lines = new LinesAndColumns(text);
const start = lines.locationForIndex(rangeStart); const start = lines.locationForIndex(rangeStart);
const end = lines.locationForIndex(rangeEnd); const end = lines.locationForIndex(rangeEnd);
@@ -26,7 +29,9 @@ const visualizeRange = (text, { rangeStart = 0, rangeEnd = text.length }) =>
codeFrameColumns( codeFrameColumns(
text, text,
locationForRange(text, rangeStart, rangeEnd), locationForRange(text, rangeStart, rangeEnd),
codeFrameColumnsOptions rangeStart > rangeEnd
? { ...codeFrameColumnsOptions, message: "[Reversed range]" }
: codeFrameColumnsOptions
); );
module.exports = visualizeRange; module.exports = visualizeRange;