prettier 1.0.0.pre.rc1 → 1.2.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 +60 -2
- data/README.md +14 -10
- data/node_modules/prettier/index.js +54 -54
- data/package.json +10 -5
- data/src/embed.js +6 -2
- data/src/nodes/args.js +59 -78
- data/src/nodes/arrays.js +36 -51
- data/src/nodes/assign.js +10 -2
- data/src/nodes/calls.js +129 -46
- data/src/nodes/case.js +11 -7
- data/src/nodes/hashes.js +58 -51
- data/src/nodes/ints.js +0 -6
- data/src/nodes/lambdas.js +6 -22
- data/src/nodes/loops.js +66 -66
- data/src/nodes/methods.js +11 -2
- data/src/nodes/params.js +15 -3
- data/src/nodes/patterns.js +17 -0
- data/src/nodes/regexp.js +32 -18
- data/src/nodes/statements.js +35 -25
- data/src/nodes/strings.js +1 -1
- data/src/parser.js +27 -12
- data/src/parser.rb +85 -17
- data/src/printer.js +13 -0
- data/src/ruby.js +11 -4
- data/src/toProc.js +2 -2
- data/src/utils.js +2 -3
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +7 -48
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
@@ -12,85 +12,85 @@ const {
|
|
12
12
|
const { containsAssignment } = require("../utils");
|
13
13
|
const inlineEnsureParens = require("../utils/inlineEnsureParens");
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
function printLoop(keyword, modifier) {
|
16
|
+
return function printLoopWithOptions(path, { rubyModifier }, print) {
|
17
|
+
const [_predicate, stmts] = path.getValue().body;
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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)
|
32
42
|
])
|
33
43
|
);
|
34
|
-
}
|
35
44
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
+
}
|
43
58
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
// The above is effectively a `do...while` loop (which we don't have in ruby).
|
53
|
-
if (modifier && path.getValue().body[1].type === "begin") {
|
54
|
-
return inlineLoop;
|
55
|
-
}
|
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
|
+
]);
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
])
|
62
|
-
|
63
|
-
|
64
|
-
]);
|
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
|
+
}
|
65
75
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
// use the modifier form and we must use the block form.
|
70
|
-
if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
|
71
|
-
return concat([breakParent, blockLoop]);
|
72
|
-
}
|
76
|
+
return group(ifBreak(blockLoop, inlineLoop));
|
77
|
+
};
|
78
|
+
}
|
73
79
|
|
74
|
-
|
75
|
-
|
80
|
+
function printFor(path, opts, print) {
|
81
|
+
const [variable, range, stmts] = path.map(print, "body");
|
76
82
|
|
77
|
-
|
78
|
-
// a local variable that then remains in the outer scope. Additionally, if the
|
79
|
-
// `each` method was somehow missing from the enumerable (it's possible...),
|
80
|
-
// then this transformation would fail. However - I've never actually seen a
|
81
|
-
// `for` loop used in production. If someone actually calls me on it, I'll fix
|
82
|
-
// this, but for now I'm leaving it.
|
83
|
-
const printFor = (path, opts, print) =>
|
84
|
-
group(
|
83
|
+
return group(
|
85
84
|
concat([
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
indent(concat([hardline,
|
85
|
+
"for ",
|
86
|
+
variable,
|
87
|
+
" in ",
|
88
|
+
range,
|
89
|
+
indent(concat([hardline, stmts])),
|
91
90
|
concat([hardline, "end"])
|
92
91
|
])
|
93
92
|
);
|
93
|
+
}
|
94
94
|
|
95
95
|
module.exports = {
|
96
96
|
while: printLoop("while", false),
|
data/src/nodes/methods.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
const { concat, group, hardline, indent } = require("../prettier");
|
1
|
+
const { concat, group, hardline, indent, line } = require("../prettier");
|
2
2
|
const { first } = require("../utils");
|
3
3
|
|
4
4
|
function printMethod(offset) {
|
@@ -48,8 +48,17 @@ function printMethod(offset) {
|
|
48
48
|
};
|
49
49
|
}
|
50
50
|
|
51
|
+
function printSingleLineMethod(path, opts, print) {
|
52
|
+
const [nameDoc, stmtDoc] = path.map(print, "body");
|
53
|
+
|
54
|
+
return group(
|
55
|
+
concat(["def ", nameDoc, " =", indent(group(concat([line, stmtDoc])))])
|
56
|
+
);
|
57
|
+
}
|
58
|
+
|
51
59
|
module.exports = {
|
52
60
|
access_ctrl: first,
|
53
61
|
def: printMethod(0),
|
54
|
-
defs: printMethod(2)
|
62
|
+
defs: printMethod(2),
|
63
|
+
defsl: printSingleLineMethod
|
55
64
|
};
|
data/src/nodes/params.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
const { concat, group, join, line } = require("../prettier");
|
1
|
+
const { concat, group, join, indent, line, softline } = require("../prettier");
|
2
2
|
const { literal } = require("../utils");
|
3
3
|
|
4
4
|
function printRestParam(symbol) {
|
@@ -64,6 +64,8 @@ function printParams(path, opts, print) {
|
|
64
64
|
parts.push(path.call(print, "body", 6));
|
65
65
|
}
|
66
66
|
|
67
|
+
const contents = [join(concat([",", line]), parts)];
|
68
|
+
|
67
69
|
// You can put an extra comma at the end of block args between pipes to
|
68
70
|
// change what it does. Below is the difference:
|
69
71
|
//
|
@@ -73,9 +75,19 @@ function printParams(path, opts, print) {
|
|
73
75
|
// In ruby 2.5, the excessed comma is indicated by having a 0 in the rest
|
74
76
|
// param position. In ruby 2.6+ it's indicated by having an "excessed_comma"
|
75
77
|
// node in the rest position. Seems odd, but it's true.
|
76
|
-
|
78
|
+
if (rest === 0 || (rest && rest.type === "excessed_comma")) {
|
79
|
+
contents.push(",");
|
80
|
+
}
|
81
|
+
|
82
|
+
// If the parent node is a paren then we skipped printing the parentheses so
|
83
|
+
// that we could handle them here and get nicer formatting.
|
84
|
+
if (["lambda", "paren"].includes(path.getParentNode().type)) {
|
85
|
+
return group(
|
86
|
+
concat(["(", indent(concat([softline].concat(contents))), softline, ")"])
|
87
|
+
);
|
88
|
+
}
|
77
89
|
|
78
|
-
return group(concat(
|
90
|
+
return group(concat(contents));
|
79
91
|
}
|
80
92
|
|
81
93
|
module.exports = {
|
data/src/nodes/patterns.js
CHANGED
@@ -48,6 +48,22 @@ function printAryPtn(path, opts, print) {
|
|
48
48
|
return args;
|
49
49
|
}
|
50
50
|
|
51
|
+
function printFndPtn(path, opts, print) {
|
52
|
+
const [constant] = path.getValue().body;
|
53
|
+
|
54
|
+
let args = [path.call(print, "body", 1)]
|
55
|
+
.concat(path.map(print, "body", 2))
|
56
|
+
.concat(path.call(print, "body", 3));
|
57
|
+
|
58
|
+
args = concat(["[", group(join(concat([",", line]), args)), "]"]);
|
59
|
+
|
60
|
+
if (constant) {
|
61
|
+
return concat([path.call(print, "body", 0), args]);
|
62
|
+
}
|
63
|
+
|
64
|
+
return args;
|
65
|
+
}
|
66
|
+
|
51
67
|
function printHshPtn(path, opts, print) {
|
52
68
|
const [constant, keyValuePairs, keyValueRest] = path.getValue().body;
|
53
69
|
let args = [];
|
@@ -113,6 +129,7 @@ function printIn(path, opts, print) {
|
|
113
129
|
|
114
130
|
module.exports = {
|
115
131
|
aryptn: printAryPtn,
|
132
|
+
fndptn: printFndPtn,
|
116
133
|
hshptn: printHshPtn,
|
117
134
|
in: printIn
|
118
135
|
};
|
data/src/nodes/regexp.js
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
const { concat } = require("../prettier");
|
2
|
+
const { hasAncestor } = require("../utils");
|
2
3
|
|
3
|
-
function
|
4
|
-
return node.
|
4
|
+
function hasContent(node, pattern) {
|
5
|
+
return node.body.some(
|
6
|
+
(child) => child.type === "@tstring_content" && pattern.test(child.body)
|
7
|
+
);
|
5
8
|
}
|
6
9
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
return true;
|
15
|
-
}
|
10
|
+
// If the first part of this regex is plain string content, we have a space
|
11
|
+
// or an =, and we're contained within a command or command_call node, then we
|
12
|
+
// want to use braces because otherwise we could end up with an ambiguous
|
13
|
+
// operator, e.g. foo / bar/ or foo /=bar/
|
14
|
+
function forwardSlashIsAmbiguous(path) {
|
15
|
+
const node = path.getValue();
|
16
|
+
const firstChildNode = node.body[0];
|
16
17
|
|
17
|
-
return
|
18
|
-
|
18
|
+
return (
|
19
|
+
firstChildNode &&
|
20
|
+
firstChildNode.type === "@tstring_content" &&
|
21
|
+
[" ", "="].includes(firstChildNode.body[0]) &&
|
22
|
+
hasAncestor(path, ["command", "command_call"])
|
19
23
|
);
|
20
24
|
}
|
21
25
|
|
@@ -28,13 +32,23 @@ function shouldUseBraces(node) {
|
|
28
32
|
// itself. In that case we switch over to using %r with braces.
|
29
33
|
function printRegexpLiteral(path, opts, print) {
|
30
34
|
const node = path.getValue();
|
31
|
-
const
|
35
|
+
const docs = path.map(print, "body");
|
36
|
+
|
37
|
+
// We should use braces if using a forward slash would be ambiguous in the
|
38
|
+
// current context or if there's a forward slash in the content of the regexp.
|
39
|
+
const useBraces = forwardSlashIsAmbiguous(path) || hasContent(node, /\//);
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
|
41
|
+
// If we should be using braces but we have braces in the body of the regexp,
|
42
|
+
// then we're just going to resort to using whatever the original content was.
|
43
|
+
if (useBraces && hasContent(node, /[{}]/)) {
|
44
|
+
return concat([node.beging].concat(docs).concat(node.ending));
|
45
|
+
}
|
36
46
|
|
37
|
-
return concat(
|
47
|
+
return concat(
|
48
|
+
[useBraces ? "%r{" : "/"]
|
49
|
+
.concat(docs)
|
50
|
+
.concat(useBraces ? "}" : "/", node.ending.slice(1))
|
51
|
+
);
|
38
52
|
}
|
39
53
|
|
40
54
|
module.exports = {
|
data/src/nodes/statements.js
CHANGED
@@ -45,35 +45,45 @@ function printBodyStmt(path, opts, print) {
|
|
45
45
|
return group(concat(parts));
|
46
46
|
}
|
47
47
|
|
48
|
+
const argNodeTypes = ["args", "args_add_star", "args_add_block"];
|
49
|
+
|
50
|
+
function printParen(path, opts, print) {
|
51
|
+
const contentNode = path.getValue().body[0];
|
52
|
+
|
53
|
+
if (!contentNode) {
|
54
|
+
return "()";
|
55
|
+
}
|
56
|
+
|
57
|
+
let contentDoc = path.call(print, "body", 0);
|
58
|
+
|
59
|
+
// If the content is params, we're going to let it handle its own parentheses
|
60
|
+
// so that it breaks nicely.
|
61
|
+
if (contentNode.type === "params") {
|
62
|
+
return contentDoc;
|
63
|
+
}
|
64
|
+
|
65
|
+
// If we have an arg type node as the contents, then it's going to return an
|
66
|
+
// array, so we need to explicitly join that content here.
|
67
|
+
if (argNodeTypes.includes(contentNode.type)) {
|
68
|
+
contentDoc = join(concat([",", line]), contentDoc);
|
69
|
+
}
|
70
|
+
|
71
|
+
return group(
|
72
|
+
concat([
|
73
|
+
"(",
|
74
|
+
indent(concat([softline, contentDoc])),
|
75
|
+
concat([softline, ")"])
|
76
|
+
])
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
48
80
|
module.exports = {
|
49
81
|
"@__end__": (path, _opts, _print) => {
|
50
82
|
const { body } = path.getValue();
|
51
83
|
return concat([trim, "__END__", literalline, body]);
|
52
84
|
},
|
53
85
|
bodystmt: printBodyStmt,
|
54
|
-
paren:
|
55
|
-
if (!path.getValue().body[0]) {
|
56
|
-
return "()";
|
57
|
-
}
|
58
|
-
|
59
|
-
let content = path.call(print, "body", 0);
|
60
|
-
|
61
|
-
if (
|
62
|
-
["args", "args_add_star", "args_add_block"].includes(
|
63
|
-
path.getValue().body[0].type
|
64
|
-
)
|
65
|
-
) {
|
66
|
-
content = join(concat([",", line]), content);
|
67
|
-
}
|
68
|
-
|
69
|
-
return group(
|
70
|
-
concat([
|
71
|
-
"(",
|
72
|
-
indent(concat([softline, content])),
|
73
|
-
concat([softline, ")"])
|
74
|
-
])
|
75
|
-
);
|
76
|
-
},
|
86
|
+
paren: printParen,
|
77
87
|
program: (path, opts, print) =>
|
78
88
|
concat([join(hardline, path.map(print, "body")), hardline]),
|
79
89
|
stmts: (path, opts, print) => {
|
@@ -88,8 +98,8 @@ module.exports = {
|
|
88
98
|
stmts[0].comments
|
89
99
|
) {
|
90
100
|
const comments = path.map(
|
91
|
-
(commentPath
|
92
|
-
|
101
|
+
(commentPath) => {
|
102
|
+
commentPath.getValue().printed = true;
|
93
103
|
return opts.printer.printComment(commentPath);
|
94
104
|
},
|
95
105
|
"body",
|