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
@@ -1,39 +1,39 @@
1
1
  const { concat, group, indent, join, line } = require("../prettier");
2
- const { concatBody, first, skipAssignIndent } = require("../utils");
2
+ const { first, skipAssignIndent } = require("../utils");
3
3
 
4
- module.exports = {
5
- assign: (path, opts, print) => {
6
- const [printedTarget, printedValue] = path.map(print, "body");
7
- let adjustedValue = printedValue;
4
+ function printAssign(path, opts, print) {
5
+ const [_targetNode, valueNode] = path.getValue().body;
6
+ const [targetDoc, valueDoc] = path.map(print, "body");
7
+
8
+ let rightSideDoc = valueDoc;
9
+
10
+ // If the right side of this assignment is a multiple assignment, then we need
11
+ // to join it together with commas.
12
+ if (["mrhs_add_star", "mrhs_new_from_args"].includes(valueNode.type)) {
13
+ rightSideDoc = group(join(concat([",", line]), valueDoc));
14
+ }
8
15
 
9
- if (
10
- ["mrhs_add_star", "mrhs_new_from_args"].includes(
11
- path.getValue().body[1].type
12
- )
13
- ) {
14
- adjustedValue = group(join(concat([",", line]), printedValue));
15
- }
16
+ if (skipAssignIndent(valueNode)) {
17
+ return group(concat([targetDoc, " = ", rightSideDoc]));
18
+ }
16
19
 
17
- if (skipAssignIndent(path.getValue().body[1])) {
18
- return group(concat([printedTarget, " = ", adjustedValue]));
19
- }
20
+ return group(concat([targetDoc, " =", indent(concat([line, rightSideDoc]))]));
21
+ }
20
22
 
21
- return group(
22
- concat([printedTarget, " =", indent(concat([line, adjustedValue]))])
23
- );
24
- },
25
- assign_error: (_path, _opts, _print) => {
26
- throw new Error("Can't set variable");
27
- },
28
- opassign: (path, opts, print) =>
29
- group(
30
- concat([
31
- path.call(print, "body", 0),
32
- " ",
33
- path.call(print, "body", 1),
34
- indent(concat([line, path.call(print, "body", 2)]))
35
- ])
36
- ),
37
- var_field: concatBody,
23
+ function printOpAssign(path, opts, print) {
24
+ return group(
25
+ concat([
26
+ path.call(print, "body", 0),
27
+ " ",
28
+ path.call(print, "body", 1),
29
+ indent(concat([line, path.call(print, "body", 2)]))
30
+ ])
31
+ );
32
+ }
33
+
34
+ module.exports = {
35
+ assign: printAssign,
36
+ opassign: printOpAssign,
37
+ var_field: first,
38
38
  var_ref: first
39
39
  };
@@ -16,7 +16,11 @@ const printBlock = (braces) => (path, opts, print) => {
16
16
  statements.type === "stmts" ? statements.body : statements.body[0].body;
17
17
 
18
18
  let doBlockBody = "";
19
- if (stmts.length !== 1 || stmts[0].type !== "void_stmt") {
19
+ if (
20
+ stmts.length !== 1 ||
21
+ stmts[0].type !== "void_stmt" ||
22
+ stmts[0].comments
23
+ ) {
20
24
  doBlockBody = indent(concat([softline, path.call(print, "body", 1)]));
21
25
  }
22
26
 
@@ -37,8 +41,9 @@ const printBlock = (braces) => (path, opts, print) => {
37
41
  // We can hit this next pattern if within the block the only statement is a
38
42
  // comment.
39
43
  if (
40
- stmts.length > 1 &&
41
- stmts.filter((stmt) => stmt.type !== "@comment").length === 1
44
+ stmts.length === 1 &&
45
+ stmts[0].type === "void_stmt" &&
46
+ stmts[0].comments
42
47
  ) {
43
48
  return concat([breakParent, doBlock]);
44
49
  }
@@ -1,84 +1,117 @@
1
- const { concat, group, indent, literalline, softline } = require("../prettier");
1
+ const {
2
+ concat,
3
+ group,
4
+ hardline,
5
+ ifBreak,
6
+ indent,
7
+ softline
8
+ } = require("../prettier");
9
+ const { first, makeCall, noIndent } = require("../utils");
10
+
2
11
  const toProc = require("../toProc");
3
- const { concatBody, first, makeCall } = require("../utils");
4
12
 
5
- const noIndent = ["array", "hash", "if", "method_add_block", "xstring_literal"];
13
+ const chained = ["call", "method_add_arg", "method_add_block"];
14
+
15
+ function printCall(path, opts, print) {
16
+ const node = path.getValue();
17
+ const [receiverNode, _operatorNode, messageNode] = node.body;
18
+
19
+ const receiverDoc = path.call(print, "body", 0);
20
+ const operatorDoc = makeCall(path, opts, print);
21
+
22
+ // You can call lambdas with a special syntax that looks like func.(*args).
23
+ // In this case, "call" is returned for the 3rd child node. We don't alter
24
+ // call syntax so if `call` is implicit, we don't print it out.
25
+ const messageDoc = messageNode === "call" ? "" : path.call(print, "body", 2);
26
+
27
+ // The right side of the call node, as in everything including the operator
28
+ // and beyond.
29
+ const rightSideDoc = concat([
30
+ receiverNode.comments ? hardline : softline,
31
+ operatorDoc,
32
+ messageDoc
33
+ ]);
6
34
 
7
- const getHeredoc = (path, print, node) => {
8
- if (node.type === "heredoc") {
9
- const { beging, ending } = node;
10
- return { beging, ending, content: ["body", 0, "body"] };
35
+ // Get a reference to the parent node so we can check if we're inside a chain
36
+ const parentNode = path.getParentNode();
37
+
38
+ // If our parent node is a chained node then we're not going to group the
39
+ // right side of the expression, as we want to have a nice multi-line layout.
40
+ if (chained.includes(parentNode.type)) {
41
+ parentNode.chain = (node.chain || 0) + 1;
42
+ parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc);
11
43
  }
12
44
 
13
- if (node.type === "string_literal" && node.body[0].type === "heredoc") {
14
- const { beging, ending } = node.body[0];
15
- return { beging, ending, content: ["body", 0, "body", 0, "body"] };
45
+ // If we're at the top of a chain, then we're going to print out a nice
46
+ // multi-line layout if this doesn't break into multiple lines.
47
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
48
+ return ifBreak(
49
+ group(indent(concat(node.breakDoc.concat(rightSideDoc)))),
50
+ concat([receiverDoc, group(rightSideDoc)])
51
+ );
16
52
  }
17
53
 
18
- return null;
19
- };
54
+ // For certain left sides of the call nodes, we want to attach directly to
55
+ // the } or end.
56
+ if (noIndent.includes(receiverNode.type)) {
57
+ return concat([receiverDoc, operatorDoc, messageDoc]);
58
+ }
20
59
 
21
- module.exports = {
22
- call: (path, opts, print) => {
23
- const [receiverNode, _operatorNode, messageNode] = path.getValue().body;
24
-
25
- const printedReceiver = path.call(print, "body", 0);
26
- const printedOperator = makeCall(path, opts, print);
27
-
28
- // You can call lambdas with a special syntax that looks like func.(*args).
29
- // In this case, "call" is returned for the 3rd child node.
30
- const printedMessage =
31
- messageNode === "call" ? messageNode : path.call(print, "body", 2);
32
-
33
- // If we have a heredoc as a receiver, then we need to move the operator and
34
- // the message up to start of the heredoc declaration, as in:
35
- //
36
- // <<~TEXT.strip
37
- // content
38
- // TEXT
39
- const heredoc = getHeredoc(path, print, receiverNode);
40
- if (heredoc) {
41
- return concat([
42
- heredoc.beging,
43
- printedOperator,
44
- printedMessage,
45
- literalline,
46
- concat(path.map.apply(path, [print].concat(heredoc.content))),
47
- heredoc.ending
48
- ]);
49
- }
60
+ return group(concat([receiverDoc, group(indent(rightSideDoc))]));
61
+ }
50
62
 
51
- // For certain left sides of the call nodes, we want to attach directly to
52
- // the } or end.
53
- if (noIndent.includes(receiverNode.type)) {
54
- return concat([printedReceiver, printedOperator, printedMessage]);
55
- }
63
+ function printMethodAddArg(path, opts, print) {
64
+ const node = path.getValue();
65
+ const argNode = node.body[1];
66
+
67
+ const [methodDoc, argsDoc] = path.map(print, "body");
68
+
69
+ // You can end up here if you have a method with a ? ending, presumably
70
+ // because the parser knows that it cannot be a local variable.
71
+ if (argsDoc.length === 0) {
72
+ return methodDoc;
73
+ }
74
+
75
+ // This case will ONLY be hit if we can successfully turn the block into a
76
+ // to_proc call. In that case, we just explicitly add the parens around it.
77
+ if (argNode.type === "args" && argsDoc.length > 0) {
78
+ return concat([methodDoc, "("].concat(argsDoc).concat(")"));
79
+ }
56
80
 
57
- return group(
58
- concat([
59
- printedReceiver,
60
- group(indent(concat([softline, printedOperator, printedMessage])))
61
- ])
81
+ // Get a reference to the parent node so we can check if we're inside a chain
82
+ const parentNode = path.getParentNode();
83
+
84
+ // If our parent node is a chained node then we're not going to group the
85
+ // right side of the expression, as we want to have a nice multi-line layout.
86
+ if (chained.includes(parentNode.type)) {
87
+ parentNode.chain = (node.chain || 0) + 1;
88
+ parentNode.breakDoc = (node.breakDoc || [methodDoc]).concat(argsDoc);
89
+ }
90
+
91
+ // If we're at the top of a chain, then we're going to print out a nice
92
+ // multi-line layout if this doesn't break into multiple lines.
93
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
94
+ return ifBreak(
95
+ group(indent(concat(node.breakDoc.concat(argsDoc)))),
96
+ concat([methodDoc, argsDoc])
62
97
  );
63
- },
64
- fcall: concatBody,
65
- method_add_arg: (path, opts, print) => {
66
- const [method, args] = path.map(print, "body");
67
- const argNode = path.getValue().body[1];
68
-
69
- // This case will ONLY be hit if we can successfully turn the block into a
70
- // to_proc call. In that case, we just explicitly add the parens around it.
71
- if (argNode.type === "args" && args.length > 0) {
72
- return concat([method, "("].concat(args).concat(")"));
73
- }
98
+ }
74
99
 
75
- return concat([method, args]);
76
- },
77
- method_add_block: (path, opts, print) => {
78
- const [method, block] = path.getValue().body;
79
- const proc = toProc(path, opts, block);
100
+ return concat([methodDoc, argsDoc]);
101
+ }
80
102
 
81
- if (proc && method.type === "call") {
103
+ function printMethodAddBlock(path, { rubyToProc }, print) {
104
+ const node = path.getValue();
105
+
106
+ const [callNode, blockNode] = node.body;
107
+ const [callDoc, blockDoc] = path.map(print, "body");
108
+
109
+ // Don't bother trying to do any kind of fancy toProc transform if the option
110
+ // is disabled.
111
+ if (rubyToProc) {
112
+ const proc = toProc(path, blockNode);
113
+
114
+ if (proc && callNode.type === "call") {
82
115
  return group(
83
116
  concat([
84
117
  path.call(print, "body", 0),
@@ -92,8 +125,34 @@ module.exports = {
92
125
  if (proc) {
93
126
  return path.call(print, "body", 0);
94
127
  }
128
+ }
95
129
 
96
- return concat(path.map(print, "body"));
97
- },
130
+ // Get a reference to the parent node so we can check if we're inside a chain
131
+ const parentNode = path.getParentNode();
132
+
133
+ // If our parent node is a chained node then we're not going to group the
134
+ // right side of the expression, as we want to have a nice multi-line layout.
135
+ if (chained.includes(parentNode.type)) {
136
+ parentNode.chain = (node.chain || 0) + 1;
137
+ parentNode.breakDoc = (node.breakDoc || [callDoc]).concat(blockDoc);
138
+ }
139
+
140
+ // If we're at the top of a chain, then we're going to print out a nice
141
+ // multi-line layout if this doesn't break into multiple lines.
142
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
143
+ return ifBreak(
144
+ group(indent(concat(node.breakDoc.concat(blockDoc)))),
145
+ concat([callDoc, blockDoc])
146
+ );
147
+ }
148
+
149
+ return concat([callDoc, blockDoc]);
150
+ }
151
+
152
+ module.exports = {
153
+ call: printCall,
154
+ fcall: first,
155
+ method_add_arg: printMethodAddArg,
156
+ method_add_block: printMethodAddBlock,
98
157
  vcall: first
99
158
  };
@@ -28,13 +28,17 @@ module.exports = {
28
28
  // The `fill` builder command expects an array of docs alternating with
29
29
  // line breaks. This is so it can loop through and determine where to break.
30
30
  const preds = fill(
31
- path
32
- .call(print, "body", 0)
33
- .reduce(
34
- (accum, pred, index) =>
35
- index === 0 ? [pred] : accum.concat([",", line, pred]),
36
- null
37
- )
31
+ path.call(print, "body", 0).reduce((accum, pred, index) => {
32
+ if (index === 0) {
33
+ return [pred];
34
+ }
35
+
36
+ // Pull off the last element and make it concat with a comma so that
37
+ // we can maintain alternating lines and docs.
38
+ return accum
39
+ .slice(0, -1)
40
+ .concat([concat([accum[accum.length - 1], ","]), line, pred]);
41
+ }, null)
38
42
  );
39
43
 
40
44
  const stmts = path.call(print, "body", 1);
@@ -0,0 +1,74 @@
1
+ const {
2
+ concat,
3
+ group,
4
+ hardline,
5
+ ifBreak,
6
+ indent,
7
+ line
8
+ } = require("../prettier");
9
+
10
+ function printClass(path, opts, print) {
11
+ const [_constant, superclass, bodystmt] = path.getValue().body;
12
+ const stmts = bodystmt.body[0];
13
+
14
+ const parts = ["class ", path.call(print, "body", 0)];
15
+ if (superclass) {
16
+ parts.push(" < ", path.call(print, "body", 1));
17
+ }
18
+
19
+ // If the body is empty and does not contain any comments, we can just
20
+ // replace the body with a semi-colon.
21
+ if (
22
+ stmts.body.length === 1 &&
23
+ stmts.body[0].type === "void_stmt" &&
24
+ !stmts.body[0].comments
25
+ ) {
26
+ return group(concat([concat(parts), ifBreak(line, "; "), "end"]));
27
+ }
28
+
29
+ return group(
30
+ concat([
31
+ concat(parts),
32
+ indent(concat([hardline, path.call(print, "body", 2)])),
33
+ concat([hardline, "end"])
34
+ ])
35
+ );
36
+ }
37
+
38
+ function printModule(path, opts, print) {
39
+ const declaration = group(concat(["module ", path.call(print, "body", 0)]));
40
+
41
+ // If the body is empty, we can replace with a ;
42
+ const stmts = path.getValue().body[1].body[0];
43
+ if (
44
+ stmts.body.length === 1 &&
45
+ stmts.body[0].type === "void_stmt" &&
46
+ !stmts.body[0].comments
47
+ ) {
48
+ return group(concat([declaration, ifBreak(line, "; "), "end"]));
49
+ }
50
+
51
+ return group(
52
+ concat([
53
+ declaration,
54
+ indent(concat([hardline, path.call(print, "body", 1)])),
55
+ concat([hardline, "end"])
56
+ ])
57
+ );
58
+ }
59
+
60
+ function printSClass(path, opts, print) {
61
+ return group(
62
+ concat([
63
+ concat(["class << ", path.call(print, "body", 0)]),
64
+ indent(concat([hardline, path.call(print, "body", 1)])),
65
+ concat([hardline, "end"])
66
+ ])
67
+ );
68
+ }
69
+
70
+ module.exports = {
71
+ class: printClass,
72
+ module: printModule,
73
+ sclass: printSClass
74
+ };
@@ -1,5 +1,14 @@
1
- const { align, concat, group, ifBreak, join, line } = require("../prettier");
2
- const { docLength, makeArgs, makeCall } = require("../utils");
1
+ const {
2
+ align,
3
+ concat,
4
+ group,
5
+ ifBreak,
6
+ indent,
7
+ join,
8
+ line,
9
+ softline
10
+ } = require("../prettier");
11
+ const { docLength, makeCall } = require("../utils");
3
12
 
4
13
  const hasDef = (node) =>
5
14
  node.body[1].type === "args_add_block" &&
@@ -23,32 +32,39 @@ const hasDef = (node) =>
23
32
  const skipArgsAlign = (path) =>
24
33
  ["to", "not_to"].includes(path.getValue().body[2].body);
25
34
 
35
+ // If there is a ternary argument to a command and it's going to get broken
36
+ // into multiple lines, then we're going to have to use parentheses around the
37
+ // command in order to make sure operator precedence doesn't get messed up.
38
+ const hasTernaryArg = (path) =>
39
+ path.getValue().body[1].body[0].body.some((node) => node.type === "ifop");
40
+
26
41
  module.exports = {
27
42
  command: (path, opts, print) => {
28
43
  const command = path.call(print, "body", 0);
29
- const { args, heredocs } = makeArgs(path, opts, print, 1);
44
+ const joinedArgs = join(concat([",", line]), path.call(print, "body", 1));
30
45
 
31
- if (heredocs.length > 1) {
32
- return concat([command, " ", join(", ", args)].concat(heredocs));
33
- }
46
+ const hasTernary = hasTernaryArg(path);
47
+ let breakArgs;
34
48
 
35
- const joinedArgs = join(concat([",", line]), args);
36
- const breakArgs = hasDef(path.getValue())
37
- ? joinedArgs
38
- : align(command.length + 1, joinedArgs);
49
+ if (hasTernary) {
50
+ breakArgs = indent(concat([softline, joinedArgs]));
51
+ } else if (hasDef(path.getValue())) {
52
+ breakArgs = joinedArgs;
53
+ } else {
54
+ breakArgs = align(command.length + 1, joinedArgs);
55
+ }
39
56
 
40
- const commandDoc = group(
57
+ return group(
41
58
  ifBreak(
42
- concat([command, " ", breakArgs]),
59
+ concat([
60
+ command,
61
+ hasTernary ? "(" : " ",
62
+ breakArgs,
63
+ hasTernary ? concat([softline, ")"]) : ""
64
+ ]),
43
65
  concat([command, " ", joinedArgs])
44
66
  )
45
67
  );
46
-
47
- if (heredocs.length === 1) {
48
- return group(concat([commandDoc].concat(heredocs)));
49
- }
50
-
51
- return commandDoc;
52
68
  },
53
69
  command_call: (path, opts, print) => {
54
70
  const parts = [
@@ -62,25 +78,14 @@ module.exports = {
62
78
  }
63
79
 
64
80
  parts.push(" ");
65
- const { args, heredocs } = makeArgs(path, opts, print, 3);
66
-
67
- if (heredocs.length > 1) {
68
- return concat(parts.concat([join(", ", args)]).concat(heredocs));
69
- }
70
81
 
71
- const joinedArgs = join(concat([",", line]), args);
82
+ const joinedArgs = join(concat([",", line]), path.call(print, "body", 3));
72
83
  const breakArgs = skipArgsAlign(path)
73
84
  ? joinedArgs
74
85
  : align(docLength(concat(parts)), joinedArgs);
75
86
 
76
- const commandDoc = group(
87
+ return group(
77
88
  ifBreak(concat(parts.concat(breakArgs)), concat(parts.concat(joinedArgs)))
78
89
  );
79
-
80
- if (heredocs.length === 1) {
81
- return group(concat([commandDoc].concat(heredocs)));
82
- }
83
-
84
- return commandDoc;
85
90
  }
86
91
  };