Files
test-repo/tests_config/run_spec.js
T
Joseph Frazier 330601c77d Find nearest node when formatting range (#1659)
* Move range extension code into helper functions

* Add findNodeByOffset() helper

This was adapted from https://github.com/prettier/prettier/pull/1637/commits/cbc1929c64db558b4e444500bca3d2ce1d550359

* Test extending formatted range to entire node

* Fix extending formatted range to entire node

* Fix style errors

* Add run_file test function

This makes it possible to use different options on a per-file basis,
which is useful for things like range formatting tests.

* Test extending the format range to nearest parseable node

This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.

* Fix extending the format range to nearest parseable node

This means you can select the range of a `catch` clause, attempt to
format it, and have the `try` formatted as well, rather than throwing an
error.

* Test that external indentation is left alone when formatting a range

* Preserve external indentation when formatting a range

* Dedupe range formatting traversal callbacks

* Simplify range formatting traversal using ast-types

See https://github.com/prettier/prettier/pull/1659#issuecomment-302974798

* Make range formatting traversal more efficient

There's less unnecessary parsing now.

* Fix style errors

* Add test where range expanding fails

* Fix test where range expanding fails

This makes sure that the range contains the entirety of the nodes
containing each of the range's endpoints.

* Add test for expanding range to beginning of line

* Pass test for expanding range to beginning of line

This makes it so that indentation before the range is added to the
formatted range.

* Don't parse/stringify AST to detect pre-range indentation

See https://github.com/prettier/prettier/pull/1659#discussion_r117790671

* When formatting a range, find closest statement rather than parsing

The `isStatement` implementation came from `docs/prettier.min.js`.

See https://github.com/prettier/prettier/pull/1659#issuecomment-303154770

* Add test for range-formatting a FunctionDeclaration's argument object

* Include FunctionDeclaration when searching for nearest node to range-format

From the spec, a Program is a series of SourceElements, each of which is
either a Statement or a FunctionDeclaration. See
https://www.ecma-international.org/ecma-262/5.1/#sec-A.5

* Remove unnecessary try-catch

See https://github.com/prettier/prettier/pull/1659#discussion_r117810096

* Add tests with multiple statements

See https://github.com/prettier/prettier/pull/1659#discussion_r117810753

* Remove unnecessary arguments from findNodeByOffset()

* Contract format range to ensure it starts/ends on nodes

* Specify test ranges in the fixtures

See https://github.com/prettier/prettier/pull/1659#discussion_r117811186

* Remove unnecessary comments from range fixtures

* Remove run_file test function

It's no longer used. This essentially reverts
8241216e68f2e0da997a4f558b03658d642c89a2

* Update range formatting docs

Clarify that the range expands to the nearest statement, and not to the
end of the line.

* Don't overwrite test options when detecting range

Now that multiple files share the same object again, we shouldn't be
re-assigning to it.

* Reuse already-read fixtures for AST_COMPARE=1 tests

* Remove `run_file` global from test eslintrc

* Undo package.json churn

`yarn` reformatted it before, but the whitespace visually sets off the
comment, so let's put it back how it was before.

See https://github.com/prettier/prettier/pull/1659#discussion_r117864655

* Remove misleading comments from isSourceElement

See https://github.com/prettier/prettier/pull/1659#discussion_r117865196

* Loop backwards through string instead of reversing it

See https://github.com/prettier/prettier/pull/1659#discussion_r117865759

* Don't recompute indent string when formatting range

See https://github.com/prettier/prettier/pull/1659#discussion_r117867268

* Rename findNodeByOffset to findNodeAtOffset

"Find x by y" is the common usage for finding an `x` by a key `y`.
However, since "by" has positional meaning, let's use "at" instead.

See https://github.com/prettier/prettier/pull/1659#discussion_r117865121

* Always trimRight() in formatRange and explain why

See https://github.com/prettier/prettier/pull/1659#discussion_r117864635

* Test formatting a range that crosses AST levels

See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688

* Fix formatting a range that crosses AST levels

See https://github.com/prettier/prettier/pull/1659#issuecomment-303243688

* Remove unnecessary try-catch

See https://github.com/prettier/prettier/pull/1659/files/e52db5e9f9ec4af599f658da03739e206dd4578c#r117878763

* Add test demonstrating range formatting indent detection

* Detect alignment from text on line before range, but don't reformat it

This avoids reformatting non-indentation that happens to precede the
range on the same line, while still correctly indenting the range based
on it.

See https://github.com/prettier/prettier/pull/1659#discussion_r117881430
2017-05-23 07:43:58 -07:00

155 lines
4.0 KiB
JavaScript

"use strict";
const fs = require("fs");
const extname = require("path").extname;
const prettier = require("../");
const parser = require("../src/parser");
const massageAST = require("../src/clean-ast.js").massageAST;
const AST_COMPARE = process.env["AST_COMPARE"];
const VERIFY_ALL_PARSERS = process.env["VERIFY_ALL_PARSERS"] || false;
const ALL_PARSERS = process.env["ALL_PARSERS"]
? JSON.parse(process.env["ALL_PARSERS"])
: ["flow", "babylon", "typescript"];
function run_spec(dirname, options, additionalParsers) {
fs.readdirSync(dirname).forEach(filename => {
const extension = extname(filename);
if (/^\.[jt]sx?$/.test(extension) && filename !== "jsfmt.spec.js") {
const path = dirname + "/" + filename;
let rangeStart = 0;
let rangeEnd = Infinity;
const source = read(path)
.replace(/\r\n/g, "\n")
.replace("<<<PRETTIER_RANGE_START>>>", (match, offset) => {
rangeStart = offset;
return "";
})
.replace("<<<PRETTIER_RANGE_END>>>", (match, offset) => {
rangeEnd = offset;
return "";
});
const mergedOptions = Object.assign(mergeDefaultOptions(options || {}), {
rangeStart: rangeStart,
rangeEnd: rangeEnd
});
const output = prettyprint(source, path, mergedOptions);
test(`${mergedOptions.parser} - ${parser.parser}-verify`, () => {
expect(raw(source + "~".repeat(80) + "\n" + output)).toMatchSnapshot(
filename
);
});
getParsersToVerify(
mergedOptions.parser,
additionalParsers || []
).forEach(parserName => {
test(`${filename} - ${parserName}-verify`, () => {
const verifyOptions = Object.assign(mergedOptions, {
parser: parserName
});
const verifyOutput = prettyprint(source, path, verifyOptions);
expect(output).toEqual(verifyOutput);
});
});
if (AST_COMPARE) {
const ast = parse(source, mergedOptions);
const astMassaged = massageAST(ast);
let ppastMassaged;
let pperr = null;
try {
const ppast = parse(
prettyprint(source, path, mergedOptions),
mergedOptions
);
ppastMassaged = massageAST(ppast);
} catch (e) {
pperr = e.stack;
}
test(path + " parse", () => {
expect(pperr).toBe(null);
expect(ppastMassaged).toBeDefined();
if (!ast.errors || ast.errors.length === 0) {
expect(astMassaged).toEqual(ppastMassaged);
}
});
}
}
});
}
global.run_spec = run_spec;
function stripLocation(ast) {
if (Array.isArray(ast)) {
return ast.map(e => stripLocation(e));
}
if (typeof ast === "object") {
const newObj = {};
for (const key in ast) {
if (
key === "loc" ||
key === "range" ||
key === "raw" ||
key === "comments"
) {
continue;
}
newObj[key] = stripLocation(ast[key]);
}
return newObj;
}
return ast;
}
function parse(string, opts) {
return stripLocation(parser.parse(string, opts));
}
function prettyprint(src, filename, options) {
return prettier.format(
src,
Object.assign(
{
filename
},
options
)
);
}
function read(filename) {
return fs.readFileSync(filename, "utf8");
}
/**
* Wraps a string in a marker object that is used by `./raw-serializer.js` to
* directly print that string in a snapshot without escaping all double quotes.
* Backticks will still be escaped.
*/
function raw(string) {
if (typeof string !== "string") {
throw new Error("Raw snapshots have to be strings.");
}
return { [Symbol.for("raw")]: string };
}
function mergeDefaultOptions(parserConfig) {
return Object.assign(
{
parser: "flow",
printWidth: 80
},
parserConfig
);
}
function getParsersToVerify(parser, additionalParsers) {
if (VERIFY_ALL_PARSERS) {
return ALL_PARSERS.splice(ALL_PARSERS.indexOf(parser), 1);
}
return additionalParsers;
}