prettier 1.6.1 → 2.0.0.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +316 -293
  3. data/CONTRIBUTING.md +6 -9
  4. data/LICENSE +1 -1
  5. data/README.md +11 -12
  6. data/dist/haml/embed.js +53 -0
  7. data/dist/haml/parser.js +31 -0
  8. data/{src → dist}/haml/parser.rb +0 -0
  9. data/dist/haml/printer.js +336 -0
  10. data/dist/parser/getInfo.js +17 -0
  11. data/{src → dist}/parser/netcat.js +1 -0
  12. data/dist/parser/parseSync.js +128 -0
  13. data/dist/parser/server.rb +140 -0
  14. data/dist/plugin.js +143 -0
  15. data/dist/prettier.js +15 -0
  16. data/dist/rbs/parser.js +34 -0
  17. data/{src → dist}/rbs/parser.rb +0 -0
  18. data/dist/rbs/printer.js +517 -0
  19. data/dist/ruby/embed.js +110 -0
  20. data/dist/ruby/nodes/alias.js +59 -0
  21. data/{src → dist}/ruby/nodes/aref.js +26 -35
  22. data/dist/ruby/nodes/args.js +165 -0
  23. data/dist/ruby/nodes/arrays.js +126 -0
  24. data/dist/ruby/nodes/assign.js +41 -0
  25. data/dist/ruby/nodes/blocks.js +68 -0
  26. data/dist/ruby/nodes/calls.js +220 -0
  27. data/dist/ruby/nodes/case.js +50 -0
  28. data/dist/ruby/nodes/class.js +54 -0
  29. data/dist/ruby/nodes/commands.js +124 -0
  30. data/dist/ruby/nodes/conditionals.js +242 -0
  31. data/dist/ruby/nodes/constants.js +38 -0
  32. data/dist/ruby/nodes/flow.js +66 -0
  33. data/dist/ruby/nodes/hashes.js +130 -0
  34. data/dist/ruby/nodes/heredocs.js +30 -0
  35. data/dist/ruby/nodes/hooks.js +35 -0
  36. data/dist/ruby/nodes/ints.js +27 -0
  37. data/dist/ruby/nodes/lambdas.js +69 -0
  38. data/dist/ruby/nodes/loops.js +73 -0
  39. data/dist/ruby/nodes/massign.js +73 -0
  40. data/dist/ruby/nodes/methods.js +70 -0
  41. data/dist/ruby/nodes/operators.js +70 -0
  42. data/dist/ruby/nodes/params.js +89 -0
  43. data/dist/ruby/nodes/patterns.js +109 -0
  44. data/dist/ruby/nodes/regexp.js +45 -0
  45. data/dist/ruby/nodes/rescue.js +82 -0
  46. data/dist/ruby/nodes/return.js +75 -0
  47. data/dist/ruby/nodes/statements.js +111 -0
  48. data/dist/ruby/nodes/strings.js +218 -0
  49. data/dist/ruby/nodes/super.js +30 -0
  50. data/dist/ruby/nodes/undef.js +26 -0
  51. data/dist/ruby/nodes.js +151 -0
  52. data/dist/ruby/parser.js +34 -0
  53. data/{src → dist}/ruby/parser.rb +1215 -252
  54. data/dist/ruby/printer.js +125 -0
  55. data/dist/ruby/toProc.js +93 -0
  56. data/dist/types/haml.js +4 -0
  57. data/dist/types/plugin.js +3 -0
  58. data/dist/types/rbs.js +4 -0
  59. data/dist/types/ruby.js +4 -0
  60. data/dist/types/utils.js +2 -0
  61. data/dist/types.js +30 -0
  62. data/dist/utils/containsAssignment.js +15 -0
  63. data/dist/utils/getTrailingComma.js +6 -0
  64. data/dist/utils/hasAncestor.js +15 -0
  65. data/{src → dist}/utils/inlineEnsureParens.js +16 -17
  66. data/dist/utils/isEmptyBodyStmt.js +10 -0
  67. data/dist/utils/isEmptyStmts.js +10 -0
  68. data/dist/utils/literal.js +8 -0
  69. data/dist/utils/literallineWithoutBreakParent.js +8 -0
  70. data/dist/utils/makeCall.js +13 -0
  71. data/dist/utils/noIndent.js +11 -0
  72. data/dist/utils/printEmptyCollection.js +44 -0
  73. data/dist/utils/skipAssignIndent.js +15 -0
  74. data/dist/utils.js +30 -0
  75. data/node_modules/prettier/bin-prettier.js +313 -190
  76. data/node_modules/prettier/doc.js +191 -323
  77. data/node_modules/prettier/index.js +2753 -3677
  78. data/node_modules/prettier/package.json +1 -1
  79. data/node_modules/prettier/parser-angular.js +13 -14
  80. data/node_modules/prettier/parser-babel.js +7 -7
  81. data/node_modules/prettier/parser-espree.js +7 -7
  82. data/node_modules/prettier/parser-flow.js +7 -7
  83. data/node_modules/prettier/parser-glimmer.js +1 -1
  84. data/node_modules/prettier/parser-graphql.js +1 -1
  85. data/node_modules/prettier/parser-html.js +17 -17
  86. data/node_modules/prettier/parser-markdown.js +9 -9
  87. data/node_modules/prettier/parser-meriyah.js +7 -7
  88. data/node_modules/prettier/parser-postcss.js +2 -2
  89. data/node_modules/prettier/parser-typescript.js +7 -7
  90. data/node_modules/prettier/parser-yaml.js +2 -2
  91. data/node_modules/prettier/third-party.js +143 -78
  92. data/package.json +26 -18
  93. metadata +74 -67
  94. data/src/haml/embed.js +0 -87
  95. data/src/haml/parser.js +0 -23
  96. data/src/haml/printer.js +0 -438
  97. data/src/parser/parseSync.js +0 -172
  98. data/src/parser/server.rb +0 -66
  99. data/src/plugin.js +0 -148
  100. data/src/prettier.js +0 -16
  101. data/src/rbs/parser.js +0 -37
  102. data/src/rbs/printer.js +0 -643
  103. data/src/ruby/embed.js +0 -142
  104. data/src/ruby/nodes/alias.js +0 -73
  105. data/src/ruby/nodes/args.js +0 -222
  106. data/src/ruby/nodes/arrays.js +0 -162
  107. data/src/ruby/nodes/assign.js +0 -47
  108. data/src/ruby/nodes/blocks.js +0 -90
  109. data/src/ruby/nodes/calls.js +0 -246
  110. data/src/ruby/nodes/case.js +0 -65
  111. data/src/ruby/nodes/class.js +0 -64
  112. data/src/ruby/nodes/commands.js +0 -131
  113. data/src/ruby/nodes/conditionals.js +0 -282
  114. data/src/ruby/nodes/constants.js +0 -43
  115. data/src/ruby/nodes/flow.js +0 -74
  116. data/src/ruby/nodes/hashes.js +0 -155
  117. data/src/ruby/nodes/heredocs.js +0 -36
  118. data/src/ruby/nodes/hooks.js +0 -34
  119. data/src/ruby/nodes/ints.js +0 -31
  120. data/src/ruby/nodes/lambdas.js +0 -76
  121. data/src/ruby/nodes/loops.js +0 -98
  122. data/src/ruby/nodes/massign.js +0 -98
  123. data/src/ruby/nodes/methods.js +0 -74
  124. data/src/ruby/nodes/operators.js +0 -83
  125. data/src/ruby/nodes/params.js +0 -106
  126. data/src/ruby/nodes/patterns.js +0 -157
  127. data/src/ruby/nodes/regexp.js +0 -56
  128. data/src/ruby/nodes/rescue.js +0 -101
  129. data/src/ruby/nodes/return.js +0 -94
  130. data/src/ruby/nodes/statements.js +0 -142
  131. data/src/ruby/nodes/strings.js +0 -272
  132. data/src/ruby/nodes/super.js +0 -35
  133. data/src/ruby/nodes/undef.js +0 -42
  134. data/src/ruby/nodes.js +0 -34
  135. data/src/ruby/parser.js +0 -37
  136. data/src/ruby/printer.js +0 -147
  137. data/src/ruby/toProc.js +0 -105
  138. data/src/utils/containsAssignment.js +0 -11
  139. data/src/utils/getTrailingComma.js +0 -5
  140. data/src/utils/hasAncestor.js +0 -17
  141. data/src/utils/isEmptyBodyStmt.js +0 -7
  142. data/src/utils/isEmptyStmts.js +0 -11
  143. data/src/utils/literal.js +0 -7
  144. data/src/utils/literallineWithoutBreakParent.js +0 -7
  145. data/src/utils/makeCall.js +0 -14
  146. data/src/utils/noIndent.js +0 -10
  147. data/src/utils/printEmptyCollection.js +0 -49
  148. data/src/utils/skipAssignIndent.js +0 -17
  149. data/src/utils.js +0 -13
@@ -0,0 +1,242 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printUnlessModifier = exports.printUnless = exports.printIfModifier = exports.printIf = exports.printElsif = exports.printElse = exports.printTernary = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { align, breakParent, hardline, group, ifBreak, indent, softline } = prettier_1.default;
10
+ // If the statements are just a single if/unless, in block or modifier form, or
11
+ // a ternary
12
+ function containsSingleConditional(stmts) {
13
+ return (stmts.body.length === 1 &&
14
+ ["if", "if_mod", "ifop", "unless", "unless_mod"].includes(stmts.body[0].type));
15
+ }
16
+ function printWithAddition(keyword, path, print, breaking) {
17
+ return [
18
+ `${keyword} `,
19
+ align(keyword.length + 1, path.call(print, "body", 0)),
20
+ indent([softline, path.call(print, "body", 1)]),
21
+ [softline, path.call(print, "body", 2)],
22
+ [softline, "end"],
23
+ breaking ? breakParent : ""
24
+ ];
25
+ }
26
+ // For the unary `not` operator, we need to explicitly add parentheses to it in
27
+ // order for it to be valid from within a ternary. Otherwise if the clause of
28
+ // the ternary isn't a unary `not`, we can just pass it along.
29
+ function printTernaryClause(clause) {
30
+ if (Array.isArray(clause)) {
31
+ const [part] = clause;
32
+ if (Array.isArray(part) && part[0] === "not") {
33
+ // We are inside of a statements list and the statement is a unary `not`.
34
+ return ["not(", part[2], ")"];
35
+ }
36
+ if (clause[0] === "not") {
37
+ // We are inside a ternary condition and the clause is a unary `not`.
38
+ return ["not(", clause[2], ")"];
39
+ }
40
+ }
41
+ return clause;
42
+ }
43
+ // The conditions for a ternary look like `foo : bar` where `foo` represents
44
+ // the truthy clause and `bar` represents the falsy clause. In the case that the
45
+ // parent node is an `unless`, these have to flip in order.
46
+ function printTernaryClauses(keyword, truthyClause, falsyClause) {
47
+ const parts = [
48
+ printTernaryClause(truthyClause),
49
+ " : ",
50
+ printTernaryClause(falsyClause)
51
+ ];
52
+ return keyword === "if" ? parts : parts.reverse();
53
+ }
54
+ // Handles ternary nodes. If it does not fit on one line, then we break out into
55
+ // an if/else statement. Otherwise we remain as a ternary.
56
+ const printTernary = (path, _opts, print) => {
57
+ const [predicate, truthyClause, falsyClause] = path.map(print, "body");
58
+ const ternaryClauses = printTernaryClauses("if", truthyClause, falsyClause);
59
+ return group(ifBreak([
60
+ "if ",
61
+ align(3, predicate),
62
+ indent([softline, truthyClause]),
63
+ [softline, "else"],
64
+ indent([softline, falsyClause]),
65
+ [softline, "end"]
66
+ ], [predicate, " ? ", ...ternaryClauses]));
67
+ };
68
+ exports.printTernary = printTernary;
69
+ // Prints an `if_mod` or `unless_mod` node. Because it was previously in the
70
+ // modifier form, we're guaranteed to not have an additional node, so we can
71
+ // just work with the predicate and the body.
72
+ function printSingle(keyword, modifier = false) {
73
+ return function printSingleWithKeyword(path, { rubyModifier }, print) {
74
+ const [, statementsNode] = path.getValue().body;
75
+ const predicateDoc = path.call(print, "body", 0);
76
+ const statementsDoc = path.call(print, "body", 1);
77
+ const multilineParts = [
78
+ `${keyword} `,
79
+ align(keyword.length + 1, predicateDoc),
80
+ indent([softline, statementsDoc]),
81
+ softline,
82
+ "end"
83
+ ];
84
+ // If we do not allow modifier form conditionals or there are comments
85
+ // inside of the body of the conditional, then we must print in the
86
+ // multiline form.
87
+ if (!rubyModifier ||
88
+ (!modifier && statementsNode.body[0].comments)) {
89
+ return [multilineParts, breakParent];
90
+ }
91
+ const inline = (0, utils_1.inlineEnsureParens)(path, [
92
+ path.call(print, "body", 1),
93
+ ` ${keyword} `,
94
+ path.call(print, "body", 0)
95
+ ]);
96
+ // An expression with a conditional modifier (expression if true), the
97
+ // conditional body is parsed before the predicate expression, meaning that
98
+ // if the parser encountered a variable declaration, it would initialize
99
+ // that variable first before evaluating the predicate expression. That
100
+ // parse order means the difference between a NameError or not. #591
101
+ // https://docs.ruby-lang.org/en/2.0.0/syntax/control_expressions_rdoc.html#label-Modifier+if+and+unless
102
+ if (modifier && (0, utils_1.containsAssignment)(statementsNode)) {
103
+ return inline;
104
+ }
105
+ return group(ifBreak(multilineParts, inline));
106
+ };
107
+ }
108
+ const noTernary = [
109
+ "alias",
110
+ "assign",
111
+ "break",
112
+ "command",
113
+ "command_call",
114
+ "heredoc",
115
+ "if",
116
+ "if_mod",
117
+ "ifop",
118
+ "lambda",
119
+ "massign",
120
+ "next",
121
+ "opassign",
122
+ "rescue_mod",
123
+ "return",
124
+ "return0",
125
+ "super",
126
+ "undef",
127
+ "unless",
128
+ "unless_mod",
129
+ "until_mod",
130
+ "var_alias",
131
+ "void_stmt",
132
+ "while_mod",
133
+ "yield",
134
+ "yield0",
135
+ "zsuper"
136
+ ];
137
+ // Certain expressions cannot be reduced to a ternary without adding parens
138
+ // around them. In this case we say they cannot be ternaried and default instead
139
+ // to breaking them into multiple lines.
140
+ function canTernaryStmts(stmts) {
141
+ if (stmts.body.length !== 1) {
142
+ return false;
143
+ }
144
+ const stmt = stmts.body[0];
145
+ // If the user is using one of the lower precedence "and" or "or" operators,
146
+ // then we can't use a ternary expression as it would break the flow control.
147
+ if (stmt.type === "binary" && ["and", "or"].includes(stmt.body[1])) {
148
+ return false;
149
+ }
150
+ // Check against the blocklist of statement types that are not allowed to be
151
+ // a part of a ternary expression.
152
+ return !noTernary.includes(stmt.type);
153
+ }
154
+ // In order for an `if` or `unless` expression to be shortened to a ternary,
155
+ // there has to be one and only one "addition" (another clause attached) which
156
+ // is of the "else" type. Both the body of the main node and the body of the
157
+ // additional node must have only one statement, and that statement list must
158
+ // pass the `canTernaryStmts` check.
159
+ function canTernary(path) {
160
+ const [predicate, stmts, addition] = path.getValue().body;
161
+ return (!["assign", "opassign", "command_call", "command"].includes(predicate.type) &&
162
+ addition &&
163
+ addition.type === "else" &&
164
+ [stmts, addition.body[0]].every(canTernaryStmts));
165
+ }
166
+ // A normalized print function for both `if` and `unless` nodes.
167
+ function printConditional(keyword) {
168
+ return (path, opts, print) => {
169
+ if (canTernary(path)) {
170
+ let ternaryParts = [
171
+ path.call(print, "body", 0),
172
+ " ? ",
173
+ ...printTernaryClauses(keyword, path.call(print, "body", 1), path.call(print, "body", 2, "body", 0))
174
+ ];
175
+ if (["binary", "call"].includes(path.getParentNode().type)) {
176
+ ternaryParts = ["(", ...ternaryParts, ")"];
177
+ }
178
+ return group(ifBreak(printWithAddition(keyword, path, print, false), ternaryParts));
179
+ }
180
+ const [predicate, statements, addition] = path.getValue().body;
181
+ // If there's an additional clause that wasn't matched earlier, we know we
182
+ // can't go for the inline option.
183
+ if (addition) {
184
+ return group(printWithAddition(keyword, path, print, true));
185
+ }
186
+ // If the body of the conditional is empty, then we explicitly have to use the
187
+ // block form.
188
+ if ((0, utils_1.isEmptyStmts)(statements)) {
189
+ return [
190
+ `${keyword} `,
191
+ align(keyword.length + 1, path.call(print, "body", 0)),
192
+ hardline,
193
+ "end"
194
+ ];
195
+ }
196
+ // Two situations in which we need to use the block form:
197
+ //
198
+ // 1. If the predicate of the conditional contains an assignment, then we can't
199
+ // know for sure that it doesn't impact the body of the conditional.
200
+ //
201
+ // 2. If the conditional contains just another conditional, then collapsing it
202
+ // would result in double modifiers on the same line.
203
+ if ((0, utils_1.containsAssignment)(predicate) ||
204
+ containsSingleConditional(statements)) {
205
+ return [
206
+ `${keyword} `,
207
+ align(keyword.length + 1, path.call(print, "body", 0)),
208
+ indent([hardline, path.call(print, "body", 1)]),
209
+ hardline,
210
+ "end"
211
+ ];
212
+ }
213
+ return printSingle(keyword)(path, opts, print);
214
+ };
215
+ }
216
+ const printElse = (path, opts, print) => {
217
+ const stmts = path.getValue().body[0];
218
+ return [
219
+ stmts.body.length === 1 && stmts.body[0].type === "command"
220
+ ? breakParent
221
+ : "",
222
+ "else",
223
+ indent([softline, path.call(print, "body", 0)])
224
+ ];
225
+ };
226
+ exports.printElse = printElse;
227
+ const printElsif = (path, opts, print) => {
228
+ const [, , addition] = path.getValue().body;
229
+ const parts = [
230
+ group(["elsif ", align("elsif".length - 1, path.call(print, "body", 0))]),
231
+ indent([hardline, path.call(print, "body", 1)])
232
+ ];
233
+ if (addition) {
234
+ parts.push(group([hardline, path.call(print, "body", 2)]));
235
+ }
236
+ return group(parts);
237
+ };
238
+ exports.printElsif = printElsif;
239
+ exports.printIf = printConditional("if");
240
+ exports.printIfModifier = printSingle("if", true);
241
+ exports.printUnless = printConditional("unless");
242
+ exports.printUnlessModifier = printSingle("unless", true);
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printTopConst = exports.printField = exports.printDefined = exports.printConstRef = exports.printConstPath = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { group, indent, join, softline } = prettier_1.default;
10
+ const printConstPath = (path, opts, print) => {
11
+ return join("::", path.map(print, "body"));
12
+ };
13
+ exports.printConstPath = printConstPath;
14
+ const printConstRef = (path, opts, print) => {
15
+ return path.call(print, "body", 0);
16
+ };
17
+ exports.printConstRef = printConstRef;
18
+ const printDefined = (path, opts, print) => {
19
+ return group([
20
+ "defined?(",
21
+ indent([softline, path.call(print, "body", 0)]),
22
+ softline,
23
+ ")"
24
+ ]);
25
+ };
26
+ exports.printDefined = printDefined;
27
+ const printField = (path, opts, print) => {
28
+ return group([
29
+ path.call(print, "body", 0),
30
+ (0, utils_1.makeCall)(path, opts, print),
31
+ path.call(print, "body", 2)
32
+ ]);
33
+ };
34
+ exports.printField = printField;
35
+ const printTopConst = (path, opts, print) => {
36
+ return ["::", path.call(print, "body", 0)];
37
+ };
38
+ exports.printTopConst = printTopConst;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printYield0 = exports.printYield = exports.printNext = exports.printBreak = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { join } = prettier_1.default;
10
+ function nodeDive(node, steps) {
11
+ let current = node;
12
+ steps.forEach((step) => {
13
+ current = current[step];
14
+ });
15
+ return current;
16
+ }
17
+ const unskippableParens = [
18
+ "if_mod",
19
+ "rescue_mod",
20
+ "unless_mod",
21
+ "until_mod",
22
+ "while_mod"
23
+ ];
24
+ function maybeHandleParens(path, print, keyword, steps) {
25
+ const node = nodeDive(path.getValue(), steps);
26
+ if (node.type !== "paren") {
27
+ return null;
28
+ }
29
+ const stmts = node.body[0].body;
30
+ if (stmts.length === 1 && !unskippableParens.includes(stmts[0].type)) {
31
+ return [`${keyword} `, path.call(print, ...steps, "body", 0)];
32
+ }
33
+ return [keyword, path.call(print, ...steps)];
34
+ }
35
+ const printBreak = (path, opts, print) => {
36
+ const content = path.getValue().body[0];
37
+ if (content.body.length === 0) {
38
+ return "break";
39
+ }
40
+ const steps = ["body", 0, "body", 0, "body", 0];
41
+ return (maybeHandleParens(path, print, "break", steps) || [
42
+ "break ",
43
+ join(", ", path.call(print, "body", 0))
44
+ ]);
45
+ };
46
+ exports.printBreak = printBreak;
47
+ const printNext = (path, opts, print) => {
48
+ const args = path.getValue().body[0].body[0];
49
+ if (!args) {
50
+ return "next";
51
+ }
52
+ const steps = ["body", 0, "body", 0, "body", 0];
53
+ return (maybeHandleParens(path, print, "next", steps) || [
54
+ "next ",
55
+ join(", ", path.call(print, "body", 0))
56
+ ]);
57
+ };
58
+ exports.printNext = printNext;
59
+ const printYield = (path, opts, print) => {
60
+ if (path.getValue().body[0].type === "paren") {
61
+ return ["yield", path.call(print, "body", 0)];
62
+ }
63
+ return ["yield ", join(", ", path.call(print, "body", 0))];
64
+ };
65
+ exports.printYield = printYield;
66
+ exports.printYield0 = (0, utils_1.literal)("yield");
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printHash = exports.printHashContents = exports.printAssocSplat = exports.printAssocNew = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { group, ifBreak, indent, join, line } = prettier_1.default;
10
+ // When attempting to convert a hash rocket into a hash label, you need to take
11
+ // care because only certain patterns are allowed. Ruby source says that they
12
+ // have to match keyword arguments to methods, but don't specify what that is.
13
+ // After some experimentation, it looks like it's:
14
+ //
15
+ // * Starts with a letter (either case) or an underscore
16
+ // * Does not end in equal
17
+ //
18
+ // This function represents that check, as it determines if it can convert the
19
+ // symbol node into a hash label.
20
+ function isValidHashLabel(symbolLiteral) {
21
+ const label = symbolLiteral.body[0].body;
22
+ return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
23
+ }
24
+ function canUseHashLabels(contentsNode) {
25
+ return contentsNode.body.every((assocNode) => {
26
+ if (assocNode.type === "assoc_splat") {
27
+ return true;
28
+ }
29
+ switch (assocNode.body[0].type) {
30
+ case "@label":
31
+ return true;
32
+ case "symbol_literal":
33
+ return isValidHashLabel(assocNode.body[0]);
34
+ case "dyna_symbol":
35
+ return true;
36
+ default:
37
+ return false;
38
+ }
39
+ });
40
+ }
41
+ const printHashKeyLabel = (path, print) => {
42
+ const node = path.getValue();
43
+ switch (node.type) {
44
+ case "@label":
45
+ return print(path);
46
+ case "symbol_literal": {
47
+ const nodePath = path;
48
+ return [nodePath.call(print, "body", 0), ":"];
49
+ }
50
+ case "dyna_symbol":
51
+ return [print(path), ":"];
52
+ default:
53
+ // This should never happen, but keeping it here so that the two key
54
+ // printers can maintain the same signature.
55
+ return "";
56
+ }
57
+ };
58
+ const printHashKeyRocket = (path, print) => {
59
+ const node = path.getValue();
60
+ let doc = print(path);
61
+ if (node.type === "@label") {
62
+ const sDoc = doc; // since we know this is a label
63
+ doc = [":", sDoc.slice(0, sDoc.length - 1)];
64
+ }
65
+ else if (node.type === "dyna_symbol") {
66
+ doc = [":", doc];
67
+ }
68
+ return [doc, " =>"];
69
+ };
70
+ const printAssocNew = (path, opts, print) => {
71
+ const [keyNode, valueNode] = path.getValue().body;
72
+ const { keyPrinter } = path.getParentNode();
73
+ const parts = [path.call((keyPath) => keyPrinter(keyPath, print), "body", 0)];
74
+ const valueDoc = path.call(print, "body", 1);
75
+ // If we're printing a child hash then we want it to break along with its
76
+ // parent hash, so we don't group the parts.
77
+ if (valueNode.type === "hash") {
78
+ parts.push(" ", valueDoc);
79
+ return parts;
80
+ }
81
+ if (!(0, utils_1.skipAssignIndent)(valueNode) || keyNode.comments) {
82
+ parts.push(indent([line, valueDoc]));
83
+ }
84
+ else {
85
+ parts.push(" ", valueDoc);
86
+ }
87
+ return group(parts);
88
+ };
89
+ exports.printAssocNew = printAssocNew;
90
+ const printAssocSplat = (path, opts, print) => {
91
+ return ["**", path.call(print, "body", 0)];
92
+ };
93
+ exports.printAssocSplat = printAssocSplat;
94
+ const printHashContents = (path, opts, print) => {
95
+ const node = path.getValue();
96
+ // First determine which key printer we're going to use, so that the child
97
+ // nodes can reference it when they go to get printed.
98
+ node.keyPrinter =
99
+ opts.rubyHashLabel && canUseHashLabels(path.getValue())
100
+ ? printHashKeyLabel
101
+ : printHashKeyRocket;
102
+ return join([",", line], path.map(print, "body"));
103
+ };
104
+ exports.printHashContents = printHashContents;
105
+ const printHash = (path, opts, print) => {
106
+ const hashNode = path.getValue();
107
+ // Hashes normally have a single assoclist_from_args child node. If it's
108
+ // missing, then it means we're dealing with an empty hash, so we can just
109
+ // exit here and print.
110
+ if (hashNode.body[0] === null) {
111
+ return (0, utils_1.printEmptyCollection)(path, opts, "{", "}");
112
+ }
113
+ const hashDoc = [
114
+ "{",
115
+ indent([
116
+ line,
117
+ path.call(print, "body", 0),
118
+ (0, utils_1.getTrailingComma)(opts) ? ifBreak(",", "") : ""
119
+ ]),
120
+ line,
121
+ "}"
122
+ ];
123
+ // If we're inside another hash, then we don't want to group our contents
124
+ // because we want this hash to break along with its parent hash.
125
+ if (path.getParentNode().type === "assoc_new") {
126
+ return hashDoc;
127
+ }
128
+ return group(hashDoc);
129
+ };
130
+ exports.printHash = printHash;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printHeredoc = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { group, lineSuffix, join } = prettier_1.default;
10
+ const printHeredoc = (path, opts, print) => {
11
+ const { body, ending } = path.getValue();
12
+ const parts = body.map((part, index) => {
13
+ if (part.type !== "@tstring_content") {
14
+ // In this case, the part of the string is an embedded expression
15
+ return path.call(print, "body", index);
16
+ }
17
+ // In this case, the part of the string is just regular string content
18
+ return join(utils_1.literallineWithoutBreakParent, part.body.split(/\r?\n/));
19
+ });
20
+ // We use a literalline break because matching indentation is required
21
+ // for the heredoc contents and ending. If the line suffix contains a
22
+ // break-parent, all ancestral groups are broken, and heredocs automatically
23
+ // break lines in groups they appear in. We prefer them to appear in-line if
24
+ // possible, so we use a literalline without the break-parent.
25
+ return group([
26
+ path.call(print, "beging"),
27
+ lineSuffix(group([utils_1.literallineWithoutBreakParent, ...parts, ending]))
28
+ ]);
29
+ };
30
+ exports.printHeredoc = printHeredoc;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printEND = exports.printBEGIN = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const { group, indent, line } = prettier_1.default;
9
+ // The `BEGIN` and `END` keywords are used to hook into the Ruby process. Any
10
+ // `BEGIN` blocks are executed right when the process starts up, and the `END`
11
+ // blocks are executed right before exiting.
12
+ //
13
+ // BEGIN {
14
+ // # content goes here
15
+ // }
16
+ //
17
+ // END {
18
+ // # content goes here
19
+ // }
20
+ //
21
+ // Interesting side note, you don't use `do...end` blocks with these hooks. Both
22
+ // nodes contain one child which is a `stmts` node.
23
+ function printHook(name) {
24
+ return function printHookWithName(path, opts, print) {
25
+ return group([
26
+ name,
27
+ " ",
28
+ path.call(print, "body", 0),
29
+ indent([line, path.call(print, "body", 1)]),
30
+ [line, "}"]
31
+ ]);
32
+ };
33
+ }
34
+ exports.printBEGIN = printHook("BEGIN");
35
+ exports.printEND = printHook("END");
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printInt = void 0;
4
+ // An @int node is any literal integer in Ruby. They can come in a number of
5
+ // bases, and look like the following:
6
+ //
7
+ // Binary (2) - 0b0110
8
+ // Octal (8) - 0o34 or 034
9
+ // Decimal (10) - a normal number like 159
10
+ // Hexidecimal (16) - 0xac5
11
+ //
12
+ // If it's a decimal number, it can be optional separated by any number of
13
+ // arbitrarily places underscores. This can be useful for dollars and cents
14
+ // (34_99), dates (2020_11_30), and normal 3 digit separation (1_222_333).
15
+ const printInt = (path) => {
16
+ const { body } = path.getValue();
17
+ // If the number is a base 10 number, is sufficiently large, and is not
18
+ // already formatted with underscores, then add them in in between the
19
+ // numbers every three characters starting from the right.
20
+ if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
21
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22
+ const segments = ` ${body}`.slice((body.length + 2) % 3).match(/.{3}/g);
23
+ return segments.join("_").trim();
24
+ }
25
+ return body;
26
+ };
27
+ exports.printInt = printInt;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.printLambda = void 0;
7
+ const prettier_1 = __importDefault(require("../../prettier"));
8
+ const utils_1 = require("../../utils");
9
+ const { group, ifBreak, indent, line } = prettier_1.default;
10
+ // We can have our params coming in as the first child of the main lambda node,
11
+ // or if we have them wrapped in parens then they'll be one level deeper. Even
12
+ // though it's possible to omit the parens if you only have one argument, we're
13
+ // going to keep them in no matter what for consistency.
14
+ function printLambdaParams(path, print) {
15
+ let node = path.getValue().body[0];
16
+ // In this case we had something like -> (foo) { bar } which would mean that
17
+ // we're looking at a paren node, so we'll descend one level deeper to get at
18
+ // the actual params node.
19
+ if (node.type !== "params") {
20
+ node = node.body[0];
21
+ }
22
+ // If we don't have any params at all, then we're just going to bail out and
23
+ // print nothing. This is to avoid printing an empty set of parentheses.
24
+ if (node.body.every((type) => !type)) {
25
+ return "";
26
+ }
27
+ return path.call(print, "body", 0);
28
+ }
29
+ // Lambda nodes represent stabby lambda literals, which can come in a couple of
30
+ // flavors. They can use either braces or do...end for their block, and their
31
+ // arguments can be not present, have no parentheses for a single argument, or
32
+ // have parentheses for multiple arguments. Below are a couple of examples:
33
+ //
34
+ // -> { 1 }
35
+ // -> a { a + 1 }
36
+ // ->(a) { a + 1 }
37
+ // ->(a, b) { a + b }
38
+ // ->(a, b = 1) { a + b }
39
+ //
40
+ // -> do
41
+ // 1
42
+ // end
43
+ //
44
+ // -> a do
45
+ // a + 1
46
+ // end
47
+ //
48
+ // ->(a, b) do
49
+ // a + b
50
+ // end
51
+ //
52
+ // Generally, we're going to favor do...end for the multi-line form and braces
53
+ // for the single-line form. However, if we have an ancestor that is a command
54
+ // or command_call node, then we'll need to use braces either way because of
55
+ // operator precendence.
56
+ const printLambda = (path, opts, print) => {
57
+ const params = printLambdaParams(path, print);
58
+ const inCommand = (0, utils_1.hasAncestor)(path, ["command", "command_call"]);
59
+ return group(ifBreak([
60
+ "->",
61
+ params,
62
+ " ",
63
+ inCommand ? "{" : "do",
64
+ indent([line, path.call(print, "body", 1)]),
65
+ line,
66
+ inCommand ? "}" : "end"
67
+ ], ["->", params, " { ", path.call(print, "body", 1), " }"]));
68
+ };
69
+ exports.printLambda = printLambda;