prettier 0.20.1 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -1
- data/CONTRIBUTING.md +5 -3
- data/README.md +51 -48
- data/node_modules/prettier/bin-prettier.js +181 -148
- data/node_modules/prettier/index.js +10335 -7465
- data/node_modules/prettier/third-party.js +33 -140
- data/package.json +1 -1
- data/src/nodes.js +1 -0
- data/src/nodes/alias.js +67 -24
- data/src/nodes/aref.js +55 -0
- data/src/nodes/arrays.js +6 -38
- data/src/nodes/calls.js +4 -19
- data/src/nodes/conditionals.js +12 -24
- data/src/nodes/flow.js +36 -18
- data/src/nodes/hashes.js +45 -45
- data/src/nodes/hooks.js +36 -6
- data/src/nodes/lambdas.js +75 -45
- data/src/nodes/loops.js +9 -24
- data/src/nodes/strings.js +0 -6
- data/src/parse.js +1 -1
- data/src/{ripper.rb → parser.rb} +27 -50
- data/src/ruby.js +1 -1
- data/src/utils.js +21 -19
- data/src/utils/inlineEnsureParens.js +42 -0
- data/src/utils/isEmptyStmts.js +7 -0
- metadata +6 -3
data/src/nodes/calls.js
CHANGED
@@ -4,20 +4,6 @@ const { concatBody, first, makeCall } = require("../utils");
|
|
4
4
|
|
5
5
|
const noIndent = ["array", "hash", "if", "method_add_block", "xstring_literal"];
|
6
6
|
|
7
|
-
const getHeredoc = (path, print, node) => {
|
8
|
-
if (node.type === "heredoc") {
|
9
|
-
const { beging, ending } = node;
|
10
|
-
return { beging, ending, content: ["body", 0, "body"] };
|
11
|
-
}
|
12
|
-
|
13
|
-
if (node.type === "string_literal" && node.body[0].type === "heredoc") {
|
14
|
-
const { beging, ending } = node.body[0];
|
15
|
-
return { beging, ending, content: ["body", 0, "body", 0, "body"] };
|
16
|
-
}
|
17
|
-
|
18
|
-
return null;
|
19
|
-
};
|
20
|
-
|
21
7
|
module.exports = {
|
22
8
|
call: (path, opts, print) => {
|
23
9
|
const [receiverNode, _operatorNode, messageNode] = path.getValue().body;
|
@@ -36,15 +22,14 @@ module.exports = {
|
|
36
22
|
// <<~TEXT.strip
|
37
23
|
// content
|
38
24
|
// TEXT
|
39
|
-
|
40
|
-
if (heredoc) {
|
25
|
+
if (receiverNode.type === "heredoc") {
|
41
26
|
return concat([
|
42
|
-
|
27
|
+
receiverNode.beging,
|
43
28
|
printedOperator,
|
44
29
|
printedMessage,
|
45
30
|
literalline,
|
46
|
-
concat(path.map
|
47
|
-
|
31
|
+
concat(path.map(print, "body", 0, "body")),
|
32
|
+
receiverNode.ending
|
48
33
|
]);
|
49
34
|
}
|
50
35
|
|
data/src/nodes/conditionals.js
CHANGED
@@ -8,7 +8,9 @@ const {
|
|
8
8
|
indent,
|
9
9
|
softline
|
10
10
|
} = require("../prettier");
|
11
|
+
|
11
12
|
const { containsAssignment } = require("../utils");
|
13
|
+
const inlineEnsureParens = require("../utils/inlineEnsureParens");
|
12
14
|
|
13
15
|
const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
|
14
16
|
concat([
|
@@ -95,30 +97,14 @@ const printSingle = (keyword) => (path, { inlineConditionals }, print) => {
|
|
95
97
|
return multiline;
|
96
98
|
}
|
97
99
|
|
98
|
-
|
99
|
-
path
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
// anything besides a local variable then we can't inline the entire
|
106
|
-
// expression without wrapping it in parentheses. This is because the
|
107
|
-
// following expressions have different semantic meaning:
|
108
|
-
//
|
109
|
-
// hash[:key] = :value if false
|
110
|
-
// hash[:key] = if false then :value end
|
111
|
-
//
|
112
|
-
// The first one will not result in an empty hash, whereas the second one
|
113
|
-
// will result in `{ key: nil }`. In this case what we need to do for the
|
114
|
-
// first expression to align is wrap it in parens, as in:
|
115
|
-
//
|
116
|
-
// hash[:key] = (:value if false)
|
117
|
-
if (["assign", "massign"].includes(path.getParentNode().type)) {
|
118
|
-
inlineParts = ["("].concat(inlineParts).concat(")");
|
119
|
-
}
|
100
|
+
const inline = concat(
|
101
|
+
inlineEnsureParens(path, [
|
102
|
+
path.call(print, "body", 1),
|
103
|
+
` ${keyword} `,
|
104
|
+
path.call(print, "body", 0)
|
105
|
+
])
|
106
|
+
);
|
120
107
|
|
121
|
-
const inline = concat(inlineParts);
|
122
108
|
return group(ifBreak(multiline, inline));
|
123
109
|
};
|
124
110
|
|
@@ -180,7 +166,9 @@ const canTernary = (path) => {
|
|
180
166
|
const [predicate, stmts, addition] = path.getValue().body;
|
181
167
|
|
182
168
|
return (
|
183
|
-
!["assign", "opassign"].includes(
|
169
|
+
!["assign", "opassign", "command_call", "command"].includes(
|
170
|
+
predicate.type
|
171
|
+
) &&
|
184
172
|
addition &&
|
185
173
|
addition.type === "else" &&
|
186
174
|
[stmts, addition.body[0]].every(canTernaryStmts)
|
data/src/nodes/flow.js
CHANGED
@@ -1,5 +1,30 @@
|
|
1
1
|
const { concat, join } = require("../prettier");
|
2
|
-
const { literal } = require("../utils");
|
2
|
+
const { literal, nodeDive } = require("../utils");
|
3
|
+
|
4
|
+
const unskippableParens = [
|
5
|
+
"if_mod",
|
6
|
+
"rescue_mod",
|
7
|
+
"unless_mod",
|
8
|
+
"until_mod",
|
9
|
+
"while_mod"
|
10
|
+
];
|
11
|
+
|
12
|
+
const maybeHandleParens = (path, print, keyword, steps) => {
|
13
|
+
const node = nodeDive(path.getValue(), steps);
|
14
|
+
if (node.type !== "paren") {
|
15
|
+
return null;
|
16
|
+
}
|
17
|
+
|
18
|
+
const stmts = node.body[0].body;
|
19
|
+
if (stmts.length === 1 && !unskippableParens.includes(stmts[0].type)) {
|
20
|
+
return concat([
|
21
|
+
`${keyword} `,
|
22
|
+
path.call.apply(path, [print].concat(steps).concat("body", 0))
|
23
|
+
]);
|
24
|
+
}
|
25
|
+
|
26
|
+
return concat([keyword, path.call.apply(path, [print].concat(steps))]);
|
27
|
+
};
|
3
28
|
|
4
29
|
module.exports = {
|
5
30
|
break: (path, opts, print) => {
|
@@ -9,14 +34,11 @@ module.exports = {
|
|
9
34
|
return "break";
|
10
35
|
}
|
11
36
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
}
|
18
|
-
|
19
|
-
return concat(["break ", join(", ", path.call(print, "body", 0))]);
|
37
|
+
const steps = ["body", 0, "body", 0, "body", 0];
|
38
|
+
return (
|
39
|
+
maybeHandleParens(path, print, "break", steps) ||
|
40
|
+
concat(["break ", join(", ", path.call(print, "body", 0))])
|
41
|
+
);
|
20
42
|
},
|
21
43
|
next: (path, opts, print) => {
|
22
44
|
const args = path.getValue().body[0].body[0];
|
@@ -25,15 +47,11 @@ module.exports = {
|
|
25
47
|
return "next";
|
26
48
|
}
|
27
49
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
]);
|
34
|
-
}
|
35
|
-
|
36
|
-
return concat(["next ", join(", ", path.call(print, "body", 0))]);
|
50
|
+
const steps = ["body", 0, "body", 0, "body", 0];
|
51
|
+
return (
|
52
|
+
maybeHandleParens(path, print, "next", steps) ||
|
53
|
+
concat(["next ", join(", ", path.call(print, "body", 0))])
|
54
|
+
);
|
37
55
|
},
|
38
56
|
yield: (path, opts, print) => {
|
39
57
|
if (path.getValue().body[0].type === "paren") {
|
data/src/nodes/hashes.js
CHANGED
@@ -7,17 +7,8 @@ const {
|
|
7
7
|
line,
|
8
8
|
literalline
|
9
9
|
} = require("../prettier");
|
10
|
-
const { prefix, skipAssignIndent } = require("../utils");
|
11
10
|
|
12
|
-
const nodeDive = (
|
13
|
-
let current = node;
|
14
|
-
|
15
|
-
steps.forEach((step) => {
|
16
|
-
current = current[step];
|
17
|
-
});
|
18
|
-
|
19
|
-
return current;
|
20
|
-
};
|
11
|
+
const { nodeDive, prefix, skipAssignIndent } = require("../utils");
|
21
12
|
|
22
13
|
// When attempting to convert a hash rocket into a hash label, you need to take
|
23
14
|
// care because only certain patterns are allowed. Ruby source says that they
|
@@ -65,6 +56,45 @@ const makeLabel = (path, { preferHashLabels }, print, steps) => {
|
|
65
56
|
}
|
66
57
|
};
|
67
58
|
|
59
|
+
function printHash(path, { addTrailingCommas }, print) {
|
60
|
+
const hashNode = path.getValue();
|
61
|
+
|
62
|
+
// Hashes normally have a single assoclist_from_args child node. If it's
|
63
|
+
// missing, then it means we're dealing with an empty hash, so we can just
|
64
|
+
// exit here and print.
|
65
|
+
if (hashNode.body[0] === null) {
|
66
|
+
return "{}";
|
67
|
+
}
|
68
|
+
|
69
|
+
// Here we get a reference to the printed assoclist_from_args child node,
|
70
|
+
// which handles printing all of the key-value pairs of the hash. We're
|
71
|
+
// wrapping it in an array in case we need to append a trailing comma.
|
72
|
+
const assocDocs = [path.call(print, "body", 0)];
|
73
|
+
|
74
|
+
// Here we get a reference to the last key-value pair's value node, in order
|
75
|
+
// to check if we're dealing with a heredoc. If we are, then the trailing
|
76
|
+
// comma printing is handled from within the assoclist_from_args node
|
77
|
+
// printing, because the trailing comma has to go after the heredoc
|
78
|
+
// declaration.
|
79
|
+
const assocNodes = hashNode.body[0].body[0];
|
80
|
+
const lastAssocValueNode = assocNodes[assocNodes.length - 1].body[1];
|
81
|
+
|
82
|
+
// If we're adding a trailing comma and the last key-value pair's value node
|
83
|
+
// is not a heredoc node, then we can safely append the extra comma if the
|
84
|
+
// hash ends up getting printed on multiple lines.
|
85
|
+
if (addTrailingCommas && lastAssocValueNode.type !== "heredoc") {
|
86
|
+
assocDocs.push(ifBreak(",", ""));
|
87
|
+
}
|
88
|
+
|
89
|
+
return group(
|
90
|
+
concat([
|
91
|
+
"{",
|
92
|
+
indent(concat([line, concat(assocDocs)])),
|
93
|
+
concat([line, "}"])
|
94
|
+
])
|
95
|
+
);
|
96
|
+
}
|
97
|
+
|
68
98
|
module.exports = {
|
69
99
|
assoc_new: (path, opts, print) => {
|
70
100
|
const valueDoc = path.call(print, "body", 1);
|
@@ -89,31 +119,15 @@ module.exports = {
|
|
89
119
|
const isInner = index !== assocNodes.length - 1;
|
90
120
|
const valueNode = assocNode.body[1];
|
91
121
|
|
92
|
-
|
93
|
-
const isSquigglyHeredoc =
|
94
|
-
valueNode &&
|
95
|
-
valueNode.type === "string_literal" &&
|
96
|
-
valueNode.body[0].type === "heredoc";
|
97
|
-
|
98
|
-
if (isStraightHeredoc || isSquigglyHeredoc) {
|
99
|
-
const heredocSteps = isStraightHeredoc
|
100
|
-
? ["body", 1]
|
101
|
-
: ["body", 1, "body", 0];
|
102
|
-
const { beging, ending } = nodeDive(assocNode, heredocSteps);
|
103
|
-
|
122
|
+
if (valueNode && valueNode.type === "heredoc") {
|
104
123
|
assocDocs.push(
|
105
124
|
makeLabel(path, opts, print, ["body", 0, index, "body", 0]),
|
106
125
|
" ",
|
107
|
-
beging,
|
126
|
+
valueNode.beging,
|
108
127
|
isInner || addTrailingCommas ? "," : "",
|
109
128
|
literalline,
|
110
|
-
concat(
|
111
|
-
|
112
|
-
path,
|
113
|
-
[print, "body", 0, index].concat(heredocSteps).concat("body")
|
114
|
-
)
|
115
|
-
),
|
116
|
-
ending,
|
129
|
+
concat(path.map(print, "body", 0, index, "body", 1, "body")),
|
130
|
+
valueNode.ending,
|
117
131
|
isInner ? line : ""
|
118
132
|
);
|
119
133
|
} else {
|
@@ -121,8 +135,6 @@ module.exports = {
|
|
121
135
|
|
122
136
|
if (isInner) {
|
123
137
|
assocDocs.push(concat([",", line]));
|
124
|
-
} else if (addTrailingCommas) {
|
125
|
-
assocDocs.push(ifBreak(",", ""));
|
126
138
|
}
|
127
139
|
}
|
128
140
|
});
|
@@ -131,17 +143,5 @@ module.exports = {
|
|
131
143
|
},
|
132
144
|
bare_assoc_hash: (path, opts, print) =>
|
133
145
|
group(join(concat([",", line]), path.map(print, "body", 0))),
|
134
|
-
hash:
|
135
|
-
if (path.getValue().body[0] === null) {
|
136
|
-
return "{}";
|
137
|
-
}
|
138
|
-
|
139
|
-
return group(
|
140
|
-
concat([
|
141
|
-
"{",
|
142
|
-
indent(concat([line, concat(path.map(print, "body"))])),
|
143
|
-
concat([line, "}"])
|
144
|
-
])
|
145
|
-
);
|
146
|
-
}
|
146
|
+
hash: printHash
|
147
147
|
};
|
data/src/nodes/hooks.js
CHANGED
@@ -1,13 +1,43 @@
|
|
1
1
|
const { concat, group, indent, line } = require("../prettier");
|
2
|
+
const { isEmptyStmts } = require("../utils");
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
+
*/
|
19
|
+
function printHook(name) {
|
20
|
+
function printHookWithName(path, opts, print) {
|
21
|
+
const stmtsNode = path.getValue().body[0];
|
22
|
+
const printedStmts = path.call(print, "body", 0);
|
23
|
+
|
24
|
+
const parts = [
|
6
25
|
`${name} {`,
|
7
|
-
indent(concat([line,
|
26
|
+
indent(concat([line, printedStmts])),
|
8
27
|
concat([line, "}"])
|
9
|
-
]
|
10
|
-
|
28
|
+
];
|
29
|
+
|
30
|
+
// If there are no statements but there are comments, then we want to skip
|
31
|
+
// printing the newline so that we don't end up with multiple spaces.
|
32
|
+
if (isEmptyStmts(stmtsNode) && stmtsNode.comments) {
|
33
|
+
parts[1] = indent(printedStmts);
|
34
|
+
}
|
35
|
+
|
36
|
+
return group(concat(parts));
|
37
|
+
}
|
38
|
+
|
39
|
+
return printHookWithName;
|
40
|
+
}
|
11
41
|
|
12
42
|
module.exports = {
|
13
43
|
BEGIN: printHook("BEGIN"),
|
data/src/nodes/lambdas.js
CHANGED
@@ -4,56 +4,86 @@ const {
|
|
4
4
|
ifBreak,
|
5
5
|
indent,
|
6
6
|
line,
|
7
|
-
removeLines,
|
8
7
|
softline
|
9
8
|
} = require("../prettier");
|
10
|
-
const { hasAncestor } = require("../utils");
|
9
|
+
const { hasAncestor, nodeDive } = require("../utils");
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
// We can have our params coming in as the first child of the main lambda node,
|
12
|
+
// or if we have them wrapped in parens then they'll be one level deeper. Even
|
13
|
+
// though it's possible to omit the parens if you only have one argument, we're
|
14
|
+
// going to keep them in no matter what for consistency.
|
15
|
+
const printLambdaParams = (path, print) => {
|
16
|
+
const steps = ["body", 0];
|
17
|
+
let params = nodeDive(path.getValue(), steps);
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
paramsConcat = path.call(print, "body", 0, "body", 0);
|
22
|
-
}
|
19
|
+
if (params.type !== "params") {
|
20
|
+
steps.push("body", 0);
|
21
|
+
params = nodeDive(path.getValue(), steps);
|
22
|
+
}
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
path.call(print, "body", 1),
|
30
|
-
" }"
|
31
|
-
]);
|
24
|
+
// If we don't have any params at all, then we're just going to bail out and
|
25
|
+
// print nothing. This is to avoid printing an empty set of parentheses.
|
26
|
+
if (params.body.every((type) => !type)) {
|
27
|
+
return "";
|
28
|
+
}
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
inlineLambda
|
43
|
-
)
|
44
|
-
);
|
45
|
-
}
|
30
|
+
return group(
|
31
|
+
concat([
|
32
|
+
"(",
|
33
|
+
indent(concat([softline, path.call.apply(path, [print].concat(steps))])),
|
34
|
+
softline,
|
35
|
+
")"
|
36
|
+
])
|
37
|
+
);
|
38
|
+
};
|
46
39
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
40
|
+
// Lambda nodes represent stabby lambda literals, which can come in a couple of
|
41
|
+
// flavors. They can use either braces or do...end for their block, and their
|
42
|
+
// arguments can be not present, have no parentheses for a single argument, or
|
43
|
+
// have parentheses for multiple arguments. Below are a couple of examples:
|
44
|
+
//
|
45
|
+
// -> { 1 }
|
46
|
+
// -> a { a + 1 }
|
47
|
+
// ->(a) { a + 1 }
|
48
|
+
// ->(a, b) { a + b }
|
49
|
+
// ->(a, b = 1) { a + b }
|
50
|
+
//
|
51
|
+
// -> do
|
52
|
+
// 1
|
53
|
+
// end
|
54
|
+
//
|
55
|
+
// -> a do
|
56
|
+
// a + 1
|
57
|
+
// end
|
58
|
+
//
|
59
|
+
// ->(a, b) do
|
60
|
+
// a + b
|
61
|
+
// end
|
62
|
+
//
|
63
|
+
// Generally, we're going to favor do...end for the multi-line form and braces
|
64
|
+
// for the single-line form. However, if we have an ancestor that is a command
|
65
|
+
// or command_call node, then we'll need to use braces either way because of
|
66
|
+
// operator precendence.
|
67
|
+
const printLambda = (path, opts, print) => {
|
68
|
+
const params = printLambdaParams(path, print);
|
69
|
+
const inCommand = hasAncestor(path, ["command", "command_call"]);
|
70
|
+
|
71
|
+
return group(
|
72
|
+
ifBreak(
|
73
|
+
concat([
|
74
|
+
"->",
|
75
|
+
params,
|
76
|
+
" ",
|
77
|
+
inCommand ? "{" : "do",
|
78
|
+
indent(concat([line, path.call(print, "body", 1)])),
|
79
|
+
line,
|
80
|
+
inCommand ? "}" : "end"
|
81
|
+
]),
|
82
|
+
concat(["->", params, " { ", path.call(print, "body", 1), " }"])
|
83
|
+
)
|
84
|
+
);
|
85
|
+
};
|
86
|
+
|
87
|
+
module.exports = {
|
88
|
+
lambda: printLambda
|
59
89
|
};
|