prettier 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
};
|