prettier 1.4.0 → 1.5.4

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.
@@ -8,9 +8,26 @@ const {
8
8
  softline
9
9
  } = require("../../prettier");
10
10
  const { getTrailingComma } = require("../../utils");
11
-
12
11
  const toProc = require("../toProc");
13
12
 
13
+ const noTrailingComma = ["command", "command_call"];
14
+
15
+ function getArgParenTrailingComma(node) {
16
+ // If we have a block, then we don't want to add a trailing comma.
17
+ if (node.type === "args_add_block" && node.body[1]) {
18
+ return "";
19
+ }
20
+
21
+ // If we only have one argument and that first argument necessitates that we
22
+ // skip putting a comma (because it would interfere with parsing the argument)
23
+ // then we don't want to add a trailing comma.
24
+ if (node.body.length === 1 && noTrailingComma.includes(node.body[0].type)) {
25
+ return "";
26
+ }
27
+
28
+ return ifBreak(",", "");
29
+ }
30
+
14
31
  function printArgParen(path, opts, print) {
15
32
  const argsNode = path.getValue().body[0];
16
33
 
@@ -32,9 +49,6 @@ function printArgParen(path, opts, print) {
32
49
  );
33
50
  }
34
51
 
35
- const args = path.call(print, "body", 0);
36
- const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
37
-
38
52
  // Now here we return a doc that represents the whole grouped expression,
39
53
  // including the surrouding parentheses.
40
54
  return group(
@@ -43,8 +57,8 @@ function printArgParen(path, opts, print) {
43
57
  indent(
44
58
  concat([
45
59
  softline,
46
- join(concat([",", line]), args),
47
- getTrailingComma(opts) && !hasBlock ? ifBreak(",", "") : ""
60
+ join(concat([",", line]), path.call(print, "body", 0)),
61
+ getTrailingComma(opts) && getArgParenTrailingComma(argsNode)
48
62
  ])
49
63
  ),
50
64
  softline,
@@ -92,25 +106,73 @@ function printArgs(path, { rubyToProc }, print) {
92
106
  return args;
93
107
  }
94
108
 
109
+ function printArgsAddBlock(path, opts, print) {
110
+ const node = path.getValue();
111
+ const parts = path.call(print, "body", 0);
112
+
113
+ if (node.body[1]) {
114
+ let blockDoc = path.call(print, "body", 1);
115
+
116
+ if (node.body[1].comments) {
117
+ // If we have a method call like:
118
+ //
119
+ // foo(
120
+ // # comment
121
+ // &block
122
+ // )
123
+ //
124
+ // then we need to make sure we don't accidentally prepend the operator
125
+ // before the comment.
126
+ blockDoc.parts[2] = concat(["&", blockDoc.parts[2]]);
127
+ } else {
128
+ // If we don't have any comments, we can just prepend the operator
129
+ blockDoc = concat(["&", blockDoc]);
130
+ }
131
+
132
+ parts.push(blockDoc);
133
+ }
134
+
135
+ return parts;
136
+ }
137
+
138
+ function printArgsAddStar(path, opts, print) {
139
+ const node = path.getValue();
140
+ const docs = path.map(print, "body");
141
+
142
+ if (node.body[1].comments) {
143
+ // If we have an array like:
144
+ //
145
+ // [
146
+ // # comment
147
+ // *values
148
+ // ]
149
+ //
150
+ // or if we have an array like:
151
+ //
152
+ // [
153
+ // *values # comment
154
+ // ]
155
+ //
156
+ // then we need to make sure we don't accidentally prepend the operator
157
+ // before the comment.
158
+ const index = node.body[1].comments.filter(({ leading }) => leading).length;
159
+ docs[1].parts[index] = concat(["*", docs[1].parts[index]]);
160
+ } else {
161
+ // If we don't have any comments, we can just prepend the operator
162
+ docs[1] = concat(["*", docs[1]]);
163
+ }
164
+
165
+ return docs[0].concat(docs[1]).concat(docs.slice(2));
166
+ }
167
+
168
+ function printBlockArg(path, opts, print) {
169
+ return concat(["&", path.call(print, "body", 0)]);
170
+ }
171
+
95
172
  module.exports = {
96
173
  arg_paren: printArgParen,
97
174
  args: printArgs,
98
- args_add_block: (path, opts, print) => {
99
- const parts = path.call(print, "body", 0);
100
-
101
- if (path.getValue().body[1]) {
102
- parts.push(concat(["&", path.call(print, "body", 1)]));
103
- }
104
-
105
- return parts;
106
- },
107
- args_add_star: (path, opts, print) => {
108
- const printed = path.map(print, "body");
109
- const parts = printed[0]
110
- .concat([concat(["*", printed[1]])])
111
- .concat(printed.slice(2));
112
-
113
- return parts;
114
- },
115
- blockarg: (path, opts, print) => concat(["&", path.call(print, "body", 0)])
175
+ args_add_block: printArgsAddBlock,
176
+ args_add_star: printArgsAddStar,
177
+ blockarg: printBlockArg
116
178
  };
@@ -63,33 +63,30 @@ function isSymbolArray(args) {
63
63
  // Prints out a word that is a part of a special array literal that accepts
64
64
  // interpolation. The body is an array of either plain strings or interpolated
65
65
  // expressions.
66
- function printSpecialArrayWord(path, opts, print) {
66
+ function printArrayLiteralWord(path, opts, print) {
67
67
  return concat(path.map(print, "body"));
68
68
  }
69
69
 
70
70
  // Prints out a special array literal. Accepts the parts of the array literal as
71
71
  // an argument, where the first element of the parts array is a string that
72
72
  // contains the special start.
73
- function printSpecialArrayParts(parts) {
73
+ function printArrayLiteral(start, parts) {
74
74
  return group(
75
75
  concat([
76
- parts[0],
76
+ start,
77
77
  "[",
78
- indent(concat([softline, join(line, parts.slice(1))])),
78
+ indent(concat([softline, join(line, parts)])),
79
79
  concat([softline, "]"])
80
80
  ])
81
81
  );
82
82
  }
83
83
 
84
- // Generates a print function with an embedded special start character for the
85
- // specific type of array literal that we're dealing with. The print function
86
- // returns an array as it expects to eventually be handed off to
87
- // printSpecialArrayParts.
88
- function printSpecialArray(start) {
89
- return function printSpecialArrayWithStart(path, opts, print) {
90
- return [start].concat(path.map(print, "body"));
91
- };
92
- }
84
+ const arrayLiteralStarts = {
85
+ qsymbols: "%i",
86
+ qwords: "%w",
87
+ symbols: "%I",
88
+ words: "%W"
89
+ };
93
90
 
94
91
  // An array node is any literal array in Ruby. This includes all of the special
95
92
  // array literals as well as regular arrays. If it is a special array literal
@@ -105,30 +102,40 @@ function printArray(path, opts, print) {
105
102
  return printEmptyCollection(path, opts, "[", "]");
106
103
  }
107
104
 
108
- // If we have an array that contains only simple string literals with no
109
- // spaces or interpolation, then we're going to print a %w array.
110
- if (opts.rubyArrayLiteral && isStringArray(args)) {
111
- const printString = (stringPath) => stringPath.call(print, "body", 0);
112
- const parts = path.map(printString, "body", 0, "body");
105
+ if (opts.rubyArrayLiteral) {
106
+ // If we have an array that contains only simple string literals with no
107
+ // spaces or interpolation, then we're going to print a %w array.
108
+ if (isStringArray(args)) {
109
+ const printString = (stringPath) => stringPath.call(print, "body", 0);
110
+ const parts = path.map(printString, "body", 0, "body");
113
111
 
114
- return printSpecialArrayParts(["%w"].concat(parts));
115
- }
112
+ return printArrayLiteral("%w", parts);
113
+ }
116
114
 
117
- // If we have an array that contains only simple symbol literals with no
118
- // interpolation, then we're going to print a %i array.
119
- if (opts.rubyArrayLiteral && isSymbolArray(args)) {
120
- const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
121
- const parts = path.map(printSymbol, "body", 0, "body");
115
+ // If we have an array that contains only simple symbol literals with no
116
+ // interpolation, then we're going to print a %i array.
117
+ if (isSymbolArray(args)) {
118
+ const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
119
+ const parts = path.map(printSymbol, "body", 0, "body");
122
120
 
123
- return printSpecialArrayParts(["%i"].concat(parts));
121
+ return printArrayLiteral("%i", parts);
122
+ }
124
123
  }
125
124
 
126
125
  // If we don't have a regular args node at this point then we have a special
127
126
  // array literal. In that case we're going to print out the body (which will
128
127
  // return to us an array with the first one being the start of the array) and
129
- // send that over to the printSpecialArrayParts function.
128
+ // send that over to the printArrayLiteral function.
130
129
  if (!["args", "args_add_star"].includes(args.type)) {
131
- return printSpecialArrayParts(path.call(print, "body", 0));
130
+ return path.call(
131
+ (arrayPath) =>
132
+ printArrayLiteral(
133
+ arrayLiteralStarts[arrayPath.getValue().type],
134
+ arrayPath.map(print, "body")
135
+ ),
136
+ "body",
137
+ 0
138
+ );
132
139
  }
133
140
 
134
141
  // Here we have a normal array of any type of object with no special literal
@@ -151,9 +158,5 @@ function printArray(path, opts, print) {
151
158
 
152
159
  module.exports = {
153
160
  array: printArray,
154
- qsymbols: printSpecialArray("%i"),
155
- qwords: printSpecialArray("%w"),
156
- symbols: printSpecialArray("%I"),
157
- word: printSpecialArrayWord,
158
- words: printSpecialArray("%W")
161
+ word: printArrayLiteralWord
159
162
  };
@@ -1,34 +1,22 @@
1
- const {
2
- concat,
3
- group,
4
- hardline,
5
- ifBreak,
6
- indent,
7
- line
8
- } = require("../../prettier");
1
+ const { concat, group, hardline, indent } = require("../../prettier");
2
+ const { isEmptyBodyStmt } = require("../../utils");
9
3
 
10
4
  function printClass(path, opts, print) {
11
5
  const [_constant, superclass, bodystmt] = path.getValue().body;
12
- const stmts = bodystmt.body[0];
13
6
 
14
7
  const parts = ["class ", path.call(print, "body", 0)];
15
8
  if (superclass) {
16
9
  parts.push(" < ", path.call(print, "body", 1));
17
10
  }
18
11
 
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"]));
12
+ const declaration = group(concat(parts));
13
+ if (isEmptyBodyStmt(bodystmt)) {
14
+ return group(concat([declaration, hardline, "end"]));
27
15
  }
28
16
 
29
17
  return group(
30
18
  concat([
31
- concat(parts),
19
+ declaration,
32
20
  indent(concat([hardline, path.call(print, "body", 2)])),
33
21
  concat([hardline, "end"])
34
22
  ])
@@ -36,16 +24,11 @@ function printClass(path, opts, print) {
36
24
  }
37
25
 
38
26
  function printModule(path, opts, print) {
27
+ const node = path.getValue();
39
28
  const declaration = group(concat(["module ", path.call(print, "body", 0)]));
40
29
 
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"]));
30
+ if (isEmptyBodyStmt(node.body[1])) {
31
+ return group(concat([declaration, hardline, "end"]));
49
32
  }
50
33
 
51
34
  return group(
@@ -58,9 +41,16 @@ function printModule(path, opts, print) {
58
41
  }
59
42
 
60
43
  function printSClass(path, opts, print) {
44
+ const bodystmt = path.getValue().body[1];
45
+ const declaration = concat(["class << ", path.call(print, "body", 0)]);
46
+
47
+ if (isEmptyBodyStmt(bodystmt)) {
48
+ return group(concat([declaration, hardline, "end"]));
49
+ }
50
+
61
51
  return group(
62
52
  concat([
63
- concat(["class << ", path.call(print, "body", 0)]),
53
+ declaration,
64
54
  indent(concat([hardline, path.call(print, "body", 1)])),
65
55
  concat([hardline, "end"])
66
56
  ])
@@ -73,7 +73,7 @@ function printCommand(path, opts, print) {
73
73
  } else if (hasDef(path.getValue())) {
74
74
  breakArgs = joinedArgs;
75
75
  } else {
76
- breakArgs = align(command.length + 1, joinedArgs);
76
+ breakArgs = align(docLength(command) + 1, joinedArgs);
77
77
  }
78
78
 
79
79
  return group(
@@ -219,7 +219,7 @@ const printConditional = (keyword) => (path, { rubyModifier }, print) => {
219
219
 
220
220
  // If the body of the conditional is empty, then we explicitly have to use the
221
221
  // block form.
222
- if (isEmptyStmts(statements) && !statements.body[0].comments) {
222
+ if (isEmptyStmts(statements)) {
223
223
  return concat([
224
224
  `${keyword} `,
225
225
  align(keyword.length + 1, path.call(print, "body", 0)),
@@ -6,6 +6,7 @@ const {
6
6
  join,
7
7
  line
8
8
  } = require("../../prettier");
9
+
9
10
  const {
10
11
  getTrailingComma,
11
12
  printEmptyCollection,
@@ -89,6 +90,13 @@ function printAssocNew(path, opts, print) {
89
90
  const parts = [path.call((keyPath) => keyPrinter(keyPath, print), "body", 0)];
90
91
  const valueDoc = path.call(print, "body", 1);
91
92
 
93
+ // If we're printing a child hash then we want it to break along with its
94
+ // parent hash, so we don't group the parts.
95
+ if (valueNode.type === "hash") {
96
+ parts.push(" ", valueDoc);
97
+ return concat(parts);
98
+ }
99
+
92
100
  if (!skipAssignIndent(valueNode) || keyNode.comments) {
93
101
  parts.push(indent(concat([line, valueDoc])));
94
102
  } else {
@@ -125,20 +133,26 @@ function printHash(path, opts, print) {
125
133
  return printEmptyCollection(path, opts, "{", "}");
126
134
  }
127
135
 
128
- return group(
129
- concat([
130
- "{",
131
- indent(
132
- concat([
133
- line,
134
- path.call(print, "body", 0),
135
- getTrailingComma(opts) ? ifBreak(",", "") : ""
136
- ])
137
- ),
138
- line,
139
- "}"
140
- ])
141
- );
136
+ let hashDoc = concat([
137
+ "{",
138
+ indent(
139
+ concat([
140
+ line,
141
+ path.call(print, "body", 0),
142
+ getTrailingComma(opts) ? ifBreak(",", "") : ""
143
+ ])
144
+ ),
145
+ line,
146
+ "}"
147
+ ]);
148
+
149
+ // If we're inside another hash, then we don't want to group our contents
150
+ // because we want this hash to break along with its parent hash.
151
+ if (path.getParentNode().type === "assoc_new") {
152
+ return hashDoc;
153
+ }
154
+
155
+ return group(hashDoc);
142
156
  }
143
157
 
144
158
  module.exports = {
@@ -1,5 +1,5 @@
1
1
  const { concat, group, lineSuffix, join } = require("../../prettier");
2
- const { literalLineNoBreak } = require("../../utils");
2
+ const { literallineWithoutBreakParent } = require("../../utils");
3
3
 
4
4
  function printHeredoc(path, opts, print) {
5
5
  const { body, ending } = path.getValue();
@@ -11,7 +11,7 @@ function printHeredoc(path, opts, print) {
11
11
  }
12
12
 
13
13
  // In this case, the part of the string is just regular string content
14
- return join(literalLineNoBreak, part.body.split("\n"));
14
+ return join(literallineWithoutBreakParent, part.body.split("\n"));
15
15
  });
16
16
 
17
17
  // We use a literalline break because matching indentation is required
@@ -23,7 +23,9 @@ function printHeredoc(path, opts, print) {
23
23
  concat([
24
24
  path.call(print, "beging"),
25
25
  lineSuffix(
26
- group(concat([literalLineNoBreak].concat(parts).concat(ending)))
26
+ group(
27
+ concat([literallineWithoutBreakParent].concat(parts).concat(ending))
28
+ )
27
29
  )
28
30
  ])
29
31
  );
@@ -10,7 +10,7 @@ const {
10
10
  softline
11
11
  } = require("../../prettier");
12
12
 
13
- const { containsAssignment } = require("../../utils");
13
+ const { containsAssignment, isEmptyStmts } = require("../../utils");
14
14
  const inlineEnsureParens = require("../../utils/inlineEnsureParens");
15
15
 
16
16
  function printLoop(keyword, modifier) {
@@ -19,17 +19,11 @@ function printLoop(keyword, modifier) {
19
19
 
20
20
  // If the only statement inside this while loop is a void statement, then we
21
21
  // can shorten to just displaying the predicate and then a semicolon.
22
- if (
23
- stmts.body.length === 1 &&
24
- stmts.body[0].type === "void_stmt" &&
25
- !stmts.body[0].comments
26
- ) {
22
+ if (isEmptyStmts(stmts)) {
27
23
  return group(
28
24
  concat([
29
- keyword,
30
- " ",
31
- path.call(print, "body", 0),
32
- ifBreak(softline, "; "),
25
+ group(concat([keyword, " ", path.call(print, "body", 0)])),
26
+ hardline,
33
27
  "end"
34
28
  ])
35
29
  );