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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -7
  3. data/CONTRIBUTING.md +4 -4
  4. data/README.md +18 -14
  5. data/package.json +9 -6
  6. data/src/embed.js +27 -8
  7. data/src/nodes.js +5 -2
  8. data/src/nodes/alias.js +29 -31
  9. data/src/nodes/aref.js +26 -26
  10. data/src/nodes/args.js +55 -47
  11. data/src/nodes/arrays.js +132 -106
  12. data/src/nodes/assign.js +32 -32
  13. data/src/nodes/blocks.js +8 -3
  14. data/src/nodes/calls.js +163 -60
  15. data/src/nodes/case.js +11 -7
  16. data/src/nodes/class.js +74 -0
  17. data/src/nodes/commands.js +36 -31
  18. data/src/nodes/conditionals.js +44 -30
  19. data/src/nodes/constants.js +39 -21
  20. data/src/nodes/flow.js +11 -1
  21. data/src/nodes/hashes.js +90 -109
  22. data/src/nodes/heredocs.js +34 -0
  23. data/src/nodes/hooks.js +21 -22
  24. data/src/nodes/ints.js +27 -20
  25. data/src/nodes/lambdas.js +14 -27
  26. data/src/nodes/loops.js +10 -5
  27. data/src/nodes/massign.js +87 -65
  28. data/src/nodes/methods.js +48 -73
  29. data/src/nodes/operators.js +70 -39
  30. data/src/nodes/params.js +26 -16
  31. data/src/nodes/patterns.js +108 -33
  32. data/src/nodes/regexp.js +45 -14
  33. data/src/nodes/rescue.js +72 -59
  34. data/src/nodes/statements.js +86 -44
  35. data/src/nodes/strings.js +95 -85
  36. data/src/nodes/super.js +35 -0
  37. data/src/nodes/undef.js +42 -0
  38. data/src/parser.js +86 -0
  39. data/src/parser.rb +2400 -621
  40. data/src/printer.js +90 -0
  41. data/src/ruby.js +19 -41
  42. data/src/toProc.js +4 -4
  43. data/src/utils.js +24 -88
  44. data/src/utils/literalLineNoBreak.js +7 -0
  45. data/src/utils/printEmptyCollection.js +42 -0
  46. metadata +12 -49
  47. data/src/nodes/scopes.js +0 -61
  48. data/src/parse.js +0 -37
  49. data/src/print.js +0 -23
@@ -1,52 +1,83 @@
1
1
  const { concat, group, indent, line, softline } = require("../prettier");
2
+ const { noIndent } = require("../utils");
2
3
 
3
- module.exports = {
4
- binary: (path, opts, print) => {
5
- const operator = path.getValue().body[1];
6
- const useNoSpace = operator === "**";
4
+ function printBinary(path, opts, print) {
5
+ const [_leftNode, operator, rightNode] = path.getValue().body;
6
+ const space = operator === "**" ? "" : " ";
7
7
 
8
+ if (noIndent.includes(rightNode.type)) {
8
9
  return group(
9
10
  concat([
10
11
  group(path.call(print, "body", 0)),
12
+ space,
13
+ operator,
14
+ space,
15
+ group(path.call(print, "body", 2))
16
+ ])
17
+ );
18
+ }
19
+
20
+ return group(
21
+ concat([
22
+ group(path.call(print, "body", 0)),
23
+ space,
24
+ group(
11
25
  indent(
12
26
  concat([
13
- useNoSpace ? "" : " ",
14
- group(
15
- concat([
16
- operator,
17
- useNoSpace ? softline : line,
18
- path.call(print, "body", 2)
19
- ])
20
- )
27
+ operator,
28
+ space === "" ? softline : line,
29
+ path.call(print, "body", 2)
21
30
  ])
22
31
  )
23
- ])
24
- );
25
- },
26
- dot2: (path, opts, print) =>
27
- concat([
28
- path.call(print, "body", 0),
29
- "..",
30
- path.getValue().body[1] ? path.call(print, "body", 1) : ""
31
- ]),
32
- dot3: (path, opts, print) =>
33
- concat([
34
- path.call(print, "body", 0),
35
- "...",
36
- path.getValue().body[1] ? path.call(print, "body", 1) : ""
37
- ]),
38
- unary: (path, opts, print) => {
39
- const oper = path.getValue().body[0];
40
- const doc = path.call(print, "body", 1);
41
-
42
- if (oper === "not") {
43
- // For the `not` operator, we're explicitly making the space character
44
- // another element in the `concat` because there are some circumstances
45
- // where we need to force parentheses (e.g., ternaries). In that case the
46
- // printer for those nodes can just take out the space and put in parens.
47
- return concat(["not", " ", doc]);
48
- }
32
+ )
33
+ ])
34
+ );
35
+ }
36
+
37
+ // dot2 nodes are used with ranges (or flip-flops). They can optionally drop
38
+ // their left side for beginless ranges or their right side for endless ranges.
39
+ function printDot2(path, opts, print) {
40
+ const [leftNode, rightNode] = path.getValue().body;
41
+
42
+ return concat([
43
+ leftNode ? path.call(print, "body", 0) : "",
44
+ "..",
45
+ rightNode ? path.call(print, "body", 1) : ""
46
+ ]);
47
+ }
48
+
49
+ // dot3 nodes are used with ranges (or flip-flops). They can optionally drop
50
+ // their left side for beginless ranges or their right side for endless ranges.
51
+ function printDot3(path, opts, print) {
52
+ const [leftNode, rightNode] = path.getValue().body;
53
+
54
+ return concat([
55
+ leftNode ? path.call(print, "body", 0) : "",
56
+ "...",
57
+ rightNode ? path.call(print, "body", 1) : ""
58
+ ]);
59
+ }
49
60
 
50
- return concat([oper[0], doc]);
61
+ function printUnary(path, opts, print) {
62
+ const node = path.getValue();
63
+ const contentsDoc = path.call(print, "body", 0);
64
+
65
+ if (node.oper === "not") {
66
+ // Here we defer to the original source, as it's kind of difficult to
67
+ // determine if we can actually remove the parentheses being used.
68
+ if (node.paren) {
69
+ return concat(["not", "(", contentsDoc, ")"]);
70
+ } else {
71
+ return concat(["not", " ", contentsDoc]);
72
+ }
51
73
  }
74
+
75
+ return concat([node.oper, contentsDoc]);
76
+ }
77
+
78
+ module.exports = {
79
+ binary: printBinary,
80
+ dot2: printDot2,
81
+ dot3: printDot3,
82
+ unary: printUnary
52
83
  };
@@ -1,12 +1,15 @@
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
- const printGenericRestParam = (symbol) => (path, opts, print) =>
5
- path.getValue().body[0]
6
- ? concat([symbol, path.call(print, "body", 0)])
7
- : symbol;
4
+ function printRestParam(symbol) {
5
+ return function printRestParamWithSymbol(path, opts, print) {
6
+ return path.getValue().body[0]
7
+ ? concat([symbol, path.call(print, "body", 0)])
8
+ : symbol;
9
+ };
10
+ }
8
11
 
9
- const printParams = (path, opts, print) => {
12
+ function printParams(path, opts, print) {
10
13
  const [
11
14
  reqs,
12
15
  optls,
@@ -61,6 +64,8 @@ const printParams = (path, opts, print) => {
61
64
  parts.push(path.call(print, "body", 6));
62
65
  }
63
66
 
67
+ const contents = [join(concat([",", line]), parts)];
68
+
64
69
  // You can put an extra comma at the end of block args between pipes to
65
70
  // change what it does. Below is the difference:
66
71
  //
@@ -70,19 +75,24 @@ const printParams = (path, opts, print) => {
70
75
  // In ruby 2.5, the excessed comma is indicated by having a 0 in the rest
71
76
  // param position. In ruby 2.6+ it's indicated by having an "excessed_comma"
72
77
  // node in the rest position. Seems odd, but it's true.
73
- const comma = rest === 0 || (rest && rest.type === "excessed_comma");
78
+ if (rest === 0 || (rest && rest.type === "excessed_comma")) {
79
+ contents.push(",");
80
+ }
74
81
 
75
- return group(concat([join(concat([",", line]), parts), comma ? "," : ""]));
76
- };
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
- const paramError = () => {
79
- throw new Error("formal argument cannot be a global variable");
80
- };
90
+ return group(concat(contents));
91
+ }
81
92
 
82
93
  module.exports = {
83
94
  args_forward: literal("..."),
84
- kwrest_param: printGenericRestParam("**"),
85
- rest_param: printGenericRestParam("*"),
86
- params: printParams,
87
- param_error: paramError
95
+ kwrest_param: printRestParam("**"),
96
+ rest_param: printRestParam("*"),
97
+ params: printParams
88
98
  };
@@ -1,43 +1,118 @@
1
1
  const { concat, group, hardline, indent, join, line } = require("../prettier");
2
2
 
3
- module.exports = {
4
- aryptn: (path, opts, print) => {
5
- const [constant, preargs, splatarg, postargs] = path.getValue().body;
6
- let args = [];
3
+ function printPatternArg(path, opts, print) {
4
+ // Pinning is a really special syntax in pattern matching that's not really
5
+ // all that well supported in ripper. Here we're just going to the original
6
+ // source to see if the variable is pinned.
7
+ if (
8
+ opts.originalText &&
9
+ opts.originalText[opts.locStart(path.getValue()) - 1] === "^"
10
+ ) {
11
+ return concat(["^", path.call(print)]);
12
+ }
7
13
 
8
- if (preargs) {
9
- args = args.concat(path.map(print, "body", 1));
10
- }
14
+ return path.call(print);
15
+ }
11
16
 
12
- if (splatarg) {
13
- args.push(concat(["*", path.call(print, "body", 2)]));
14
- }
17
+ function printAryPtn(path, opts, print) {
18
+ const [constant, preargs, splatarg, postargs] = path.getValue().body;
19
+ let args = [];
15
20
 
16
- if (postargs) {
17
- args = args.concat(path.map(print, "body", 3));
18
- }
21
+ if (preargs) {
22
+ args = args.concat(
23
+ path.map((argPath) => printPatternArg(argPath, opts, print), "body", 1)
24
+ );
25
+ }
19
26
 
20
- args = group(join(concat([",", line]), args));
27
+ if (splatarg) {
28
+ args.push(concat(["*", path.call(print, "body", 2)]));
29
+ }
21
30
 
22
- if (constant || path.getParentNode().type === "binary") {
23
- args = concat(["[", args, "]"]);
24
- }
31
+ if (postargs) {
32
+ args = args.concat(path.map(print, "body", 3));
33
+ }
25
34
 
26
- if (constant) {
27
- return concat([path.call(print, "body", 0), args]);
28
- }
35
+ args = group(join(concat([",", line]), args));
29
36
 
30
- return args;
31
- },
32
- hshptn: () => {
33
- throw new Error(
34
- "Hash pattern not currently supported (https://bugs.ruby-lang.org/issues/16008)"
35
- );
36
- },
37
- in: (path, opts, print) =>
38
- concat([
39
- "in ",
40
- path.call(print, "body", 0),
41
- indent(concat([hardline, path.call(print, "body", 1)]))
42
- ])
37
+ if (
38
+ constant ||
39
+ ["aryptn", "binary", "hshptn"].includes(path.getParentNode().type)
40
+ ) {
41
+ args = concat(["[", args, "]"]);
42
+ }
43
+
44
+ if (constant) {
45
+ return concat([path.call(print, "body", 0), args]);
46
+ }
47
+
48
+ return args;
49
+ }
50
+
51
+ function printHshPtn(path, opts, print) {
52
+ const [constant, keyValuePairs, keyValueRest] = path.getValue().body;
53
+ let args = [];
54
+
55
+ if (keyValuePairs) {
56
+ const printPair = (pairPath) => {
57
+ const parts = [pairPath.call(print, 0)];
58
+
59
+ if (pairPath.getValue()[1]) {
60
+ parts.push(
61
+ " ",
62
+ pairPath.call(
63
+ (pairValuePath) => printPatternArg(pairValuePath, opts, print),
64
+ 1
65
+ )
66
+ );
67
+ }
68
+
69
+ return concat(parts);
70
+ };
71
+
72
+ args = args.concat(path.map(printPair, "body", 1));
73
+ }
74
+
75
+ if (keyValueRest) {
76
+ args.push(concat(["**", path.call(print, "body", 2)]));
77
+ }
78
+
79
+ args = group(join(concat([",", line]), args));
80
+
81
+ if (constant) {
82
+ args = concat(["[", args, "]"]);
83
+ } else if (
84
+ ["aryptn", "binary", "hshptn"].includes(path.getParentNode().type)
85
+ ) {
86
+ args = concat(["{", args, "}"]);
87
+ }
88
+
89
+ if (constant) {
90
+ return concat([path.call(print, "body", 0), args]);
91
+ }
92
+
93
+ return args;
94
+ }
95
+
96
+ function printIn(path, opts, print) {
97
+ const parts = [
98
+ "in ",
99
+ path.call(
100
+ (valuePath) => printPatternArg(valuePath, opts, print),
101
+ "body",
102
+ 0
103
+ ),
104
+ indent(concat([hardline, path.call(print, "body", 1)]))
105
+ ];
106
+
107
+ if (path.getValue().body[2]) {
108
+ parts.push(hardline, path.call(print, "body", 2));
109
+ }
110
+
111
+ return group(concat(parts));
112
+ }
113
+
114
+ module.exports = {
115
+ aryptn: printAryPtn,
116
+ hshptn: printHshPtn,
117
+ in: printIn
43
118
  };
@@ -1,18 +1,49 @@
1
1
  const { concat } = require("../prettier");
2
- const { makeList } = require("../utils");
2
+ const { hasAncestor } = require("../utils");
3
3
 
4
- module.exports = {
5
- regexp: makeList,
6
- regexp_literal: (path, opts, print) => {
7
- const [contents, ending] = path.map(print, "body");
8
-
9
- const useBraces = contents.some(
10
- (content) => typeof content === "string" && content.includes("/")
11
- );
12
- const parts = [useBraces ? "%r{" : "/"]
13
- .concat(contents)
14
- .concat([useBraces ? "}" : "/", ending.slice(1)]);
15
-
16
- return concat(parts);
4
+ function isStringContent(node) {
5
+ return node.type === "@tstring_content";
6
+ }
7
+
8
+ function shouldUseBraces(path) {
9
+ const node = path.getValue();
10
+ const first = node.body[0];
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;
17
22
  }
23
+
24
+ return node.body.some(
25
+ (child) => isStringContent(child) && child.body.includes("/")
26
+ );
27
+ }
28
+
29
+ // This function is responsible for printing out regexp_literal nodes. They can
30
+ // either use the special %r literal syntax or they can use forward slashes. At
31
+ // the end of either of those they can have modifiers like m or x that have
32
+ // special meaning for the regex engine.
33
+ //
34
+ // We favor the use of forward slashes unless the regex contains a forward slash
35
+ // itself. In that case we switch over to using %r with braces.
36
+ function printRegexpLiteral(path, opts, print) {
37
+ const node = path.getValue();
38
+ const useBraces = shouldUseBraces(path);
39
+
40
+ const parts = [useBraces ? "%r{" : "/"]
41
+ .concat(path.map(print, "body"))
42
+ .concat([useBraces ? "}" : "/", node.ending.slice(1)]);
43
+
44
+ return concat(parts);
45
+ }
46
+
47
+ module.exports = {
48
+ regexp_literal: printRegexpLiteral
18
49
  };
@@ -9,73 +9,86 @@ const {
9
9
  } = require("../prettier");
10
10
  const { literal } = require("../utils");
11
11
 
12
- module.exports = {
13
- begin: (path, opts, print) =>
14
- concat([
15
- "begin",
16
- indent(concat([hardline, concat(path.map(print, "body"))])),
17
- hardline,
18
- "end"
19
- ]),
20
- ensure: (path, opts, print) =>
21
- concat([
22
- "ensure",
23
- indent(concat([hardline, concat(path.map(print, "body"))]))
24
- ]),
25
- redo: literal("redo"),
26
- rescue: (path, opts, print) => {
27
- const [exception, variable, _stmts, addition] = path.getValue().body;
28
- const parts = ["rescue"];
12
+ function printBegin(path, opts, print) {
13
+ return concat([
14
+ "begin",
15
+ indent(concat([hardline, concat(path.map(print, "body"))])),
16
+ hardline,
17
+ "end"
18
+ ]);
19
+ }
29
20
 
30
- if (exception || variable) {
31
- if (exception) {
32
- if (Array.isArray(exception)) {
33
- // In this case, it's actually only the one exception (it's an array
34
- // of length 1).
35
- parts.push(" ", path.call(print, "body", 0, 0));
36
- } else {
37
- // Here we have multiple exceptions from which we're rescuing, so we
38
- // need to align them and join them together.
39
- const joiner = concat([",", line]);
40
- const exceptions = group(join(joiner, path.call(print, "body", 0)));
21
+ function printEnsure(path, opts, print) {
22
+ return concat([
23
+ path.call(print, "body", 0),
24
+ indent(concat([hardline, path.call(print, "body", 1)]))
25
+ ]);
26
+ }
41
27
 
42
- parts.push(" ", align("rescue ".length, exceptions));
43
- }
44
- }
28
+ function printRescue(path, opts, print) {
29
+ const [exception, variable, _stmts, addition] = path.getValue().body;
30
+ const parts = ["rescue"];
31
+
32
+ if (exception || variable) {
33
+ if (exception) {
34
+ if (Array.isArray(exception)) {
35
+ // In this case, it's actually only the one exception (it's an array
36
+ // of length 1).
37
+ parts.push(" ", path.call(print, "body", 0, 0));
38
+ } else {
39
+ // Here we have multiple exceptions from which we're rescuing, so we
40
+ // need to align them and join them together.
41
+ const joiner = concat([",", line]);
42
+ const exceptions = group(join(joiner, path.call(print, "body", 0)));
45
43
 
46
- if (variable) {
47
- parts.push(" => ", path.call(print, "body", 1));
44
+ parts.push(" ", align("rescue ".length, exceptions));
48
45
  }
49
- } else {
50
- // If you don't specify an error to rescue in a `begin/rescue` block, then
51
- // implicitly you're rescuing from `StandardError`. In this case, we're
52
- // just going to explicitly add it.
53
- parts.push(" StandardError");
54
46
  }
55
47
 
56
- const rescueBody = path.call(print, "body", 2);
57
-
58
- if (rescueBody.parts.length > 0) {
59
- parts.push(indent(concat([hardline, rescueBody])));
48
+ if (variable) {
49
+ parts.push(" => ", path.call(print, "body", 1));
60
50
  }
51
+ } else {
52
+ // If you don't specify an error to rescue in a `begin/rescue` block, then
53
+ // implicitly you're rescuing from `StandardError`. In this case, we're
54
+ // just going to explicitly add it.
55
+ parts.push(" StandardError");
56
+ }
61
57
 
62
- // This is the next clause on the `begin` statement, either another
63
- // `rescue`, and `ensure`, or an `else` clause.
64
- if (addition) {
65
- parts.push(concat([hardline, path.call(print, "body", 3)]));
66
- }
58
+ const rescueBody = path.call(print, "body", 2);
59
+
60
+ if (rescueBody.parts.length > 0) {
61
+ parts.push(indent(concat([hardline, rescueBody])));
62
+ }
67
63
 
68
- return group(concat(parts));
69
- },
70
- rescue_mod: (path, opts, print) =>
71
- concat([
72
- "begin",
73
- indent(concat([hardline, path.call(print, "body", 0)])),
74
- hardline,
75
- "rescue StandardError",
76
- indent(concat([hardline, path.call(print, "body", 1)])),
77
- hardline,
78
- "end"
79
- ]),
64
+ // This is the next clause on the `begin` statement, either another
65
+ // `rescue`, and `ensure`, or an `else` clause.
66
+ if (addition) {
67
+ parts.push(concat([hardline, path.call(print, "body", 3)]));
68
+ }
69
+
70
+ return group(concat(parts));
71
+ }
72
+
73
+ function printRescueMod(path, opts, print) {
74
+ const [statementDoc, valueDoc] = path.map(print, "body");
75
+
76
+ return concat([
77
+ "begin",
78
+ indent(concat([hardline, statementDoc])),
79
+ hardline,
80
+ "rescue StandardError",
81
+ indent(concat([hardline, valueDoc])),
82
+ hardline,
83
+ "end"
84
+ ]);
85
+ }
86
+
87
+ module.exports = {
88
+ begin: printBegin,
89
+ ensure: printEnsure,
90
+ redo: literal("redo"),
91
+ rescue: printRescue,
92
+ rescue_mod: printRescueMod,
80
93
  retry: literal("retry")
81
94
  };