prettier 0.20.0 → 1.0.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
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
  };