prettier 1.0.0 → 1.2.2
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 +59 -1
- data/README.md +5 -0
- data/lib/prettier.rb +2 -2
- data/node_modules/prettier/index.js +54 -54
- data/package.json +3 -1
- data/src/nodes/arrays.js +2 -21
- data/src/nodes/assign.js +5 -1
- data/src/nodes/calls.js +46 -7
- data/src/nodes/case.js +53 -49
- data/src/nodes/hashes.js +8 -31
- data/src/nodes/loops.js +66 -66
- data/src/nodes/methods.js +29 -4
- data/src/nodes/patterns.js +37 -9
- data/src/nodes/regexp.js +30 -23
- data/src/nodes/return.js +33 -22
- data/src/nodes/statements.js +2 -2
- data/src/parser.rb +71 -11
- data/src/printer.js +45 -0
- data/src/utils.js +2 -0
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +4 -3
data/src/nodes/case.js
CHANGED
@@ -8,54 +8,58 @@ const {
|
|
8
8
|
line
|
9
9
|
} = require("../prettier");
|
10
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
}
|
58
|
-
|
59
|
-
return group(concat(parts));
|
11
|
+
function printCase(path, opts, print) {
|
12
|
+
const statement = ["case"];
|
13
|
+
|
14
|
+
// You don't need to explicitly have something to test against in a case
|
15
|
+
// statement (without it it effectively becomes an if/elsif chain).
|
16
|
+
if (path.getValue().body[0]) {
|
17
|
+
statement.push(" ", path.call(print, "body", 0));
|
18
|
+
}
|
19
|
+
|
20
|
+
return concat(
|
21
|
+
statement.concat([hardline, path.call(print, "body", 1), hardline, "end"])
|
22
|
+
);
|
23
|
+
}
|
24
|
+
|
25
|
+
function printWhen(path, opts, print) {
|
26
|
+
const [_preds, _stmts, addition] = path.getValue().body;
|
27
|
+
|
28
|
+
// The `fill` builder command expects an array of docs alternating with
|
29
|
+
// line breaks. This is so it can loop through and determine where to break.
|
30
|
+
const preds = fill(
|
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)
|
42
|
+
);
|
43
|
+
|
44
|
+
const stmts = path.call(print, "body", 1);
|
45
|
+
const parts = [concat(["when ", align("when ".length, preds)])];
|
46
|
+
|
47
|
+
// It's possible in a when to just have empty void statements, in which case
|
48
|
+
// we would skip adding the body.
|
49
|
+
if (!stmts.parts.every((part) => !part)) {
|
50
|
+
parts.push(indent(concat([hardline, stmts])));
|
51
|
+
}
|
52
|
+
|
53
|
+
// This is the next clause on the case statement, either another `when` or
|
54
|
+
// an `else` clause.
|
55
|
+
if (addition) {
|
56
|
+
parts.push(hardline, path.call(print, "body", 2));
|
60
57
|
}
|
58
|
+
|
59
|
+
return group(concat(parts));
|
60
|
+
}
|
61
|
+
|
62
|
+
module.exports = {
|
63
|
+
case: printCase,
|
64
|
+
when: printWhen
|
61
65
|
};
|
data/src/nodes/hashes.js
CHANGED
@@ -1,14 +1,11 @@
|
|
1
|
-
const {
|
2
|
-
concat,
|
3
|
-
group,
|
4
|
-
hardline,
|
5
|
-
ifBreak,
|
6
|
-
indent,
|
7
|
-
join,
|
8
|
-
line
|
9
|
-
} = require("../prettier");
|
1
|
+
const { concat, group, ifBreak, indent, join, line } = require("../prettier");
|
10
2
|
|
11
|
-
const {
|
3
|
+
const {
|
4
|
+
getTrailingComma,
|
5
|
+
prefix,
|
6
|
+
printEmptyCollection,
|
7
|
+
skipAssignIndent
|
8
|
+
} = require("../utils");
|
12
9
|
|
13
10
|
// When attempting to convert a hash rocket into a hash label, you need to take
|
14
11
|
// care because only certain patterns are allowed. Ruby source says that they
|
@@ -96,26 +93,6 @@ function printHashContents(path, opts, print) {
|
|
96
93
|
return join(concat([",", line]), path.map(print, "body"));
|
97
94
|
}
|
98
95
|
|
99
|
-
function printEmptyHashWithComments(path, opts) {
|
100
|
-
const hashNode = path.getValue();
|
101
|
-
|
102
|
-
const printComment = (commentPath, index) => {
|
103
|
-
hashNode.comments[index].printed = true;
|
104
|
-
return opts.printer.printComment(commentPath);
|
105
|
-
};
|
106
|
-
|
107
|
-
return group(
|
108
|
-
concat([
|
109
|
-
"{",
|
110
|
-
indent(
|
111
|
-
concat([hardline, join(hardline, path.map(printComment, "comments"))])
|
112
|
-
),
|
113
|
-
line,
|
114
|
-
"}"
|
115
|
-
])
|
116
|
-
);
|
117
|
-
}
|
118
|
-
|
119
96
|
function printHash(path, opts, print) {
|
120
97
|
const hashNode = path.getValue();
|
121
98
|
|
@@ -123,7 +100,7 @@ function printHash(path, opts, print) {
|
|
123
100
|
// missing, then it means we're dealing with an empty hash, so we can just
|
124
101
|
// exit here and print.
|
125
102
|
if (hashNode.body[0] === null) {
|
126
|
-
return
|
103
|
+
return printEmptyCollection(path, opts, "{", "}");
|
127
104
|
}
|
128
105
|
|
129
106
|
return group(
|
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) {
|
@@ -16,8 +16,7 @@ function printMethod(offset) {
|
|
16
16
|
}
|
17
17
|
|
18
18
|
// In case there are no parens but there are arguments
|
19
|
-
const parens =
|
20
|
-
params.type === "params" && params.body.some((paramType) => paramType);
|
19
|
+
const parens = params.type === "params" && params.body.some((type) => type);
|
21
20
|
|
22
21
|
declaration.push(
|
23
22
|
path.call(print, "body", offset),
|
@@ -48,8 +47,34 @@ function printMethod(offset) {
|
|
48
47
|
};
|
49
48
|
}
|
50
49
|
|
50
|
+
function printSingleLineMethod(path, opts, print) {
|
51
|
+
let paramsNode = path.getValue().body[1];
|
52
|
+
let paramsDoc = "";
|
53
|
+
|
54
|
+
if (paramsNode) {
|
55
|
+
if (paramsNode.body[0].type === "params") {
|
56
|
+
paramsNode = paramsNode.body[0];
|
57
|
+
}
|
58
|
+
|
59
|
+
if (paramsNode.type === "params" && paramsNode.body.some((type) => type)) {
|
60
|
+
paramsDoc = path.call(print, "body", 1);
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
return group(
|
65
|
+
concat([
|
66
|
+
"def ",
|
67
|
+
path.call(print, "body", 0),
|
68
|
+
paramsDoc,
|
69
|
+
" =",
|
70
|
+
indent(group(concat([line, path.call(print, "body", 2)])))
|
71
|
+
])
|
72
|
+
);
|
73
|
+
}
|
74
|
+
|
51
75
|
module.exports = {
|
52
76
|
access_ctrl: first,
|
53
77
|
def: printMethod(0),
|
54
|
-
defs: printMethod(2)
|
78
|
+
defs: printMethod(2),
|
79
|
+
defsl: printSingleLineMethod
|
55
80
|
};
|
data/src/nodes/patterns.js
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
const { concat, group, hardline, indent, join, line } = require("../prettier");
|
2
2
|
|
3
|
+
const patterns = ["aryptn", "binary", "fndptn", "hshptn", "rassign"];
|
4
|
+
|
3
5
|
function printPatternArg(path, opts, print) {
|
4
6
|
// Pinning is a really special syntax in pattern matching that's not really
|
5
7
|
// all that well supported in ripper. Here we're just going to the original
|
@@ -34,10 +36,7 @@ function printAryPtn(path, opts, print) {
|
|
34
36
|
|
35
37
|
args = group(join(concat([",", line]), args));
|
36
38
|
|
37
|
-
if (
|
38
|
-
constant ||
|
39
|
-
["aryptn", "binary", "hshptn"].includes(path.getParentNode().type)
|
40
|
-
) {
|
39
|
+
if (constant || patterns.includes(path.getParentNode().type)) {
|
41
40
|
args = concat(["[", args, "]"]);
|
42
41
|
}
|
43
42
|
|
@@ -48,6 +47,22 @@ function printAryPtn(path, opts, print) {
|
|
48
47
|
return args;
|
49
48
|
}
|
50
49
|
|
50
|
+
function printFndPtn(path, opts, print) {
|
51
|
+
const [constant] = path.getValue().body;
|
52
|
+
|
53
|
+
let args = [concat(["*", path.call(print, "body", 1)])]
|
54
|
+
.concat(path.map(print, "body", 2))
|
55
|
+
.concat(concat(["*", path.call(print, "body", 3)]));
|
56
|
+
|
57
|
+
args = concat(["[", group(join(concat([",", line]), args)), "]"]);
|
58
|
+
|
59
|
+
if (constant) {
|
60
|
+
return concat([path.call(print, "body", 0), args]);
|
61
|
+
}
|
62
|
+
|
63
|
+
return args;
|
64
|
+
}
|
65
|
+
|
51
66
|
function printHshPtn(path, opts, print) {
|
52
67
|
const [constant, keyValuePairs, keyValueRest] = path.getValue().body;
|
53
68
|
let args = [];
|
@@ -80,10 +95,8 @@ function printHshPtn(path, opts, print) {
|
|
80
95
|
|
81
96
|
if (constant) {
|
82
97
|
args = concat(["[", args, "]"]);
|
83
|
-
} else if (
|
84
|
-
["
|
85
|
-
) {
|
86
|
-
args = concat(["{", args, "}"]);
|
98
|
+
} else if (patterns.includes(path.getParentNode().type)) {
|
99
|
+
args = concat(["{ ", args, " }"]);
|
87
100
|
}
|
88
101
|
|
89
102
|
if (constant) {
|
@@ -111,8 +124,23 @@ function printIn(path, opts, print) {
|
|
111
124
|
return group(concat(parts));
|
112
125
|
}
|
113
126
|
|
127
|
+
function printRAssign(path, opts, print) {
|
128
|
+
const { keyword } = path.getValue();
|
129
|
+
const [leftDoc, rightDoc] = path.map(print, "body");
|
130
|
+
|
131
|
+
return group(
|
132
|
+
concat([
|
133
|
+
leftDoc,
|
134
|
+
keyword ? " in" : " =>",
|
135
|
+
group(indent(concat([line, rightDoc])))
|
136
|
+
])
|
137
|
+
);
|
138
|
+
}
|
139
|
+
|
114
140
|
module.exports = {
|
115
141
|
aryptn: printAryPtn,
|
142
|
+
fndptn: printFndPtn,
|
116
143
|
hshptn: printHshPtn,
|
117
|
-
in: printIn
|
144
|
+
in: printIn,
|
145
|
+
rassign: printRAssign
|
118
146
|
};
|
data/src/nodes/regexp.js
CHANGED
@@ -1,28 +1,25 @@
|
|
1
1
|
const { concat } = require("../prettier");
|
2
2
|
const { hasAncestor } = require("../utils");
|
3
3
|
|
4
|
-
function
|
5
|
-
return node.
|
4
|
+
function hasContent(node, pattern) {
|
5
|
+
return node.body.some(
|
6
|
+
(child) => child.type === "@tstring_content" && pattern.test(child.body)
|
7
|
+
);
|
6
8
|
}
|
7
9
|
|
8
|
-
|
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) {
|
9
15
|
const node = path.getValue();
|
10
|
-
const
|
11
|
-
|
12
|
-
// If the first part of this regex is plain string content and we have a
|
13
|
-
// space or an =, then we want to use braces because otherwise we could end up
|
14
|
-
// with an ambiguous operator, e.g. foo / bar/ or foo /=bar/
|
15
|
-
if (
|
16
|
-
first &&
|
17
|
-
isStringContent(first) &&
|
18
|
-
[" ", "="].includes(first.body[0]) &&
|
19
|
-
hasAncestor(path, ["command", "command_call"])
|
20
|
-
) {
|
21
|
-
return true;
|
22
|
-
}
|
16
|
+
const firstChildNode = node.body[0];
|
23
17
|
|
24
|
-
return
|
25
|
-
|
18
|
+
return (
|
19
|
+
firstChildNode &&
|
20
|
+
firstChildNode.type === "@tstring_content" &&
|
21
|
+
[" ", "="].includes(firstChildNode.body[0]) &&
|
22
|
+
hasAncestor(path, ["command", "command_call"])
|
26
23
|
);
|
27
24
|
}
|
28
25
|
|
@@ -35,13 +32,23 @@ function shouldUseBraces(path) {
|
|
35
32
|
// itself. In that case we switch over to using %r with braces.
|
36
33
|
function printRegexpLiteral(path, opts, print) {
|
37
34
|
const node = path.getValue();
|
38
|
-
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, /\//);
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
+
}
|
43
46
|
|
44
|
-
return concat(
|
47
|
+
return concat(
|
48
|
+
[useBraces ? "%r{" : "/"]
|
49
|
+
.concat(docs)
|
50
|
+
.concat(useBraces ? "}" : "/", node.ending.slice(1))
|
51
|
+
);
|
45
52
|
}
|
46
53
|
|
47
54
|
module.exports = {
|