prettier 0.20.0 → 1.0.0.pre.rc2

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +118 -4
  3. data/CONTRIBUTING.md +8 -6
  4. data/README.md +67 -60
  5. data/node_modules/prettier/bin-prettier.js +12317 -50112
  6. data/node_modules/prettier/index.js +33352 -27419
  7. data/node_modules/prettier/third-party.js +5678 -7676
  8. data/package.json +4 -4
  9. data/src/embed.js +27 -8
  10. data/src/nodes.js +6 -2
  11. data/src/nodes/alias.js +65 -24
  12. data/src/nodes/aref.js +55 -0
  13. data/src/nodes/args.js +55 -47
  14. data/src/nodes/arrays.js +150 -137
  15. data/src/nodes/assign.js +32 -32
  16. data/src/nodes/blocks.js +8 -3
  17. data/src/nodes/calls.js +129 -70
  18. data/src/nodes/case.js +11 -7
  19. data/src/nodes/class.js +74 -0
  20. data/src/nodes/commands.js +36 -31
  21. data/src/nodes/conditionals.js +48 -46
  22. data/src/nodes/constants.js +39 -21
  23. data/src/nodes/flow.js +45 -17
  24. data/src/nodes/hashes.js +126 -112
  25. data/src/nodes/heredocs.js +34 -0
  26. data/src/nodes/hooks.js +36 -7
  27. data/src/nodes/ints.js +27 -20
  28. data/src/nodes/lambdas.js +69 -52
  29. data/src/nodes/loops.js +19 -29
  30. data/src/nodes/massign.js +87 -65
  31. data/src/nodes/methods.js +48 -73
  32. data/src/nodes/operators.js +70 -39
  33. data/src/nodes/params.js +26 -16
  34. data/src/nodes/patterns.js +108 -33
  35. data/src/nodes/regexp.js +38 -14
  36. data/src/nodes/rescue.js +72 -59
  37. data/src/nodes/statements.js +86 -44
  38. data/src/nodes/strings.js +94 -90
  39. data/src/nodes/super.js +35 -0
  40. data/src/nodes/undef.js +42 -0
  41. data/src/parser.js +71 -0
  42. data/src/parser.rb +2554 -0
  43. data/src/printer.js +90 -0
  44. data/src/ruby.js +20 -61
  45. data/src/toProc.js +4 -4
  46. data/src/utils.js +24 -88
  47. data/src/utils/inlineEnsureParens.js +42 -0
  48. data/src/utils/isEmptyStmts.js +7 -0
  49. data/src/utils/literalLineNoBreak.js +7 -0
  50. metadata +15 -20
  51. data/src/haml.js +0 -21
  52. data/src/haml/embed.js +0 -58
  53. data/src/haml/nodes/comment.js +0 -27
  54. data/src/haml/nodes/doctype.js +0 -32
  55. data/src/haml/nodes/filter.js +0 -16
  56. data/src/haml/nodes/hamlComment.js +0 -21
  57. data/src/haml/nodes/script.js +0 -29
  58. data/src/haml/nodes/silentScript.js +0 -59
  59. data/src/haml/nodes/tag.js +0 -157
  60. data/src/haml/parse.js +0 -18
  61. data/src/haml/parse.rb +0 -64
  62. data/src/haml/print.js +0 -38
  63. data/src/nodes/scopes.js +0 -61
  64. data/src/parse.js +0 -37
  65. data/src/print.js +0 -23
  66. data/src/ripper.rb +0 -811
@@ -0,0 +1,34 @@
1
+ const { concat, group, lineSuffix, join } = require("../prettier");
2
+ const { literalLineNoBreak } = require("../utils");
3
+
4
+ function printHeredoc(path, opts, print) {
5
+ const { body, ending } = path.getValue();
6
+
7
+ const parts = body.map((part, index) => {
8
+ if (part.type !== "@tstring_content") {
9
+ // In this case, the part of the string is an embedded expression
10
+ return path.call(print, "body", index);
11
+ }
12
+
13
+ // In this case, the part of the string is just regular string content
14
+ return join(literalLineNoBreak, part.body.split("\n"));
15
+ });
16
+
17
+ // We use a literalline break because matching indentation is required
18
+ // for the heredoc contents and ending. If the line suffix contains a
19
+ // break-parent, all ancestral groups are broken, and heredocs automatically
20
+ // break lines in groups they appear in. We prefer them to appear in-line if
21
+ // possible, so we use a literalline without the break-parent.
22
+ return group(
23
+ concat([
24
+ path.call(print, "beging"),
25
+ lineSuffix(
26
+ group(concat([literalLineNoBreak].concat(parts).concat(ending)))
27
+ )
28
+ ])
29
+ );
30
+ }
31
+
32
+ module.exports = {
33
+ heredoc: printHeredoc
34
+ };
@@ -1,13 +1,42 @@
1
1
  const { concat, group, indent, line } = require("../prettier");
2
+ const { isEmptyStmts } = require("../utils");
2
3
 
3
- const printHook = (name) => (path, opts, print) =>
4
- group(
5
- concat([
6
- `${name} {`,
7
- indent(concat([line, path.call(print, "body", 0)])),
4
+ // The `BEGIN` and `END` keywords are used to hook into the Ruby process. Any
5
+ // `BEGIN` blocks are executed right when the process starts up, and the `END`
6
+ // blocks are executed right before exiting.
7
+ //
8
+ // BEGIN {
9
+ // # content goes here
10
+ // }
11
+ //
12
+ // END {
13
+ // # content goes here
14
+ // }
15
+ //
16
+ // Interesting side note, you don't use `do...end` blocks with these hooks. Both
17
+ // nodes contain one child which is a `stmts` node.
18
+ function printHook(name) {
19
+ return function printHookWithName(path, opts, print) {
20
+ const stmtsNode = path.getValue().body[1];
21
+ const printedStmts = path.call(print, "body", 1);
22
+
23
+ const parts = [
24
+ name,
25
+ " ",
26
+ path.call(print, "body", 0),
27
+ indent(concat([line, printedStmts])),
8
28
  concat([line, "}"])
9
- ])
10
- );
29
+ ];
30
+
31
+ // If there are no statements but there are comments, then we want to skip
32
+ // printing the newline so that we don't end up with multiple spaces.
33
+ if (isEmptyStmts(stmtsNode) && stmtsNode.comments) {
34
+ parts[1] = indent(printedStmts);
35
+ }
36
+
37
+ return group(concat(parts));
38
+ };
39
+ }
11
40
 
12
41
  module.exports = {
13
42
  BEGIN: printHook("BEGIN"),
@@ -1,24 +1,31 @@
1
- module.exports = {
2
- "@int": (path, _opts, _print) => {
3
- const { body } = path.getValue();
1
+ // An @int node is any literal integer in Ruby. They can come in a number of
2
+ // bases, and look like the following:
3
+ //
4
+ // Binary (2) - 0b0110
5
+ // Octal (8) - 0o34 or 034
6
+ // Decimal (10) - a normal number like 159
7
+ // Hexidecimal (16) - 0xac5
8
+ //
9
+ // If it's a decimal number, it can be optional separated by any number of
10
+ // arbitrarily places underscores. This can be useful for dollars and cents
11
+ // (34_99), dates (2020_11_30), and normal 3 digit separation (1_222_333).
12
+ function printInt(path, _opts, _print) {
13
+ const { body } = path.getValue();
4
14
 
5
- // If the number is octal and does not contain the optional "o" character
6
- // after the leading 0, add it in.
7
- if (/^0[0-9]/.test(body)) {
8
- return `0o${body.slice(1)}`;
9
- }
15
+ // If the number is a base 10 number, is sufficiently large, and is not
16
+ // already formatted with underscores, then add them in in between the
17
+ // numbers every three characters starting from the right.
18
+ if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
19
+ return ` ${body}`
20
+ .slice((body.length + 2) % 3)
21
+ .match(/.{3}/g)
22
+ .join("_")
23
+ .trim();
24
+ }
10
25
 
11
- // If the number is a base 10 number, is sufficiently large, and is not
12
- // already formatted with underscores, then add them in in between the
13
- // numbers every three characters starting from the right.
14
- if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
15
- return ` ${body}`
16
- .slice((body.length + 2) % 3)
17
- .match(/.{3}/g)
18
- .join("_")
19
- .trim();
20
- }
26
+ return body;
27
+ }
21
28
 
22
- return body;
23
- }
29
+ module.exports = {
30
+ "@int": printInt
24
31
  };
@@ -1,59 +1,76 @@
1
- const {
2
- concat,
3
- group,
4
- ifBreak,
5
- indent,
6
- line,
7
- removeLines,
8
- softline
9
- } = require("../prettier");
1
+ const { concat, group, ifBreak, indent, line } = require("../prettier");
10
2
  const { hasAncestor } = require("../utils");
11
3
 
12
- module.exports = {
13
- lambda: (path, opts, print) => {
14
- let params = path.getValue().body[0];
15
- let paramsConcat = "";
4
+ // We can have our params coming in as the first child of the main lambda node,
5
+ // or if we have them wrapped in parens then they'll be one level deeper. Even
6
+ // though it's possible to omit the parens if you only have one argument, we're
7
+ // going to keep them in no matter what for consistency.
8
+ function printLambdaParams(path, print) {
9
+ let node = path.getValue().body[0];
16
10
 
17
- if (params.type === "params") {
18
- paramsConcat = path.call(print, "body", 0);
19
- } else {
20
- [params] = params.body;
21
- paramsConcat = path.call(print, "body", 0, "body", 0);
22
- }
11
+ // In this case we had something like -> (foo) { bar } which would mean that
12
+ // we're looking at a paren node, so we'll descend one level deeper to get at
13
+ // the actual params node.
14
+ if (node.type !== "params") {
15
+ node = node.body[0];
16
+ }
23
17
 
24
- const noParams = params.body.every((type) => !type);
25
- const inlineLambda = concat([
26
- "->",
27
- noParams ? "" : concat(["(", paramsConcat, ")"]),
28
- " { ",
29
- path.call(print, "body", 1),
30
- " }"
31
- ]);
18
+ // If we don't have any params at all, then we're just going to bail out and
19
+ // print nothing. This is to avoid printing an empty set of parentheses.
20
+ if (node.body.every((type) => !type)) {
21
+ return "";
22
+ }
32
23
 
33
- if (hasAncestor(path, ["command", "command_call"])) {
34
- return group(
35
- ifBreak(
36
- concat([
37
- "lambda {",
38
- noParams ? "" : concat([" |", removeLines(paramsConcat), "|"]),
39
- indent(concat([line, path.call(print, "body", 1)])),
40
- concat([line, "}"])
41
- ]),
42
- inlineLambda
43
- )
44
- );
45
- }
24
+ return path.call(print, "body", 0);
25
+ }
46
26
 
47
- return group(
48
- ifBreak(
49
- concat([
50
- "lambda do",
51
- noParams ? "" : concat([" |", removeLines(paramsConcat), "|"]),
52
- indent(concat([softline, path.call(print, "body", 1)])),
53
- concat([softline, "end"])
54
- ]),
55
- inlineLambda
56
- )
57
- );
58
- }
27
+ // Lambda nodes represent stabby lambda literals, which can come in a couple of
28
+ // flavors. They can use either braces or do...end for their block, and their
29
+ // arguments can be not present, have no parentheses for a single argument, or
30
+ // have parentheses for multiple arguments. Below are a couple of examples:
31
+ //
32
+ // -> { 1 }
33
+ // -> a { a + 1 }
34
+ // ->(a) { a + 1 }
35
+ // ->(a, b) { a + b }
36
+ // ->(a, b = 1) { a + b }
37
+ //
38
+ // -> do
39
+ // 1
40
+ // end
41
+ //
42
+ // -> a do
43
+ // a + 1
44
+ // end
45
+ //
46
+ // ->(a, b) do
47
+ // a + b
48
+ // end
49
+ //
50
+ // Generally, we're going to favor do...end for the multi-line form and braces
51
+ // for the single-line form. However, if we have an ancestor that is a command
52
+ // or command_call node, then we'll need to use braces either way because of
53
+ // operator precendence.
54
+ function printLambda(path, opts, print) {
55
+ const params = printLambdaParams(path, print);
56
+ const inCommand = hasAncestor(path, ["command", "command_call"]);
57
+
58
+ return group(
59
+ ifBreak(
60
+ concat([
61
+ "->",
62
+ params,
63
+ " ",
64
+ inCommand ? "{" : "do",
65
+ indent(concat([line, path.call(print, "body", 1)])),
66
+ line,
67
+ inCommand ? "}" : "end"
68
+ ]),
69
+ concat(["->", params, " { ", path.call(print, "body", 1), " }"])
70
+ )
71
+ );
72
+ }
73
+
74
+ module.exports = {
75
+ lambda: printLambda
59
76
  };
@@ -1,5 +1,6 @@
1
1
  const {
2
2
  align,
3
+ breakParent,
3
4
  concat,
4
5
  group,
5
6
  hardline,
@@ -7,14 +8,20 @@ const {
7
8
  indent,
8
9
  softline
9
10
  } = require("../prettier");
11
+
10
12
  const { containsAssignment } = require("../utils");
13
+ const inlineEnsureParens = require("../utils/inlineEnsureParens");
11
14
 
12
- const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
13
- const [_predicate, statements] = path.getValue().body;
15
+ const printLoop = (keyword, modifier) => (path, { rubyModifier }, print) => {
16
+ const [_predicate, stmts] = path.getValue().body;
14
17
 
15
18
  // If the only statement inside this while loop is a void statement, then we
16
19
  // can shorten to just displaying the predicate and then a semicolon.
17
- if (statements.body.length === 1 && statements.body[0].type === "void_stmt") {
20
+ if (
21
+ stmts.body.length === 1 &&
22
+ stmts.body[0].type === "void_stmt" &&
23
+ !stmts.body[0].comments
24
+ ) {
18
25
  return group(
19
26
  concat([
20
27
  keyword,
@@ -26,30 +33,13 @@ const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
26
33
  );
27
34
  }
28
35
 
29
- let inlineParts = [
30
- path.call(print, "body", 1),
31
- ` ${keyword} `,
32
- path.call(print, "body", 0)
33
- ];
34
-
35
- // If the return value of this loop expression is being assigned to anything
36
- // besides a local variable then we can't inline the entire expression
37
- // without wrapping it in parentheses. This is because the following
38
- // expressions have different semantic meaning:
39
- //
40
- // hash[:key] = break :value while false
41
- // hash[:key] = while false do break :value end
42
- //
43
- // The first one will not result in an empty hash, whereas the second one
44
- // will result in `{ key: nil }`. In this case what we need to do for the
45
- // first expression to align is wrap it in parens, as in:
46
- //
47
- // hash[:key] = (break :value while false)
48
- if (["assign", "massign"].includes(path.getParentNode().type)) {
49
- inlineParts = ["("].concat(inlineParts).concat(")");
50
- }
51
-
52
- const inlineLoop = concat(inlineParts);
36
+ const inlineLoop = concat(
37
+ inlineEnsureParens(path, [
38
+ path.call(print, "body", 1),
39
+ ` ${keyword} `,
40
+ path.call(print, "body", 0)
41
+ ])
42
+ );
53
43
 
54
44
  // If we're in the modifier form and we're modifying a `begin`, then this is a
55
45
  // special case where we need to explicitly use the modifier form because
@@ -77,8 +67,8 @@ const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
77
67
  // an assignment (in which case we can't know for certain that that
78
68
  // assignment doesn't impact the statements inside the loop) then we can't
79
69
  // use the modifier form and we must use the block form.
80
- if (!inlineLoops || containsAssignment(path.getValue().body[0])) {
81
- return blockLoop;
70
+ if (!rubyModifier || containsAssignment(path.getValue().body[0])) {
71
+ return concat([breakParent, blockLoop]);
82
72
  }
83
73
 
84
74
  return group(ifBreak(blockLoop, inlineLoop));
@@ -1,69 +1,91 @@
1
1
  const { concat, group, indent, join, line, softline } = require("../prettier");
2
- const { makeList } = require("../utils");
3
2
 
4
- module.exports = {
5
- massign: (path, opts, print) => {
6
- let right = path.call(print, "body", 1);
7
-
8
- if (
9
- ["mrhs_add_star", "mrhs_new_from_args"].includes(
10
- path.getValue().body[1].type
11
- )
12
- ) {
13
- right = group(join(concat([",", line]), right));
14
- }
15
-
16
- const parts = [join(concat([",", line]), path.call(print, "body", 0))];
17
- if (path.getValue().body[0].comma) {
18
- parts.push(",");
19
- }
20
-
21
- return group(
22
- concat([group(concat(parts)), " =", indent(concat([line, right]))])
23
- );
24
- },
25
- mlhs: makeList,
26
- mlhs_add_post: (path, opts, print) =>
27
- path.call(print, "body", 0).concat(path.call(print, "body", 1)),
28
- mlhs_add_star: (path, opts, print) =>
29
- path
30
- .call(print, "body", 0)
31
- .concat([
32
- path.getValue().body[1]
33
- ? concat(["*", path.call(print, "body", 1)])
34
- : "*"
35
- ]),
36
- mlhs_paren: (path, opts, print) => {
37
- if (["massign", "mlhs_paren"].includes(path.getParentNode().type)) {
38
- // If we're nested in brackets as part of the left hand side of an
39
- // assignment, i.e., (a, b, c) = 1, 2, 3
40
- // ignore the current node and just go straight to the content
41
- return path.call(print, "body", 0);
42
- }
43
-
44
- const parts = [
45
- softline,
46
- join(concat([",", line]), path.call(print, "body", 0))
47
- ];
48
-
49
- if (path.getValue().body[0].comma) {
50
- parts.push(",");
51
- }
52
-
53
- return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
54
- },
55
- mrhs: makeList,
56
- mrhs_add_star: (path, opts, print) =>
57
- path
58
- .call(print, "body", 0)
59
- .concat([concat(["*", path.call(print, "body", 1)])]),
60
- mrhs_new_from_args: (path, opts, print) => {
61
- const parts = path.call(print, "body", 0);
62
-
63
- if (path.getValue().body.length > 1) {
64
- parts.push(path.call(print, "body", 1));
65
- }
66
-
67
- return parts;
3
+ function printMAssign(path, opts, print) {
4
+ let right = path.call(print, "body", 1);
5
+
6
+ if (
7
+ ["mrhs_add_star", "mrhs_new_from_args"].includes(
8
+ path.getValue().body[1].type
9
+ )
10
+ ) {
11
+ right = group(join(concat([",", line]), right));
12
+ }
13
+
14
+ const parts = [join(concat([",", line]), path.call(print, "body", 0))];
15
+ if (path.getValue().body[0].comma) {
16
+ parts.push(",");
17
+ }
18
+
19
+ return group(
20
+ concat([group(concat(parts)), " =", indent(concat([line, right]))])
21
+ );
22
+ }
23
+
24
+ function printMLHS(path, opts, print) {
25
+ return path.map(print, "body");
26
+ }
27
+
28
+ function printMLHSAddPost(path, opts, print) {
29
+ return path.call(print, "body", 0).concat(path.call(print, "body", 1));
30
+ }
31
+
32
+ function printMLHSAddStar(path, opts, print) {
33
+ const rightParts = ["*"];
34
+
35
+ if (path.getValue().body[1]) {
36
+ rightParts.push(path.call(print, "body", 1));
37
+ }
38
+
39
+ return path.call(print, "body", 0).concat(concat(rightParts));
40
+ }
41
+
42
+ function printMLHSParen(path, opts, print) {
43
+ if (["massign", "mlhs_paren"].includes(path.getParentNode().type)) {
44
+ // If we're nested in brackets as part of the left hand side of an
45
+ // assignment, i.e., (a, b, c) = 1, 2, 3
46
+ // ignore the current node and just go straight to the content
47
+ return path.call(print, "body", 0);
68
48
  }
49
+
50
+ const parts = [
51
+ softline,
52
+ join(concat([",", line]), path.call(print, "body", 0))
53
+ ];
54
+
55
+ if (path.getValue().body[0].comma) {
56
+ parts.push(",");
57
+ }
58
+
59
+ return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
60
+ }
61
+
62
+ function printMRHS(path, opts, print) {
63
+ return path.map(print, "body");
64
+ }
65
+
66
+ function printMRHSAddStar(path, opts, print) {
67
+ const [leftDoc, rightDoc] = path.map(print, "body");
68
+
69
+ return leftDoc.concat([concat(["*", rightDoc])]);
70
+ }
71
+
72
+ function printMRHSNewFromArgs(path, opts, print) {
73
+ const parts = path.call(print, "body", 0);
74
+
75
+ if (path.getValue().body[1]) {
76
+ parts.push(path.call(print, "body", 1));
77
+ }
78
+
79
+ return parts;
80
+ }
81
+
82
+ module.exports = {
83
+ massign: printMAssign,
84
+ mlhs: printMLHS,
85
+ mlhs_add_post: printMLHSAddPost,
86
+ mlhs_add_star: printMLHSAddStar,
87
+ mlhs_paren: printMLHSParen,
88
+ mrhs: printMRHS,
89
+ mrhs_add_star: printMRHSAddStar,
90
+ mrhs_new_from_args: printMRHSNewFromArgs
69
91
  };