prettier 0.20.0 → 1.0.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +118 -4
- data/CONTRIBUTING.md +8 -6
- data/README.md +67 -60
- data/node_modules/prettier/bin-prettier.js +12317 -50112
- data/node_modules/prettier/index.js +33352 -27419
- data/node_modules/prettier/third-party.js +5678 -7676
- data/package.json +4 -4
- data/src/embed.js +27 -8
- data/src/nodes.js +6 -2
- data/src/nodes/alias.js +65 -24
- data/src/nodes/aref.js +55 -0
- data/src/nodes/args.js +55 -47
- data/src/nodes/arrays.js +150 -137
- data/src/nodes/assign.js +32 -32
- data/src/nodes/blocks.js +8 -3
- data/src/nodes/calls.js +129 -70
- data/src/nodes/case.js +11 -7
- data/src/nodes/class.js +74 -0
- data/src/nodes/commands.js +36 -31
- data/src/nodes/conditionals.js +48 -46
- data/src/nodes/constants.js +39 -21
- data/src/nodes/flow.js +45 -17
- data/src/nodes/hashes.js +126 -112
- data/src/nodes/heredocs.js +34 -0
- data/src/nodes/hooks.js +36 -7
- data/src/nodes/ints.js +27 -20
- data/src/nodes/lambdas.js +69 -52
- data/src/nodes/loops.js +19 -29
- data/src/nodes/massign.js +87 -65
- data/src/nodes/methods.js +48 -73
- data/src/nodes/operators.js +70 -39
- data/src/nodes/params.js +26 -16
- data/src/nodes/patterns.js +108 -33
- data/src/nodes/regexp.js +38 -14
- data/src/nodes/rescue.js +72 -59
- data/src/nodes/statements.js +86 -44
- data/src/nodes/strings.js +94 -90
- data/src/nodes/super.js +35 -0
- data/src/nodes/undef.js +42 -0
- data/src/parser.js +71 -0
- data/src/parser.rb +2554 -0
- data/src/printer.js +90 -0
- data/src/ruby.js +20 -61
- data/src/toProc.js +4 -4
- data/src/utils.js +24 -88
- data/src/utils/inlineEnsureParens.js +42 -0
- data/src/utils/isEmptyStmts.js +7 -0
- data/src/utils/literalLineNoBreak.js +7 -0
- metadata +15 -20
- data/src/haml.js +0 -21
- data/src/haml/embed.js +0 -58
- data/src/haml/nodes/comment.js +0 -27
- data/src/haml/nodes/doctype.js +0 -32
- data/src/haml/nodes/filter.js +0 -16
- data/src/haml/nodes/hamlComment.js +0 -21
- data/src/haml/nodes/script.js +0 -29
- data/src/haml/nodes/silentScript.js +0 -59
- data/src/haml/nodes/tag.js +0 -157
- data/src/haml/parse.js +0 -18
- data/src/haml/parse.rb +0 -64
- data/src/haml/print.js +0 -38
- data/src/nodes/scopes.js +0 -61
- data/src/parse.js +0 -37
- data/src/print.js +0 -23
- data/src/ripper.rb +0 -811
@@ -0,0 +1,34 @@
|
|
1
|
+
const { concat, group, lineSuffix, join } = require("../prettier");
|
2
|
+
const { literalLineNoBreak } = require("../utils");
|
3
|
+
|
4
|
+
function printHeredoc(path, opts, print) {
|
5
|
+
const { body, ending } = path.getValue();
|
6
|
+
|
7
|
+
const parts = body.map((part, index) => {
|
8
|
+
if (part.type !== "@tstring_content") {
|
9
|
+
// In this case, the part of the string is an embedded expression
|
10
|
+
return path.call(print, "body", index);
|
11
|
+
}
|
12
|
+
|
13
|
+
// In this case, the part of the string is just regular string content
|
14
|
+
return join(literalLineNoBreak, part.body.split("\n"));
|
15
|
+
});
|
16
|
+
|
17
|
+
// We use a literalline break because matching indentation is required
|
18
|
+
// for the heredoc contents and ending. If the line suffix contains a
|
19
|
+
// break-parent, all ancestral groups are broken, and heredocs automatically
|
20
|
+
// break lines in groups they appear in. We prefer them to appear in-line if
|
21
|
+
// possible, so we use a literalline without the break-parent.
|
22
|
+
return group(
|
23
|
+
concat([
|
24
|
+
path.call(print, "beging"),
|
25
|
+
lineSuffix(
|
26
|
+
group(concat([literalLineNoBreak].concat(parts).concat(ending)))
|
27
|
+
)
|
28
|
+
])
|
29
|
+
);
|
30
|
+
}
|
31
|
+
|
32
|
+
module.exports = {
|
33
|
+
heredoc: printHeredoc
|
34
|
+
};
|
data/src/nodes/hooks.js
CHANGED
@@ -1,13 +1,42 @@
|
|
1
1
|
const { concat, group, indent, line } = require("../prettier");
|
2
|
+
const { isEmptyStmts } = require("../utils");
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
// The `BEGIN` and `END` keywords are used to hook into the Ruby process. Any
|
5
|
+
// `BEGIN` blocks are executed right when the process starts up, and the `END`
|
6
|
+
// blocks are executed right before exiting.
|
7
|
+
//
|
8
|
+
// BEGIN {
|
9
|
+
// # content goes here
|
10
|
+
// }
|
11
|
+
//
|
12
|
+
// END {
|
13
|
+
// # content goes here
|
14
|
+
// }
|
15
|
+
//
|
16
|
+
// Interesting side note, you don't use `do...end` blocks with these hooks. Both
|
17
|
+
// nodes contain one child which is a `stmts` node.
|
18
|
+
function printHook(name) {
|
19
|
+
return function printHookWithName(path, opts, print) {
|
20
|
+
const stmtsNode = path.getValue().body[1];
|
21
|
+
const printedStmts = path.call(print, "body", 1);
|
22
|
+
|
23
|
+
const parts = [
|
24
|
+
name,
|
25
|
+
" ",
|
26
|
+
path.call(print, "body", 0),
|
27
|
+
indent(concat([line, printedStmts])),
|
8
28
|
concat([line, "}"])
|
9
|
-
]
|
10
|
-
|
29
|
+
];
|
30
|
+
|
31
|
+
// If there are no statements but there are comments, then we want to skip
|
32
|
+
// printing the newline so that we don't end up with multiple spaces.
|
33
|
+
if (isEmptyStmts(stmtsNode) && stmtsNode.comments) {
|
34
|
+
parts[1] = indent(printedStmts);
|
35
|
+
}
|
36
|
+
|
37
|
+
return group(concat(parts));
|
38
|
+
};
|
39
|
+
}
|
11
40
|
|
12
41
|
module.exports = {
|
13
42
|
BEGIN: printHook("BEGIN"),
|
data/src/nodes/ints.js
CHANGED
@@ -1,24 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
// An @int node is any literal integer in Ruby. They can come in a number of
|
2
|
+
// bases, and look like the following:
|
3
|
+
//
|
4
|
+
// Binary (2) - 0b0110
|
5
|
+
// Octal (8) - 0o34 or 034
|
6
|
+
// Decimal (10) - a normal number like 159
|
7
|
+
// Hexidecimal (16) - 0xac5
|
8
|
+
//
|
9
|
+
// If it's a decimal number, it can be optional separated by any number of
|
10
|
+
// arbitrarily places underscores. This can be useful for dollars and cents
|
11
|
+
// (34_99), dates (2020_11_30), and normal 3 digit separation (1_222_333).
|
12
|
+
function printInt(path, _opts, _print) {
|
13
|
+
const { body } = path.getValue();
|
4
14
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
}
|
15
|
+
// If the number is a base 10 number, is sufficiently large, and is not
|
16
|
+
// already formatted with underscores, then add them in in between the
|
17
|
+
// numbers every three characters starting from the right.
|
18
|
+
if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
|
19
|
+
return ` ${body}`
|
20
|
+
.slice((body.length + 2) % 3)
|
21
|
+
.match(/.{3}/g)
|
22
|
+
.join("_")
|
23
|
+
.trim();
|
24
|
+
}
|
10
25
|
|
11
|
-
|
12
|
-
|
13
|
-
// numbers every three characters starting from the right.
|
14
|
-
if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
|
15
|
-
return ` ${body}`
|
16
|
-
.slice((body.length + 2) % 3)
|
17
|
-
.match(/.{3}/g)
|
18
|
-
.join("_")
|
19
|
-
.trim();
|
20
|
-
}
|
26
|
+
return body;
|
27
|
+
}
|
21
28
|
|
22
|
-
|
23
|
-
|
29
|
+
module.exports = {
|
30
|
+
"@int": printInt
|
24
31
|
};
|
data/src/nodes/lambdas.js
CHANGED
@@ -1,59 +1,76 @@
|
|
1
|
-
const {
|
2
|
-
concat,
|
3
|
-
group,
|
4
|
-
ifBreak,
|
5
|
-
indent,
|
6
|
-
line,
|
7
|
-
removeLines,
|
8
|
-
softline
|
9
|
-
} = require("../prettier");
|
1
|
+
const { concat, group, ifBreak, indent, line } = require("../prettier");
|
10
2
|
const { hasAncestor } = require("../utils");
|
11
3
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
// We can have our params coming in as the first child of the main lambda node,
|
5
|
+
// or if we have them wrapped in parens then they'll be one level deeper. Even
|
6
|
+
// though it's possible to omit the parens if you only have one argument, we're
|
7
|
+
// going to keep them in no matter what for consistency.
|
8
|
+
function printLambdaParams(path, print) {
|
9
|
+
let node = path.getValue().body[0];
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
11
|
+
// In this case we had something like -> (foo) { bar } which would mean that
|
12
|
+
// we're looking at a paren node, so we'll descend one level deeper to get at
|
13
|
+
// the actual params node.
|
14
|
+
if (node.type !== "params") {
|
15
|
+
node = node.body[0];
|
16
|
+
}
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
path.call(print, "body", 1),
|
30
|
-
" }"
|
31
|
-
]);
|
18
|
+
// If we don't have any params at all, then we're just going to bail out and
|
19
|
+
// print nothing. This is to avoid printing an empty set of parentheses.
|
20
|
+
if (node.body.every((type) => !type)) {
|
21
|
+
return "";
|
22
|
+
}
|
32
23
|
|
33
|
-
|
34
|
-
|
35
|
-
ifBreak(
|
36
|
-
concat([
|
37
|
-
"lambda {",
|
38
|
-
noParams ? "" : concat([" |", removeLines(paramsConcat), "|"]),
|
39
|
-
indent(concat([line, path.call(print, "body", 1)])),
|
40
|
-
concat([line, "}"])
|
41
|
-
]),
|
42
|
-
inlineLambda
|
43
|
-
)
|
44
|
-
);
|
45
|
-
}
|
24
|
+
return path.call(print, "body", 0);
|
25
|
+
}
|
46
26
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
27
|
+
// Lambda nodes represent stabby lambda literals, which can come in a couple of
|
28
|
+
// flavors. They can use either braces or do...end for their block, and their
|
29
|
+
// arguments can be not present, have no parentheses for a single argument, or
|
30
|
+
// have parentheses for multiple arguments. Below are a couple of examples:
|
31
|
+
//
|
32
|
+
// -> { 1 }
|
33
|
+
// -> a { a + 1 }
|
34
|
+
// ->(a) { a + 1 }
|
35
|
+
// ->(a, b) { a + b }
|
36
|
+
// ->(a, b = 1) { a + b }
|
37
|
+
//
|
38
|
+
// -> do
|
39
|
+
// 1
|
40
|
+
// end
|
41
|
+
//
|
42
|
+
// -> a do
|
43
|
+
// a + 1
|
44
|
+
// end
|
45
|
+
//
|
46
|
+
// ->(a, b) do
|
47
|
+
// a + b
|
48
|
+
// end
|
49
|
+
//
|
50
|
+
// Generally, we're going to favor do...end for the multi-line form and braces
|
51
|
+
// for the single-line form. However, if we have an ancestor that is a command
|
52
|
+
// or command_call node, then we'll need to use braces either way because of
|
53
|
+
// operator precendence.
|
54
|
+
function printLambda(path, opts, print) {
|
55
|
+
const params = printLambdaParams(path, print);
|
56
|
+
const inCommand = hasAncestor(path, ["command", "command_call"]);
|
57
|
+
|
58
|
+
return group(
|
59
|
+
ifBreak(
|
60
|
+
concat([
|
61
|
+
"->",
|
62
|
+
params,
|
63
|
+
" ",
|
64
|
+
inCommand ? "{" : "do",
|
65
|
+
indent(concat([line, path.call(print, "body", 1)])),
|
66
|
+
line,
|
67
|
+
inCommand ? "}" : "end"
|
68
|
+
]),
|
69
|
+
concat(["->", params, " { ", path.call(print, "body", 1), " }"])
|
70
|
+
)
|
71
|
+
);
|
72
|
+
}
|
73
|
+
|
74
|
+
module.exports = {
|
75
|
+
lambda: printLambda
|
59
76
|
};
|
data/src/nodes/loops.js
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
const {
|
2
2
|
align,
|
3
|
+
breakParent,
|
3
4
|
concat,
|
4
5
|
group,
|
5
6
|
hardline,
|
@@ -7,14 +8,20 @@ const {
|
|
7
8
|
indent,
|
8
9
|
softline
|
9
10
|
} = require("../prettier");
|
11
|
+
|
10
12
|
const { containsAssignment } = require("../utils");
|
13
|
+
const inlineEnsureParens = require("../utils/inlineEnsureParens");
|
11
14
|
|
12
|
-
const printLoop = (keyword, modifier) => (path, {
|
13
|
-
const [_predicate,
|
15
|
+
const printLoop = (keyword, modifier) => (path, { rubyModifier }, print) => {
|
16
|
+
const [_predicate, stmts] = path.getValue().body;
|
14
17
|
|
15
18
|
// If the only statement inside this while loop is a void statement, then we
|
16
19
|
// can shorten to just displaying the predicate and then a semicolon.
|
17
|
-
if (
|
20
|
+
if (
|
21
|
+
stmts.body.length === 1 &&
|
22
|
+
stmts.body[0].type === "void_stmt" &&
|
23
|
+
!stmts.body[0].comments
|
24
|
+
) {
|
18
25
|
return group(
|
19
26
|
concat([
|
20
27
|
keyword,
|
@@ -26,30 +33,13 @@ const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
|
|
26
33
|
);
|
27
34
|
}
|
28
35
|
|
29
|
-
|
30
|
-
path
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
// besides a local variable then we can't inline the entire expression
|
37
|
-
// without wrapping it in parentheses. This is because the following
|
38
|
-
// expressions have different semantic meaning:
|
39
|
-
//
|
40
|
-
// hash[:key] = break :value while false
|
41
|
-
// hash[:key] = while false do break :value end
|
42
|
-
//
|
43
|
-
// The first one will not result in an empty hash, whereas the second one
|
44
|
-
// will result in `{ key: nil }`. In this case what we need to do for the
|
45
|
-
// first expression to align is wrap it in parens, as in:
|
46
|
-
//
|
47
|
-
// hash[:key] = (break :value while false)
|
48
|
-
if (["assign", "massign"].includes(path.getParentNode().type)) {
|
49
|
-
inlineParts = ["("].concat(inlineParts).concat(")");
|
50
|
-
}
|
51
|
-
|
52
|
-
const inlineLoop = concat(inlineParts);
|
36
|
+
const inlineLoop = concat(
|
37
|
+
inlineEnsureParens(path, [
|
38
|
+
path.call(print, "body", 1),
|
39
|
+
` ${keyword} `,
|
40
|
+
path.call(print, "body", 0)
|
41
|
+
])
|
42
|
+
);
|
53
43
|
|
54
44
|
// If we're in the modifier form and we're modifying a `begin`, then this is a
|
55
45
|
// special case where we need to explicitly use the modifier form because
|
@@ -77,8 +67,8 @@ const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
|
|
77
67
|
// an assignment (in which case we can't know for certain that that
|
78
68
|
// assignment doesn't impact the statements inside the loop) then we can't
|
79
69
|
// use the modifier form and we must use the block form.
|
80
|
-
if (!
|
81
|
-
return blockLoop;
|
70
|
+
if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
|
71
|
+
return concat([breakParent, blockLoop]);
|
82
72
|
}
|
83
73
|
|
84
74
|
return group(ifBreak(blockLoop, inlineLoop));
|
data/src/nodes/massign.js
CHANGED
@@ -1,69 +1,91 @@
|
|
1
1
|
const { concat, group, indent, join, line, softline } = require("../prettier");
|
2
|
-
const { makeList } = require("../utils");
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
[
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if (path.getValue().body[0].comma) {
|
50
|
-
parts.push(",");
|
51
|
-
}
|
52
|
-
|
53
|
-
return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
|
54
|
-
},
|
55
|
-
mrhs: makeList,
|
56
|
-
mrhs_add_star: (path, opts, print) =>
|
57
|
-
path
|
58
|
-
.call(print, "body", 0)
|
59
|
-
.concat([concat(["*", path.call(print, "body", 1)])]),
|
60
|
-
mrhs_new_from_args: (path, opts, print) => {
|
61
|
-
const parts = path.call(print, "body", 0);
|
62
|
-
|
63
|
-
if (path.getValue().body.length > 1) {
|
64
|
-
parts.push(path.call(print, "body", 1));
|
65
|
-
}
|
66
|
-
|
67
|
-
return parts;
|
3
|
+
function printMAssign(path, opts, print) {
|
4
|
+
let right = path.call(print, "body", 1);
|
5
|
+
|
6
|
+
if (
|
7
|
+
["mrhs_add_star", "mrhs_new_from_args"].includes(
|
8
|
+
path.getValue().body[1].type
|
9
|
+
)
|
10
|
+
) {
|
11
|
+
right = group(join(concat([",", line]), right));
|
12
|
+
}
|
13
|
+
|
14
|
+
const parts = [join(concat([",", line]), path.call(print, "body", 0))];
|
15
|
+
if (path.getValue().body[0].comma) {
|
16
|
+
parts.push(",");
|
17
|
+
}
|
18
|
+
|
19
|
+
return group(
|
20
|
+
concat([group(concat(parts)), " =", indent(concat([line, right]))])
|
21
|
+
);
|
22
|
+
}
|
23
|
+
|
24
|
+
function printMLHS(path, opts, print) {
|
25
|
+
return path.map(print, "body");
|
26
|
+
}
|
27
|
+
|
28
|
+
function printMLHSAddPost(path, opts, print) {
|
29
|
+
return path.call(print, "body", 0).concat(path.call(print, "body", 1));
|
30
|
+
}
|
31
|
+
|
32
|
+
function printMLHSAddStar(path, opts, print) {
|
33
|
+
const rightParts = ["*"];
|
34
|
+
|
35
|
+
if (path.getValue().body[1]) {
|
36
|
+
rightParts.push(path.call(print, "body", 1));
|
37
|
+
}
|
38
|
+
|
39
|
+
return path.call(print, "body", 0).concat(concat(rightParts));
|
40
|
+
}
|
41
|
+
|
42
|
+
function printMLHSParen(path, opts, print) {
|
43
|
+
if (["massign", "mlhs_paren"].includes(path.getParentNode().type)) {
|
44
|
+
// If we're nested in brackets as part of the left hand side of an
|
45
|
+
// assignment, i.e., (a, b, c) = 1, 2, 3
|
46
|
+
// ignore the current node and just go straight to the content
|
47
|
+
return path.call(print, "body", 0);
|
68
48
|
}
|
49
|
+
|
50
|
+
const parts = [
|
51
|
+
softline,
|
52
|
+
join(concat([",", line]), path.call(print, "body", 0))
|
53
|
+
];
|
54
|
+
|
55
|
+
if (path.getValue().body[0].comma) {
|
56
|
+
parts.push(",");
|
57
|
+
}
|
58
|
+
|
59
|
+
return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
|
60
|
+
}
|
61
|
+
|
62
|
+
function printMRHS(path, opts, print) {
|
63
|
+
return path.map(print, "body");
|
64
|
+
}
|
65
|
+
|
66
|
+
function printMRHSAddStar(path, opts, print) {
|
67
|
+
const [leftDoc, rightDoc] = path.map(print, "body");
|
68
|
+
|
69
|
+
return leftDoc.concat([concat(["*", rightDoc])]);
|
70
|
+
}
|
71
|
+
|
72
|
+
function printMRHSNewFromArgs(path, opts, print) {
|
73
|
+
const parts = path.call(print, "body", 0);
|
74
|
+
|
75
|
+
if (path.getValue().body[1]) {
|
76
|
+
parts.push(path.call(print, "body", 1));
|
77
|
+
}
|
78
|
+
|
79
|
+
return parts;
|
80
|
+
}
|
81
|
+
|
82
|
+
module.exports = {
|
83
|
+
massign: printMAssign,
|
84
|
+
mlhs: printMLHS,
|
85
|
+
mlhs_add_post: printMLHSAddPost,
|
86
|
+
mlhs_add_star: printMLHSAddStar,
|
87
|
+
mlhs_paren: printMLHSParen,
|
88
|
+
mrhs: printMRHS,
|
89
|
+
mrhs_add_star: printMRHSAddStar,
|
90
|
+
mrhs_new_from_args: printMRHSNewFromArgs
|
69
91
|
};
|