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
@@ -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
  };