prettier 1.2.3 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +349 -358
  3. data/README.md +21 -93
  4. data/node_modules/prettier/index.js +54 -54
  5. data/package.json +1 -2
  6. data/rubocop.yml +26 -0
  7. data/src/haml/embed.js +87 -0
  8. data/src/haml/nodes/comment.js +27 -0
  9. data/src/haml/nodes/doctype.js +34 -0
  10. data/src/haml/nodes/filter.js +16 -0
  11. data/src/haml/nodes/hamlComment.js +21 -0
  12. data/src/haml/nodes/plain.js +6 -0
  13. data/src/haml/nodes/root.js +8 -0
  14. data/src/haml/nodes/script.js +33 -0
  15. data/src/haml/nodes/silentScript.js +59 -0
  16. data/src/haml/nodes/tag.js +193 -0
  17. data/src/haml/parser.js +22 -0
  18. data/src/haml/parser.rb +138 -0
  19. data/src/haml/printer.js +28 -0
  20. data/src/parser/getLang.js +32 -0
  21. data/src/parser/getNetcat.js +50 -0
  22. data/src/parser/netcat.js +15 -0
  23. data/src/parser/parseSync.js +33 -0
  24. data/src/parser/requestParse.js +74 -0
  25. data/src/parser/server.rb +61 -0
  26. data/src/plugin.js +26 -4
  27. data/src/prettier.js +1 -0
  28. data/src/rbs/parser.js +39 -0
  29. data/src/rbs/parser.rb +94 -0
  30. data/src/rbs/printer.js +605 -0
  31. data/src/ruby/embed.js +58 -8
  32. data/src/ruby/nodes/args.js +20 -6
  33. data/src/ruby/nodes/blocks.js +64 -59
  34. data/src/ruby/nodes/calls.js +12 -43
  35. data/src/ruby/nodes/class.js +17 -27
  36. data/src/ruby/nodes/commands.js +7 -2
  37. data/src/ruby/nodes/conditionals.js +1 -1
  38. data/src/ruby/nodes/hashes.js +28 -14
  39. data/src/ruby/nodes/hooks.js +9 -19
  40. data/src/ruby/nodes/loops.js +4 -10
  41. data/src/ruby/nodes/methods.js +8 -17
  42. data/src/ruby/nodes/params.js +22 -14
  43. data/src/ruby/nodes/patterns.js +9 -5
  44. data/src/ruby/nodes/rescue.js +32 -25
  45. data/src/ruby/nodes/return.js +0 -4
  46. data/src/ruby/nodes/statements.js +11 -13
  47. data/src/ruby/nodes/strings.js +27 -35
  48. data/src/ruby/parser.js +2 -49
  49. data/src/ruby/parser.rb +256 -232
  50. data/src/ruby/printer.js +0 -2
  51. data/src/ruby/toProc.js +4 -8
  52. data/src/utils.js +1 -0
  53. data/src/utils/isEmptyBodyStmt.js +7 -0
  54. data/src/utils/isEmptyStmts.js +9 -5
  55. data/src/utils/makeCall.js +3 -0
  56. data/src/utils/noIndent.js +1 -0
  57. data/src/utils/printEmptyCollection.js +9 -2
  58. metadata +26 -2
@@ -0,0 +1,605 @@
1
+ const {
2
+ concat,
3
+ group,
4
+ hardline,
5
+ indent,
6
+ makeString,
7
+ join,
8
+ line,
9
+ softline
10
+ } = require("../prettier");
11
+
12
+ // For some lists of entities in the AST, the parser returns them as an unsorted
13
+ // object (presumably because Ruby hashes have implicit ordering). We do not
14
+ // have that in JavaScript, so here we sort each object by its position in the
15
+ // source string.
16
+ function getSortedKeys(object) {
17
+ return Object.keys(object).sort(
18
+ (left, right) =>
19
+ object[left].type.location.start_pos -
20
+ object[right].type.location.start_pos
21
+ );
22
+ }
23
+
24
+ // In some cases, we want to just defer to whatever was in the source.
25
+ function getSource(node, opts) {
26
+ return opts.originalText.slice(
27
+ node.location.start_pos,
28
+ node.location.end_pos
29
+ );
30
+ }
31
+
32
+ // This is the generic node print function, used to convert any node in the AST
33
+ // into its equivalent Doc representation.
34
+ function printNode(path, opts, print) {
35
+ const node = path.getValue();
36
+ let doc = null;
37
+
38
+ if (node.declarations) {
39
+ return printRoot();
40
+ }
41
+
42
+ /* istanbul ignore else */
43
+ if (node.declaration) {
44
+ switch (node.declaration) {
45
+ case "alias":
46
+ doc = printTypeAlias();
47
+ break;
48
+ case "class":
49
+ doc = printClass();
50
+ break;
51
+ case "constant":
52
+ case "global":
53
+ doc = printConstant();
54
+ break;
55
+ case "interface":
56
+ doc = printInterface();
57
+ break;
58
+ case "module":
59
+ doc = printModule();
60
+ break;
61
+ /* istanbul ignore next */
62
+ default:
63
+ throw new Error(`unknown declaration: ${node.declaration}`);
64
+ }
65
+ } else if (node.member) {
66
+ switch (node.member) {
67
+ case "alias":
68
+ doc = printAlias();
69
+ break;
70
+ case "attr_accessor":
71
+ case "attr_reader":
72
+ case "attr_writer":
73
+ doc = printAttr();
74
+ break;
75
+ case "class_variable":
76
+ case "instance_variable":
77
+ doc = printVariable();
78
+ break;
79
+ case "class_instance_variable":
80
+ doc = concat(["self.", printVariable()]);
81
+ break;
82
+ case "include":
83
+ case "extend":
84
+ case "prepend":
85
+ doc = printMixin();
86
+ break;
87
+ case "public":
88
+ case "private":
89
+ doc = node.member;
90
+ break;
91
+ case "method_definition":
92
+ doc = printMethodDefinition();
93
+ break;
94
+ /* istanbul ignore next */
95
+ default:
96
+ throw new Error(`unknown member: ${node.member}`);
97
+ }
98
+ } else {
99
+ const ast = JSON.stringify(node, null, 2);
100
+ throw new Error(`Unsupported node encountered:\n${ast}`);
101
+ }
102
+
103
+ // Certain nodes can't have annotations at all
104
+ if (node.annotations && node.annotations.length > 0) {
105
+ doc = concat([printAnnotations(), hardline, doc]);
106
+ }
107
+
108
+ if (node.comment) {
109
+ doc = concat([printComment(), hardline, doc]);
110
+ }
111
+
112
+ return doc;
113
+
114
+ // Prints out a string in the source, which looks like:
115
+ // 'foo'
116
+ function printString(node) {
117
+ // We're going to go straight to the source here, as if we don't then we're
118
+ // going to end up with the result of String#inspect, which does weird
119
+ // things to escape sequences.
120
+ const value = getSource(node, opts);
121
+
122
+ // Get the quote that was used in the source and the quote that we want to
123
+ // be using.
124
+ const originalQuote = value[0];
125
+ const preferredQuote = opts.rubySingleQuote ? "'" : '"';
126
+
127
+ // Determine if we're allowed to change the quote based on whether or not
128
+ // there is an escape sequence in the source string.
129
+ const quote = node.literal.includes("\\") ? originalQuote : preferredQuote;
130
+
131
+ return makeString(value.slice(1, -1), quote, false);
132
+ }
133
+
134
+ // Certain nodes are names with optional arguments attached, as in Array[A].
135
+ // We handle all of that printing centralized here.
136
+ function printNameAndArgs(path) {
137
+ const node = path.getValue();
138
+
139
+ if (node.args.length === 0) {
140
+ return node.name;
141
+ }
142
+
143
+ return group(
144
+ concat([node.name, "[", join(", ", path.map(printType, "args")), "]"])
145
+ );
146
+ }
147
+
148
+ // This is the big function that prints out any individual type, which can
149
+ // look like all kinds of things, listed in the case statement below.
150
+ function printType(path) {
151
+ const node = path.getValue();
152
+
153
+ switch (node.class) {
154
+ case "literal":
155
+ if (node.literal[0] === '"') {
156
+ return printString(node);
157
+ }
158
+ return node.literal;
159
+ case "optional":
160
+ return concat([path.call(printType, "type"), "?"]);
161
+ case "tuple":
162
+ // If we don't have any sub types, we explicitly need the space in between
163
+ // the brackets to not confuse the parser.
164
+ if (node.types.length === 0) {
165
+ return "[ ]";
166
+ }
167
+
168
+ return group(
169
+ concat(["[", join(", ", path.map(printType, "types")), "]"])
170
+ );
171
+ case "union": {
172
+ const doc = group(
173
+ join(concat([line, "| "]), path.map(printType, "types"))
174
+ );
175
+
176
+ const parentType = path.getParentNode().class;
177
+ return parentType === "intersection" ? concat(["(", doc, ")"]) : doc;
178
+ }
179
+ case "intersection":
180
+ return group(join(concat([line, "& "]), path.map(printType, "types")));
181
+ case "class_singleton":
182
+ return concat(["singleton(", node.name, ")"]);
183
+ case "proc":
184
+ return concat(["^", printMethodSignature(path)]);
185
+ case "record": {
186
+ const parts = [];
187
+
188
+ getSortedKeys(node.fields).forEach((field) => {
189
+ const fieldParts = [];
190
+
191
+ if (node.fields[field].joiner === "rocket") {
192
+ fieldParts.push(`${field} => `);
193
+ } else {
194
+ fieldParts.push(`${field}: `);
195
+ }
196
+
197
+ fieldParts.push(path.call(printType, "fields", field, "type"));
198
+ parts.push(concat(fieldParts));
199
+ });
200
+
201
+ return group(
202
+ concat([
203
+ "{",
204
+ indent(concat([line, join(concat([",", line]), parts)])),
205
+ line,
206
+ "}"
207
+ ])
208
+ );
209
+ }
210
+ case "class_instance":
211
+ case "interface":
212
+ return printNameAndArgs(path);
213
+ case "alias":
214
+ case "variable":
215
+ return node.name;
216
+ case "bool":
217
+ case "bot":
218
+ case "class":
219
+ case "instance":
220
+ case "nil":
221
+ case "self":
222
+ case "top":
223
+ case "untyped":
224
+ case "void":
225
+ return node.class;
226
+ /* istanbul ignore next */
227
+ default:
228
+ throw new Error(`unknown type: ${node.class}`);
229
+ }
230
+ }
231
+
232
+ // Prints out the root of the tree, which includes zero or more declarations.
233
+ function printRoot() {
234
+ return concat([
235
+ join(concat([hardline, hardline]), path.map(print, "declarations")),
236
+ hardline
237
+ ]);
238
+ }
239
+
240
+ // Prints out the members of a class, module, or interface.
241
+ function printMembers() {
242
+ let lastLine = null;
243
+ const docs = [];
244
+
245
+ path.each((memberPath) => {
246
+ const memberNode = memberPath.getValue();
247
+
248
+ if (lastLine !== null && memberNode.location.start.line - lastLine >= 2) {
249
+ docs.push(concat([hardline, hardline]));
250
+ } else {
251
+ docs.push(hardline);
252
+ }
253
+
254
+ docs.push(print(memberPath));
255
+ lastLine = memberNode.location.end.line;
256
+ }, "members");
257
+
258
+ return concat(docs);
259
+ }
260
+
261
+ // Prints out a type alias, which is a declaration that looks like:
262
+ // type foo = String
263
+ function printTypeAlias() {
264
+ return group(
265
+ concat([
266
+ "type ",
267
+ node.name,
268
+ " =",
269
+ indent(group(concat([line, path.call(printType, "type")])))
270
+ ])
271
+ );
272
+ }
273
+
274
+ // Prints out the name of a class, interface, or module declaration.
275
+ // Additionally loops through each type parameter if there are any and print
276
+ // them out joined by commas. Checks for validation and variance.
277
+ function printNameAndTypeParams() {
278
+ if (node.type_params.params.length === 0) {
279
+ return node.name;
280
+ }
281
+
282
+ const docs = path.map(
283
+ (paramPath) => {
284
+ const node = paramPath.getValue();
285
+ const parts = [];
286
+
287
+ if (node.skip_validation) {
288
+ parts.push("unchecked");
289
+ }
290
+
291
+ if (node.variance === "covariant") {
292
+ parts.push("out");
293
+ } else if (node.variance === "contravariant") {
294
+ parts.push("in");
295
+ }
296
+
297
+ return join(" ", parts.concat(node.name));
298
+ },
299
+ "type_params",
300
+ "params"
301
+ );
302
+
303
+ return concat([node.name, "[", join(", ", docs), "]"]);
304
+ }
305
+
306
+ // Prints out a class declarations, which looks like:
307
+ // class Foo end
308
+ function printClass() {
309
+ const parts = ["class ", printNameAndTypeParams()];
310
+
311
+ if (node.super_class) {
312
+ parts.push(" < ", path.call(printNameAndArgs, "super_class"));
313
+ }
314
+
315
+ parts.push(indent(printMembers()), hardline, "end");
316
+
317
+ return group(concat(parts));
318
+ }
319
+
320
+ // Prints out a constant or a global declaration, which looks like:
321
+ // Foo: String
322
+ // $foo: String
323
+ function printConstant() {
324
+ return group(concat([node.name, ": ", path.call(printType, "type")]));
325
+ }
326
+
327
+ // Prints out an interface declaration, which looks like:
328
+ // interface _Foo end
329
+ function printInterface() {
330
+ return group(
331
+ concat([
332
+ "interface ",
333
+ printNameAndTypeParams(),
334
+ indent(printMembers()),
335
+ hardline,
336
+ "end"
337
+ ])
338
+ );
339
+ }
340
+
341
+ // Prints out a module declaration, which looks like:
342
+ // module Foo end
343
+ function printModule() {
344
+ const parts = ["module ", printNameAndTypeParams()];
345
+
346
+ if (node.self_types.length > 0) {
347
+ parts.push(" : ", join(", ", path.map(printNameAndArgs, "self_types")));
348
+ }
349
+
350
+ parts.push(indent(printMembers()), hardline, "end");
351
+
352
+ return group(concat(parts));
353
+ }
354
+
355
+ // Prints out an alias within a declaration, which looks like:
356
+ // alias foo bar
357
+ // alias self.foo self.bar
358
+ function printAlias() {
359
+ if (node.kind === "singleton") {
360
+ return concat(["alias self.", node.new_name, " self.", node.old_name]);
361
+ }
362
+
363
+ return concat(["alias ", node.new_name, " ", node.old_name]);
364
+ }
365
+
366
+ // Prints out an attr_* meta method, which looks like:
367
+ // attr_accessor foo
368
+ // attr_reader self.foo()
369
+ // attr_writer self.foo(@bar): String
370
+ function printAttr() {
371
+ const parts = [node.member, " "];
372
+
373
+ if (node.kind === "singleton") {
374
+ parts.push("self.");
375
+ }
376
+
377
+ parts.push(node.name);
378
+
379
+ if (node.ivar_name === false) {
380
+ parts.push("()");
381
+ } else if (node.ivar_name) {
382
+ parts.push("(", node.ivar_name, ")");
383
+ }
384
+
385
+ parts.push(": ", path.call(printType, "type"));
386
+
387
+ return group(concat(parts));
388
+ }
389
+
390
+ // Prints out a variable member, which looks like:
391
+ // @foo: String
392
+ // self.@foo: String
393
+ // @@foo: String
394
+ function printVariable() {
395
+ return group(concat([node.name, ": ", path.call(printType, "type")]));
396
+ }
397
+
398
+ // Prints out a mixin, which looks like:
399
+ // include Foo
400
+ // prepend Foo
401
+ // extend Foo
402
+ function printMixin() {
403
+ return group(concat([node.member, " ", printNameAndArgs(path)]));
404
+ }
405
+
406
+ // Returns an array of printed parameters so that the calling function can
407
+ // join them together in whatever way.
408
+ function printMethodParams(path) {
409
+ const node = path.getValue();
410
+ let parts = [];
411
+
412
+ // required positionals, as in (A)
413
+ parts = parts.concat(path.map(printMethodParam, "required_positionals"));
414
+
415
+ // optional positionals, as in (?A)
416
+ parts = parts.concat(
417
+ path.map(
418
+ (paramPath) => concat(["?", printMethodParam(paramPath)]),
419
+ "optional_positionals"
420
+ )
421
+ );
422
+
423
+ // rest positional, as in (*A)
424
+ if (node.rest_positionals) {
425
+ parts.push(
426
+ concat(["*", path.call(printMethodParam, "rest_positionals")])
427
+ );
428
+ }
429
+
430
+ // trailing positionals are required positionals after a rest
431
+ parts = parts.concat(path.map(printMethodParam, "trailing_positionals"));
432
+
433
+ // required keywords, as in (a: A)
434
+ getSortedKeys(node.required_keywords).forEach((name) => {
435
+ parts.push(
436
+ concat([
437
+ name,
438
+ ": ",
439
+ path.call(printMethodParam, "required_keywords", name)
440
+ ])
441
+ );
442
+ });
443
+
444
+ // optional keywords, as in (?a: A)
445
+ getSortedKeys(node.optional_keywords).forEach((name) => {
446
+ parts.push(
447
+ concat([
448
+ "?",
449
+ name,
450
+ ": ",
451
+ path.call(printMethodParam, "optional_keywords", name)
452
+ ])
453
+ );
454
+ });
455
+
456
+ // rest keyword, as in (**A)
457
+ if (node.rest_keywords) {
458
+ parts.push(concat(["**", path.call(printMethodParam, "rest_keywords")]));
459
+ }
460
+
461
+ return parts;
462
+
463
+ // Prints out a method parameter at a given path. Handles printing out the
464
+ // name if there is one (and whether or not it's escaped).
465
+ function printMethodParam(path) {
466
+ const node = path.getValue();
467
+ const parts = [path.call(printType, "type")];
468
+
469
+ if (node.name) {
470
+ parts.push(" ");
471
+
472
+ if (node.escaped) {
473
+ parts.push("`", node.name, "`");
474
+ } else {
475
+ parts.push(node.name);
476
+ }
477
+ }
478
+
479
+ return concat(parts);
480
+ }
481
+ }
482
+
483
+ // Prints out a specific method signature, which looks like:
484
+ // (T t) -> void
485
+ function printMethodSignature(path) {
486
+ const node = path.getValue();
487
+ const parts = [];
488
+
489
+ // We won't have a type_params key if we're printing a block
490
+ if (node.type_params && node.type_params.length > 0) {
491
+ parts.push("[", join(", ", node.type_params), "] ");
492
+ }
493
+
494
+ let params = path.call(printMethodParams, "type");
495
+
496
+ if (params.length > 0) {
497
+ parts.push(
498
+ "(",
499
+ indent(concat([softline, join(concat([",", line]), params)])),
500
+ softline,
501
+ ") "
502
+ );
503
+ }
504
+
505
+ if (node.block) {
506
+ if (!node.block.required) {
507
+ parts.push("?");
508
+ }
509
+
510
+ parts.push(
511
+ "{",
512
+ indent(concat([line, path.call(printMethodSignature, "block")])),
513
+ line,
514
+ "} "
515
+ );
516
+ }
517
+
518
+ parts.push("-> ", path.call(printType, "type", "return_type"));
519
+
520
+ return group(concat(parts));
521
+ }
522
+
523
+ // Prints out a method definition, which looks like:
524
+ // def t: (T t) -> void
525
+ function printMethodDefinition() {
526
+ let typeDocs = path.map(printMethodSignature, "types");
527
+
528
+ if (node.overload) {
529
+ typeDocs.push("...");
530
+ }
531
+
532
+ if (typeDocs.length === 1) {
533
+ typeDocs = concat([" ", typeDocs[0]]);
534
+ } else {
535
+ typeDocs = indent(
536
+ group(concat([line, join(concat([line, "| "]), typeDocs)]))
537
+ );
538
+ }
539
+
540
+ const parts = ["def "];
541
+
542
+ if (node.kind === "singleton") {
543
+ parts.push("self.");
544
+ } else if (node.kind === "singleton_instance") {
545
+ parts.push("self?.");
546
+ }
547
+
548
+ const escaped = isMethodNameEscaped();
549
+ parts.push(escaped ? `\`${node.name}\`` : node.name, ":", typeDocs);
550
+
551
+ return group(concat(parts));
552
+
553
+ // Determine if a method name is escaped in the original source.
554
+ function isMethodNameEscaped() {
555
+ const pos = node.location.start_pos + 4;
556
+ const name = opts.originalText.slice(pos, pos + 2).trimStart();
557
+
558
+ return name[0] === "`" && name[1] !== ":";
559
+ }
560
+ }
561
+
562
+ // An annotation can be attached to most kinds of nodes, and should be printed
563
+ // using %a{}.
564
+ function printAnnotations() {
565
+ return join(hardline, path.map(printAnnotation, "annotations"));
566
+
567
+ function printAnnotation(path) {
568
+ const node = path.getValue();
569
+
570
+ // If there are already braces inside the annotation, then we're just
571
+ // going to print out the original string to avoid having to escape
572
+ // anything.
573
+ if (/[{}]/.test(node.string)) {
574
+ return getSource(node, opts);
575
+ }
576
+
577
+ return concat(["%a{", node.string, "}"]);
578
+ }
579
+ }
580
+
581
+ // Comments come in as one whole string, so here we split it up into multiple
582
+ // lines and then prefix it with the pound sign.
583
+ function printComment() {
584
+ const lines = node.comment.string.slice(0, -1).split("\n");
585
+
586
+ return join(
587
+ hardline,
588
+ lines.map((segment) => `# ${segment}`)
589
+ );
590
+ }
591
+ }
592
+
593
+ // This is an escape-hatch to ignore nodes in the tree. If you have a comment
594
+ // that includes this pattern, then the entire node will be ignored and just the
595
+ // original source will be printed out.
596
+ function hasPrettierIgnore(path) {
597
+ const node = path.getValue();
598
+
599
+ return node.comment && node.comment.string.includes("prettier-ignore");
600
+ }
601
+
602
+ module.exports = {
603
+ print: printNode,
604
+ hasPrettierIgnore
605
+ };