prettier 0.22.0 → 1.1.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 +115 -8
- data/CONTRIBUTING.md +1 -1
- data/README.md +17 -14
- data/node_modules/prettier/index.js +54 -54
- data/package.json +10 -5
- data/src/embed.js +11 -7
- data/src/nodes/args.js +59 -79
- data/src/nodes/arrays.js +38 -50
- data/src/nodes/assign.js +2 -2
- data/src/nodes/calls.js +132 -50
- data/src/nodes/case.js +11 -7
- data/src/nodes/conditionals.js +4 -4
- data/src/nodes/hashes.js +59 -51
- data/src/nodes/heredocs.js +2 -2
- data/src/nodes/hooks.js +5 -3
- data/src/nodes/ints.js +0 -6
- data/src/nodes/lambdas.js +6 -22
- data/src/nodes/loops.js +67 -66
- data/src/nodes/methods.js +2 -0
- data/src/nodes/operators.js +24 -13
- data/src/nodes/params.js +15 -3
- data/src/nodes/regexp.js +38 -9
- data/src/nodes/rescue.js +2 -2
- data/src/nodes/statements.js +65 -45
- data/src/nodes/strings.js +12 -11
- data/src/parser.js +27 -12
- data/src/parser.rb +190 -54
- data/src/printer.js +16 -1
- data/src/ruby.js +15 -21
- data/src/toProc.js +2 -2
- data/src/utils.js +15 -3
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +5 -46
data/src/nodes/case.js
CHANGED
@@ -28,13 +28,17 @@ module.exports = {
|
|
28
28
|
// The `fill` builder command expects an array of docs alternating with
|
29
29
|
// line breaks. This is so it can loop through and determine where to break.
|
30
30
|
const preds = fill(
|
31
|
-
path
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
path.call(print, "body", 0).reduce((accum, pred, index) => {
|
32
|
+
if (index === 0) {
|
33
|
+
return [pred];
|
34
|
+
}
|
35
|
+
|
36
|
+
// Pull off the last element and make it concat with a comma so that
|
37
|
+
// we can maintain alternating lines and docs.
|
38
|
+
return accum
|
39
|
+
.slice(0, -1)
|
40
|
+
.concat([concat([accum[accum.length - 1], ","]), line, pred]);
|
41
|
+
}, null)
|
38
42
|
);
|
39
43
|
|
40
44
|
const stmts = path.call(print, "body", 1);
|
data/src/nodes/conditionals.js
CHANGED
@@ -81,7 +81,7 @@ const printTernary = (path, _opts, print) => {
|
|
81
81
|
// modifier form, we're guaranteed to not have an additional node, so we can
|
82
82
|
// just work with the predicate and the body.
|
83
83
|
function printSingle(keyword, modifier = false) {
|
84
|
-
return function printSingleWithKeyword(path, {
|
84
|
+
return function printSingleWithKeyword(path, { rubyModifier }, print) {
|
85
85
|
const [_predicateNode, statementsNode] = path.getValue().body;
|
86
86
|
const predicateDoc = path.call(print, "body", 0);
|
87
87
|
const statementsDoc = path.call(print, "body", 1);
|
@@ -97,7 +97,7 @@ function printSingle(keyword, modifier = false) {
|
|
97
97
|
// If we do not allow modifier form conditionals or there are comments
|
98
98
|
// inside of the body of the conditional, then we must print in the
|
99
99
|
// multiline form.
|
100
|
-
if (!
|
100
|
+
if (!rubyModifier || (!modifier && statementsNode.body[0].comments)) {
|
101
101
|
return concat([concat(multilineParts), breakParent]);
|
102
102
|
}
|
103
103
|
|
@@ -190,7 +190,7 @@ const canTernary = (path) => {
|
|
190
190
|
};
|
191
191
|
|
192
192
|
// A normalized print function for both `if` and `unless` nodes.
|
193
|
-
const printConditional = (keyword) => (path, {
|
193
|
+
const printConditional = (keyword) => (path, { rubyModifier }, print) => {
|
194
194
|
if (canTernary(path)) {
|
195
195
|
let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
|
196
196
|
printTernaryClauses(
|
@@ -239,7 +239,7 @@ const printConditional = (keyword) => (path, { inlineConditionals }, print) => {
|
|
239
239
|
]);
|
240
240
|
}
|
241
241
|
|
242
|
-
return printSingle(keyword)(path, {
|
242
|
+
return printSingle(keyword)(path, { rubyModifier }, print);
|
243
243
|
};
|
244
244
|
|
245
245
|
module.exports = {
|
data/src/nodes/hashes.js
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
+
const { concat, group, ifBreak, indent, join, line } = require("../prettier");
|
2
|
+
|
1
3
|
const {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
join,
|
8
|
-
line
|
9
|
-
} = require("../prettier");
|
10
|
-
const { prefix, skipAssignIndent } = require("../utils");
|
4
|
+
getTrailingComma,
|
5
|
+
prefix,
|
6
|
+
printEmptyCollection,
|
7
|
+
skipAssignIndent
|
8
|
+
} = require("../utils");
|
11
9
|
|
12
10
|
// When attempting to convert a hash rocket into a hash label, you need to take
|
13
11
|
// care because only certain patterns are allowed. Ruby source says that they
|
@@ -24,35 +22,54 @@ function isValidHashLabel(symbolLiteral) {
|
|
24
22
|
return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
|
25
23
|
}
|
26
24
|
|
27
|
-
function
|
28
|
-
|
29
|
-
|
25
|
+
function canUseHashLabels(contentsNode) {
|
26
|
+
return contentsNode.body.every((assocNode) => {
|
27
|
+
if (assocNode.type === "assoc_splat") {
|
28
|
+
return true;
|
29
|
+
}
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
return
|
40
|
-
}
|
41
|
-
return concat([labelDoc, " =>"]);
|
31
|
+
switch (assocNode.body[0].type) {
|
32
|
+
case "@label":
|
33
|
+
return true;
|
34
|
+
case "symbol_literal":
|
35
|
+
return isValidHashLabel(assocNode.body[0]);
|
36
|
+
case "dyna_symbol":
|
37
|
+
return true;
|
38
|
+
default:
|
39
|
+
return false;
|
42
40
|
}
|
41
|
+
});
|
42
|
+
}
|
43
|
+
|
44
|
+
function printHashKeyLabel(path, print) {
|
45
|
+
const node = path.getValue();
|
46
|
+
|
47
|
+
switch (node.type) {
|
48
|
+
case "@label":
|
49
|
+
return print(path);
|
50
|
+
case "symbol_literal":
|
51
|
+
return concat([path.call(print, "body", 0), ":"]);
|
43
52
|
case "dyna_symbol":
|
44
|
-
|
45
|
-
return concat(labelDoc.parts.slice(1).concat(":"));
|
46
|
-
}
|
47
|
-
return concat([labelDoc, " =>"]);
|
48
|
-
default:
|
49
|
-
return concat([labelDoc, " =>"]);
|
53
|
+
return concat(print(path).parts.slice(1).concat(":"));
|
50
54
|
}
|
51
55
|
}
|
52
56
|
|
57
|
+
function printHashKeyRocket(path, print) {
|
58
|
+
const node = path.getValue();
|
59
|
+
const doc = print(path);
|
60
|
+
|
61
|
+
if (node.type === "@label") {
|
62
|
+
return `:${doc.slice(0, doc.length - 1)} =>`;
|
63
|
+
}
|
64
|
+
|
65
|
+
return concat([doc, " =>"]);
|
66
|
+
}
|
67
|
+
|
53
68
|
function printAssocNew(path, opts, print) {
|
69
|
+
const { keyPrinter } = path.getParentNode();
|
70
|
+
|
71
|
+
const parts = [path.call((keyPath) => keyPrinter(keyPath, print), "body", 0)];
|
54
72
|
const valueDoc = path.call(print, "body", 1);
|
55
|
-
const parts = [printHashKey(path, opts, print)];
|
56
73
|
|
57
74
|
if (skipAssignIndent(path.getValue().body[1])) {
|
58
75
|
parts.push(" ", valueDoc);
|
@@ -63,22 +80,17 @@ function printAssocNew(path, opts, print) {
|
|
63
80
|
return group(concat(parts));
|
64
81
|
}
|
65
82
|
|
66
|
-
function
|
67
|
-
const
|
83
|
+
function printHashContents(path, opts, print) {
|
84
|
+
const node = path.getValue();
|
68
85
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
concat([hardline, join(hardline, path.map(printComment, "comments"))])
|
78
|
-
),
|
79
|
-
line,
|
80
|
-
"}"
|
81
|
-
]);
|
86
|
+
// First determine which key printer we're going to use, so that the child
|
87
|
+
// nodes can reference it when they go to get printed.
|
88
|
+
node.keyPrinter =
|
89
|
+
opts.rubyHashLabel && canUseHashLabels(path.getValue())
|
90
|
+
? printHashKeyLabel
|
91
|
+
: printHashKeyRocket;
|
92
|
+
|
93
|
+
return join(concat([",", line]), path.map(print, "body"));
|
82
94
|
}
|
83
95
|
|
84
96
|
function printHash(path, opts, print) {
|
@@ -88,7 +100,7 @@ function printHash(path, opts, print) {
|
|
88
100
|
// missing, then it means we're dealing with an empty hash, so we can just
|
89
101
|
// exit here and print.
|
90
102
|
if (hashNode.body[0] === null) {
|
91
|
-
return
|
103
|
+
return printEmptyCollection(path, opts, "{", "}");
|
92
104
|
}
|
93
105
|
|
94
106
|
return group(
|
@@ -98,7 +110,7 @@ function printHash(path, opts, print) {
|
|
98
110
|
concat([
|
99
111
|
line,
|
100
112
|
path.call(print, "body", 0),
|
101
|
-
opts
|
113
|
+
getTrailingComma(opts) ? ifBreak(",", "") : ""
|
102
114
|
])
|
103
115
|
),
|
104
116
|
line,
|
@@ -107,10 +119,6 @@ function printHash(path, opts, print) {
|
|
107
119
|
);
|
108
120
|
}
|
109
121
|
|
110
|
-
function printHashContents(path, opts, print) {
|
111
|
-
return group(join(concat([",", line]), path.map(print, "body")));
|
112
|
-
}
|
113
|
-
|
114
122
|
module.exports = {
|
115
123
|
assoc_new: printAssocNew,
|
116
124
|
assoc_splat: prefix("**"),
|
data/src/nodes/heredocs.js
CHANGED
@@ -2,7 +2,7 @@ const { concat, group, lineSuffix, join } = require("../prettier");
|
|
2
2
|
const { literalLineNoBreak } = require("../utils");
|
3
3
|
|
4
4
|
function printHeredoc(path, opts, print) {
|
5
|
-
const {
|
5
|
+
const { body, ending } = path.getValue();
|
6
6
|
|
7
7
|
const parts = body.map((part, index) => {
|
8
8
|
if (part.type !== "@tstring_content") {
|
@@ -21,7 +21,7 @@ function printHeredoc(path, opts, print) {
|
|
21
21
|
// possible, so we use a literalline without the break-parent.
|
22
22
|
return group(
|
23
23
|
concat([
|
24
|
-
beging,
|
24
|
+
path.call(print, "beging"),
|
25
25
|
lineSuffix(
|
26
26
|
group(concat([literalLineNoBreak].concat(parts).concat(ending)))
|
27
27
|
)
|
data/src/nodes/hooks.js
CHANGED
@@ -17,11 +17,13 @@ const { isEmptyStmts } = require("../utils");
|
|
17
17
|
// nodes contain one child which is a `stmts` node.
|
18
18
|
function printHook(name) {
|
19
19
|
return function printHookWithName(path, opts, print) {
|
20
|
-
const stmtsNode = path.getValue().body[
|
21
|
-
const printedStmts = path.call(print, "body",
|
20
|
+
const stmtsNode = path.getValue().body[1];
|
21
|
+
const printedStmts = path.call(print, "body", 1);
|
22
22
|
|
23
23
|
const parts = [
|
24
|
-
|
24
|
+
name,
|
25
|
+
" ",
|
26
|
+
path.call(print, "body", 0),
|
25
27
|
indent(concat([line, printedStmts])),
|
26
28
|
concat([line, "}"])
|
27
29
|
];
|
data/src/nodes/ints.js
CHANGED
@@ -12,12 +12,6 @@
|
|
12
12
|
function printInt(path, _opts, _print) {
|
13
13
|
const { body } = path.getValue();
|
14
14
|
|
15
|
-
// If the number is octal and does not contain the optional "o" character
|
16
|
-
// after the leading 0, add it in.
|
17
|
-
if (/^0[0-9]/.test(body)) {
|
18
|
-
return `0o${body.slice(1)}`;
|
19
|
-
}
|
20
|
-
|
21
15
|
// If the number is a base 10 number, is sufficiently large, and is not
|
22
16
|
// already formatted with underscores, then add them in in between the
|
23
17
|
// numbers every three characters starting from the right.
|
data/src/nodes/lambdas.js
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
const {
|
2
|
-
concat,
|
3
|
-
group,
|
4
|
-
ifBreak,
|
5
|
-
indent,
|
6
|
-
line,
|
7
|
-
softline
|
8
|
-
} = require("../prettier");
|
1
|
+
const { concat, group, ifBreak, indent, line } = require("../prettier");
|
9
2
|
const { hasAncestor } = require("../utils");
|
10
3
|
|
11
4
|
// We can have our params coming in as the first child of the main lambda node,
|
@@ -13,31 +6,22 @@ const { hasAncestor } = require("../utils");
|
|
13
6
|
// though it's possible to omit the parens if you only have one argument, we're
|
14
7
|
// going to keep them in no matter what for consistency.
|
15
8
|
function printLambdaParams(path, print) {
|
16
|
-
|
17
|
-
let paramsNode = path.getValue().body[0];
|
9
|
+
let node = path.getValue().body[0];
|
18
10
|
|
19
11
|
// In this case we had something like -> (foo) { bar } which would mean that
|
20
12
|
// we're looking at a paren node, so we'll descend one level deeper to get at
|
21
13
|
// the actual params node.
|
22
|
-
if (
|
23
|
-
|
24
|
-
paramsNode = paramsNode.body[0];
|
14
|
+
if (node.type !== "params") {
|
15
|
+
node = node.body[0];
|
25
16
|
}
|
26
17
|
|
27
18
|
// If we don't have any params at all, then we're just going to bail out and
|
28
19
|
// print nothing. This is to avoid printing an empty set of parentheses.
|
29
|
-
if (
|
20
|
+
if (node.body.every((type) => !type)) {
|
30
21
|
return "";
|
31
22
|
}
|
32
23
|
|
33
|
-
return
|
34
|
-
concat([
|
35
|
-
"(",
|
36
|
-
indent(concat([softline, path.call.apply(path, paramsPath)])),
|
37
|
-
softline,
|
38
|
-
")"
|
39
|
-
])
|
40
|
-
);
|
24
|
+
return path.call(print, "body", 0);
|
41
25
|
}
|
42
26
|
|
43
27
|
// Lambda nodes represent stabby lambda literals, which can come in a couple of
|
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,
|
@@ -11,85 +12,85 @@ const {
|
|
11
12
|
const { containsAssignment } = require("../utils");
|
12
13
|
const inlineEnsureParens = require("../utils/inlineEnsureParens");
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
function printLoop(keyword, modifier) {
|
16
|
+
return function printLoopWithOptions(path, { rubyModifier }, print) {
|
17
|
+
const [_predicate, stmts] = path.getValue().body;
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
19
|
+
// If the only statement inside this while loop is a void statement, then we
|
20
|
+
// can shorten to just displaying the predicate and then a semicolon.
|
21
|
+
if (
|
22
|
+
stmts.body.length === 1 &&
|
23
|
+
stmts.body[0].type === "void_stmt" &&
|
24
|
+
!stmts.body[0].comments
|
25
|
+
) {
|
26
|
+
return group(
|
27
|
+
concat([
|
28
|
+
keyword,
|
29
|
+
" ",
|
30
|
+
path.call(print, "body", 0),
|
31
|
+
ifBreak(softline, "; "),
|
32
|
+
"end"
|
33
|
+
])
|
34
|
+
);
|
35
|
+
}
|
36
|
+
|
37
|
+
const inlineLoop = concat(
|
38
|
+
inlineEnsureParens(path, [
|
39
|
+
path.call(print, "body", 1),
|
40
|
+
` ${keyword} `,
|
41
|
+
path.call(print, "body", 0)
|
31
42
|
])
|
32
43
|
);
|
33
|
-
}
|
34
44
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
45
|
+
// If we're in the modifier form and we're modifying a `begin`, then this is
|
46
|
+
// a special case where we need to explicitly use the modifier form because
|
47
|
+
// otherwise the semantic meaning changes. This looks like:
|
48
|
+
//
|
49
|
+
// begin
|
50
|
+
// foo
|
51
|
+
// end while bar
|
52
|
+
//
|
53
|
+
// The above is effectively a `do...while` loop (which we don't have in
|
54
|
+
// ruby).
|
55
|
+
if (modifier && path.getValue().body[1].type === "begin") {
|
56
|
+
return inlineLoop;
|
57
|
+
}
|
42
58
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
// The above is effectively a `do...while` loop (which we don't have in ruby).
|
52
|
-
if (modifier && path.getValue().body[1].type === "begin") {
|
53
|
-
return inlineLoop;
|
54
|
-
}
|
59
|
+
const blockLoop = concat([
|
60
|
+
concat([
|
61
|
+
`${keyword} `,
|
62
|
+
align(keyword.length + 1, path.call(print, "body", 0))
|
63
|
+
]),
|
64
|
+
indent(concat([softline, path.call(print, "body", 1)])),
|
65
|
+
concat([softline, "end"])
|
66
|
+
]);
|
55
67
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
])
|
61
|
-
|
62
|
-
|
63
|
-
]);
|
68
|
+
// If we're disallowing inline loops or if the predicate of the loop
|
69
|
+
// contains an assignment (in which case we can't know for certain that that
|
70
|
+
// assignment doesn't impact the statements inside the loop) then we can't
|
71
|
+
// use the modifier form and we must use the block form.
|
72
|
+
if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
|
73
|
+
return concat([breakParent, blockLoop]);
|
74
|
+
}
|
64
75
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
// use the modifier form and we must use the block form.
|
69
|
-
if (!inlineLoops || containsAssignment(path.getValue().body[0])) {
|
70
|
-
return blockLoop;
|
71
|
-
}
|
76
|
+
return group(ifBreak(blockLoop, inlineLoop));
|
77
|
+
};
|
78
|
+
}
|
72
79
|
|
73
|
-
|
74
|
-
|
80
|
+
function printFor(path, opts, print) {
|
81
|
+
const [variable, range, stmts] = path.map(print, "body");
|
75
82
|
|
76
|
-
|
77
|
-
// a local variable that then remains in the outer scope. Additionally, if the
|
78
|
-
// `each` method was somehow missing from the enumerable (it's possible...),
|
79
|
-
// then this transformation would fail. However - I've never actually seen a
|
80
|
-
// `for` loop used in production. If someone actually calls me on it, I'll fix
|
81
|
-
// this, but for now I'm leaving it.
|
82
|
-
const printFor = (path, opts, print) =>
|
83
|
-
group(
|
83
|
+
return group(
|
84
84
|
concat([
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
indent(concat([hardline,
|
85
|
+
"for ",
|
86
|
+
variable,
|
87
|
+
" in ",
|
88
|
+
range,
|
89
|
+
indent(concat([hardline, stmts])),
|
90
90
|
concat([hardline, "end"])
|
91
91
|
])
|
92
92
|
);
|
93
|
+
}
|
93
94
|
|
94
95
|
module.exports = {
|
95
96
|
while: printLoop("while", false),
|