prettier 0.21.0 → 1.0.1
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 +112 -7
- data/CONTRIBUTING.md +4 -4
- data/README.md +18 -14
- data/package.json +9 -6
- data/src/embed.js +27 -8
- data/src/nodes.js +5 -2
- data/src/nodes/alias.js +29 -31
- data/src/nodes/aref.js +26 -26
- data/src/nodes/args.js +55 -47
- data/src/nodes/arrays.js +132 -106
- data/src/nodes/assign.js +32 -32
- data/src/nodes/blocks.js +8 -3
- data/src/nodes/calls.js +163 -60
- data/src/nodes/case.js +11 -7
- data/src/nodes/class.js +74 -0
- data/src/nodes/commands.js +36 -31
- data/src/nodes/conditionals.js +44 -30
- data/src/nodes/constants.js +39 -21
- data/src/nodes/flow.js +11 -1
- data/src/nodes/hashes.js +90 -109
- data/src/nodes/heredocs.js +34 -0
- data/src/nodes/hooks.js +21 -22
- data/src/nodes/ints.js +27 -20
- data/src/nodes/lambdas.js +14 -27
- data/src/nodes/loops.js +10 -5
- data/src/nodes/massign.js +87 -65
- data/src/nodes/methods.js +48 -73
- data/src/nodes/operators.js +70 -39
- data/src/nodes/params.js +26 -16
- data/src/nodes/patterns.js +108 -33
- data/src/nodes/regexp.js +45 -14
- data/src/nodes/rescue.js +72 -59
- data/src/nodes/statements.js +86 -44
- data/src/nodes/strings.js +95 -85
- data/src/nodes/super.js +35 -0
- data/src/nodes/undef.js +42 -0
- data/src/parser.js +86 -0
- data/src/parser.rb +2400 -621
- data/src/printer.js +90 -0
- data/src/ruby.js +19 -41
- data/src/toProc.js +4 -4
- data/src/utils.js +24 -88
- data/src/utils/literalLineNoBreak.js +7 -0
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +12 -49
- data/src/nodes/scopes.js +0 -61
- data/src/parse.js +0 -37
- data/src/print.js +0 -23
data/src/nodes/statements.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
const {
|
2
|
+
breakParent,
|
2
3
|
concat,
|
3
4
|
dedent,
|
4
5
|
group,
|
@@ -11,63 +12,104 @@ const {
|
|
11
12
|
trim
|
12
13
|
} = require("../prettier");
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
return concat([trim, "__END__", literalline, body]);
|
18
|
-
},
|
19
|
-
bodystmt: (path, opts, print) => {
|
20
|
-
const [_statements, rescue, elseClause, ensure] = path.getValue().body;
|
21
|
-
const parts = [path.call(print, "body", 0)];
|
15
|
+
function printBodyStmt(path, opts, print) {
|
16
|
+
const [stmts, rescue, elseClause, ensure] = path.getValue().body;
|
17
|
+
const parts = [];
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
if (
|
20
|
+
stmts.body.length > 1 ||
|
21
|
+
stmts.body[0].type != "void_stmt" ||
|
22
|
+
stmts.body[0].comments
|
23
|
+
) {
|
24
|
+
parts.push(path.call(print, "body", 0));
|
25
|
+
}
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
elseClause.type === "else"
|
31
|
-
? path.call(print, "body", 2, "body", 0)
|
32
|
-
: path.call(print, "body", 2);
|
27
|
+
if (rescue) {
|
28
|
+
parts.push(dedent(concat([hardline, path.call(print, "body", 1)])));
|
29
|
+
}
|
33
30
|
|
34
|
-
|
35
|
-
|
31
|
+
if (elseClause) {
|
32
|
+
// Before Ruby 2.6, this piece of bodystmt was an explicit "else" node
|
33
|
+
const stmts =
|
34
|
+
elseClause.type === "else"
|
35
|
+
? path.call(print, "body", 2, "body", 0)
|
36
|
+
: path.call(print, "body", 2);
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
}
|
38
|
+
parts.push(concat([dedent(concat([hardline, "else"])), hardline, stmts]));
|
39
|
+
}
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
paren: (path, opts, print) => {
|
45
|
-
if (!path.getValue().body[0]) {
|
46
|
-
return "()";
|
47
|
-
}
|
41
|
+
if (ensure) {
|
42
|
+
parts.push(dedent(concat([hardline, path.call(print, "body", 3)])));
|
43
|
+
}
|
48
44
|
|
49
|
-
|
45
|
+
return group(concat(parts));
|
46
|
+
}
|
50
47
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
) {
|
56
|
-
content = join(concat([",", line]), content);
|
57
|
-
}
|
48
|
+
const argNodeTypes = ["args", "args_add_star", "args_add_block"];
|
49
|
+
|
50
|
+
function printParen(path, opts, print) {
|
51
|
+
const contentNode = path.getValue().body[0];
|
58
52
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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
|
+
|
80
|
+
module.exports = {
|
81
|
+
"@__end__": (path, _opts, _print) => {
|
82
|
+
const { body } = path.getValue();
|
83
|
+
return concat([trim, "__END__", literalline, body]);
|
66
84
|
},
|
85
|
+
bodystmt: printBodyStmt,
|
86
|
+
paren: printParen,
|
67
87
|
program: (path, opts, print) =>
|
68
88
|
concat([join(hardline, path.map(print, "body")), hardline]),
|
69
89
|
stmts: (path, opts, print) => {
|
70
90
|
const stmts = path.getValue().body;
|
91
|
+
|
92
|
+
// This is a special case where we have only comments inside a statement
|
93
|
+
// list. In this case we want to avoid doing any kind of line number
|
94
|
+
// tracking and just print out the comments.
|
95
|
+
if (
|
96
|
+
stmts.length === 1 &&
|
97
|
+
stmts[0].type === "void_stmt" &&
|
98
|
+
stmts[0].comments
|
99
|
+
) {
|
100
|
+
const comments = path.map(
|
101
|
+
(commentPath) => {
|
102
|
+
commentPath.getValue().printed = true;
|
103
|
+
return opts.printer.printComment(commentPath);
|
104
|
+
},
|
105
|
+
"body",
|
106
|
+
0,
|
107
|
+
"comments"
|
108
|
+
);
|
109
|
+
|
110
|
+
return concat([breakParent, join(hardline, comments)]);
|
111
|
+
}
|
112
|
+
|
71
113
|
const parts = [];
|
72
114
|
let lineNo = null;
|
73
115
|
|
data/src/nodes/strings.js
CHANGED
@@ -8,31 +8,31 @@ const {
|
|
8
8
|
join
|
9
9
|
} = require("../prettier");
|
10
10
|
|
11
|
-
const { concatBody, empty, makeList, prefix, surround } = require("../utils");
|
12
|
-
|
13
11
|
// If there is some part of this string that matches an escape sequence or that
|
14
12
|
// contains the interpolation pattern ("#{"), then we are locked into whichever
|
15
13
|
// quote the user chose. (If they chose single quotes, then double quoting
|
16
14
|
// would activate the escape sequence, and if they chose double quotes, then
|
17
15
|
// single quotes would deactivate it.)
|
18
|
-
|
19
|
-
|
16
|
+
function isQuoteLocked(node) {
|
17
|
+
return node.body.some(
|
20
18
|
(part) =>
|
21
19
|
part.type === "@tstring_content" &&
|
22
20
|
(part.body.includes("#{") || part.body.includes("\\"))
|
23
21
|
);
|
22
|
+
}
|
24
23
|
|
25
24
|
// A string is considered to be able to use single quotes if it contains only
|
26
25
|
// plain string content and that content does not contain a single quote.
|
27
|
-
|
28
|
-
|
26
|
+
function isSingleQuotable(node) {
|
27
|
+
return node.body.every(
|
29
28
|
(part) => part.type === "@tstring_content" && !part.body.includes("'")
|
30
29
|
);
|
30
|
+
}
|
31
31
|
|
32
32
|
const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
|
33
33
|
|
34
|
-
|
35
|
-
const replaceOther =
|
34
|
+
function normalizeQuotes(content, enclosingQuote, originalQuote) {
|
35
|
+
const replaceOther = originalQuote === '"';
|
36
36
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
37
37
|
|
38
38
|
// Escape and unescape single and double quotes as needed to be able to
|
@@ -52,7 +52,7 @@ const normalizeQuotes = (content, enclosingQuote, originalQuote) => {
|
|
52
52
|
|
53
53
|
return `\\${escaped}`;
|
54
54
|
});
|
55
|
-
}
|
55
|
+
}
|
56
56
|
|
57
57
|
const quotePairs = {
|
58
58
|
"(": ")",
|
@@ -61,51 +61,100 @@ const quotePairs = {
|
|
61
61
|
"<": ">"
|
62
62
|
};
|
63
63
|
|
64
|
-
|
64
|
+
function getClosingQuote(quote) {
|
65
65
|
if (!quote.startsWith("%")) {
|
66
66
|
return quote;
|
67
67
|
}
|
68
68
|
|
69
|
-
const boundary = /%
|
69
|
+
const boundary = /%[Qq]?(.)/.exec(quote)[1];
|
70
70
|
if (boundary in quotePairs) {
|
71
71
|
return quotePairs[boundary];
|
72
72
|
}
|
73
73
|
|
74
74
|
return boundary;
|
75
|
-
}
|
75
|
+
}
|
76
76
|
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
// Prints a @CHAR node. @CHAR nodes are special character strings that usually
|
78
|
+
// are strings of length 1. If they're any longer than we'll try to apply the
|
79
|
+
// correct quotes.
|
80
|
+
function printChar(path, { rubySingleQuote }, _print) {
|
81
|
+
const { body } = path.getValue();
|
80
82
|
|
81
|
-
|
82
|
-
|
83
|
+
if (body.length !== 2) {
|
84
|
+
return body;
|
85
|
+
}
|
86
|
+
|
87
|
+
const quote = rubySingleQuote ? "'" : '"';
|
88
|
+
return concat([quote, body.slice(1), quote]);
|
89
|
+
}
|
90
|
+
|
91
|
+
// Prints a dynamic symbol. Assumes there's a quote property attached to the
|
92
|
+
// node that will tell us which quote to use when printing. We're just going to
|
93
|
+
// use whatever quote was provided.
|
94
|
+
function printDynaSymbol(path, opts, print) {
|
95
|
+
const { quote } = path.getValue();
|
96
|
+
|
97
|
+
return concat([":", quote].concat(path.map(print, "body")).concat(quote));
|
98
|
+
}
|
99
|
+
|
100
|
+
// Prints out an interpolated variable in the string by converting it into an
|
101
|
+
// embedded expression.
|
102
|
+
function printStringDVar(path, opts, print) {
|
103
|
+
return concat(["#{", path.call(print, "body", 0), "}"]);
|
104
|
+
}
|
105
|
+
|
106
|
+
// Prints out a literal string. This function does its best to respect the
|
107
|
+
// wishes of the user with regards to single versus double quotes, but if the
|
108
|
+
// string contains any escape expressions then it will just keep the original
|
109
|
+
// quotes.
|
110
|
+
function printStringLiteral(path, { rubySingleQuote }, print) {
|
111
|
+
const node = path.getValue();
|
112
|
+
|
113
|
+
// If the string is empty, it will not have any parts, so just print out the
|
114
|
+
// quotes corresponding to the config
|
115
|
+
if (node.body.length === 0) {
|
116
|
+
return rubySingleQuote ? "''" : '""';
|
117
|
+
}
|
118
|
+
|
119
|
+
// Determine the quote that should enclose the new string
|
120
|
+
let quote;
|
121
|
+
if (isQuoteLocked(node)) {
|
122
|
+
quote = node.quote;
|
123
|
+
} else {
|
124
|
+
quote = rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
|
125
|
+
}
|
126
|
+
|
127
|
+
const parts = node.body.map((part, index) => {
|
128
|
+
if (part.type !== "@tstring_content") {
|
129
|
+
// In this case, the part of the string is an embedded expression
|
130
|
+
return path.call(print, "body", index);
|
83
131
|
}
|
84
132
|
|
85
|
-
|
86
|
-
return
|
87
|
-
|
88
|
-
|
89
|
-
|
133
|
+
// In this case, the part of the string is just regular string content
|
134
|
+
return join(
|
135
|
+
literalline,
|
136
|
+
normalizeQuotes(part.body, quote, node.quote).split("\n")
|
137
|
+
);
|
138
|
+
});
|
90
139
|
|
91
|
-
|
92
|
-
|
93
|
-
heredoc: (path, opts, print) => {
|
94
|
-
const { beging, body, ending } = path.getValue();
|
140
|
+
return concat([quote].concat(parts).concat(getClosingQuote(quote)));
|
141
|
+
}
|
95
142
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
143
|
+
// Prints out a symbol literal. Its child will always be the ident that
|
144
|
+
// represents the string content of the symbol.
|
145
|
+
function printSymbolLiteral(path, opts, print) {
|
146
|
+
return concat([":", path.call(print, "body", 0)]);
|
147
|
+
}
|
101
148
|
|
102
|
-
|
103
|
-
|
104
|
-
|
149
|
+
// Prints out an xstring literal. Its child is an array of string parts,
|
150
|
+
// including plain string content and interpolated content.
|
151
|
+
function printXStringLiteral(path, opts, print) {
|
152
|
+
return concat(["`"].concat(path.map(print, "body")).concat("`"));
|
153
|
+
}
|
105
154
|
|
106
|
-
|
107
|
-
|
108
|
-
|
155
|
+
module.exports = {
|
156
|
+
"@CHAR": printChar,
|
157
|
+
dyna_symbol: printDynaSymbol,
|
109
158
|
string_concat: (path, opts, print) =>
|
110
159
|
group(
|
111
160
|
concat([
|
@@ -114,14 +163,15 @@ module.exports = {
|
|
114
163
|
indent(concat([hardline, path.call(print, "body", 1)]))
|
115
164
|
])
|
116
165
|
),
|
117
|
-
string_dvar:
|
166
|
+
string_dvar: printStringDVar,
|
118
167
|
string_embexpr: (path, opts, print) => {
|
119
168
|
const parts = path.call(print, "body", 0);
|
120
169
|
|
121
|
-
// If the interpolated expression is inside of
|
122
|
-
// that gets sent to the command line) then we don't want
|
123
|
-
// indent, as this can lead to some very odd looking
|
124
|
-
|
170
|
+
// If the interpolated expression is inside of a heredoc or an xstring
|
171
|
+
// literal (a string that gets sent to the command line) then we don't want
|
172
|
+
// to automatically indent, as this can lead to some very odd looking
|
173
|
+
// expressions
|
174
|
+
if (["heredoc", "xstring_literal"].includes(path.getParentNode().type)) {
|
125
175
|
return concat(["#{", parts, "}"]);
|
126
176
|
}
|
127
177
|
|
@@ -129,47 +179,7 @@ module.exports = {
|
|
129
179
|
concat(["#{", indent(concat([softline, parts])), concat([softline, "}"])])
|
130
180
|
);
|
131
181
|
},
|
132
|
-
string_literal:
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
// If the string is empty, it will not have any parts, so just print out the
|
137
|
-
// quotes corresponding to the config
|
138
|
-
if (string.body.length === 0) {
|
139
|
-
return preferSingleQuotes ? "''" : '""';
|
140
|
-
}
|
141
|
-
|
142
|
-
// Determine the quote that should enclose the new string
|
143
|
-
let quote;
|
144
|
-
if (isQuoteLocked(string)) {
|
145
|
-
({ quote } = stringLiteral);
|
146
|
-
} else {
|
147
|
-
quote = preferSingleQuotes && isSingleQuotable(string) ? "'" : '"';
|
148
|
-
}
|
149
|
-
|
150
|
-
const parts = string.body.map((part, index) => {
|
151
|
-
if (part.type !== "@tstring_content") {
|
152
|
-
// In this case, the part of the string is an embedded expression
|
153
|
-
return path.call(print, "body", 0, "body", index);
|
154
|
-
}
|
155
|
-
|
156
|
-
// In this case, the part of the string is just regular string content
|
157
|
-
return join(
|
158
|
-
literalline,
|
159
|
-
normalizeQuotes(part.body, quote, stringLiteral.quote).split("\n")
|
160
|
-
);
|
161
|
-
});
|
162
|
-
|
163
|
-
return concat([quote].concat(parts).concat(getClosingQuote(quote)));
|
164
|
-
},
|
165
|
-
symbol: prefix(":"),
|
166
|
-
symbol_literal: concatBody,
|
167
|
-
word_add: concatBody,
|
168
|
-
word_new: empty,
|
169
|
-
xstring: makeList,
|
170
|
-
xstring_literal: (path, opts, print) => {
|
171
|
-
const parts = path.call(print, "body", 0);
|
172
|
-
|
173
|
-
return concat(["`"].concat(parts).concat("`"));
|
174
|
-
}
|
182
|
+
string_literal: printStringLiteral,
|
183
|
+
symbol_literal: printSymbolLiteral,
|
184
|
+
xstring_literal: printXStringLiteral
|
175
185
|
};
|
data/src/nodes/super.js
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
const { align, concat, group, join, line } = require("../prettier");
|
2
|
+
const { literal } = require("../utils");
|
3
|
+
|
4
|
+
function printSuper(path, opts, print) {
|
5
|
+
const args = path.getValue().body[0];
|
6
|
+
|
7
|
+
if (args.type === "arg_paren") {
|
8
|
+
// In case there are explicitly no arguments but they are using parens,
|
9
|
+
// we assume they are attempting to override the initializer and pass no
|
10
|
+
// arguments up.
|
11
|
+
if (args.body[0] === null) {
|
12
|
+
return "super()";
|
13
|
+
}
|
14
|
+
|
15
|
+
return concat(["super", path.call(print, "body", 0)]);
|
16
|
+
}
|
17
|
+
|
18
|
+
const keyword = "super ";
|
19
|
+
const argsDocs = path.call(print, "body", 0);
|
20
|
+
|
21
|
+
return group(
|
22
|
+
concat([
|
23
|
+
keyword,
|
24
|
+
align(keyword.length, group(join(concat([",", line]), argsDocs)))
|
25
|
+
])
|
26
|
+
);
|
27
|
+
}
|
28
|
+
|
29
|
+
// Version of super without any parens or args.
|
30
|
+
const printZSuper = literal("super");
|
31
|
+
|
32
|
+
module.exports = {
|
33
|
+
super: printSuper,
|
34
|
+
zsuper: printZSuper
|
35
|
+
};
|
data/src/nodes/undef.js
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
const {
|
2
|
+
addTrailingComment,
|
3
|
+
align,
|
4
|
+
concat,
|
5
|
+
group,
|
6
|
+
join,
|
7
|
+
line
|
8
|
+
} = require("../prettier");
|
9
|
+
|
10
|
+
function printUndefSymbol(path, opts, print) {
|
11
|
+
const node = path.getValue();
|
12
|
+
|
13
|
+
// Since we're going to descend into the symbol literal to grab out the ident
|
14
|
+
// node, then we need to make sure we copy over any comments as well,
|
15
|
+
// otherwise we could accidentally skip printing them.
|
16
|
+
if (node.comments) {
|
17
|
+
node.comments.forEach((comment) => {
|
18
|
+
addTrailingComment(node.body[0], comment);
|
19
|
+
});
|
20
|
+
}
|
21
|
+
|
22
|
+
return path.call(print, "body", 0);
|
23
|
+
}
|
24
|
+
|
25
|
+
function printUndef(path, opts, print) {
|
26
|
+
const keyword = "undef ";
|
27
|
+
const argNodes = path.map(
|
28
|
+
(symbolPath) => printUndefSymbol(symbolPath, opts, print),
|
29
|
+
"body"
|
30
|
+
);
|
31
|
+
|
32
|
+
return group(
|
33
|
+
concat([
|
34
|
+
keyword,
|
35
|
+
align(keyword.length, join(concat([",", line]), argNodes))
|
36
|
+
])
|
37
|
+
);
|
38
|
+
}
|
39
|
+
|
40
|
+
module.exports = {
|
41
|
+
undef: printUndef
|
42
|
+
};
|