prettier 1.5.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +17 -1
  3. data/CONTRIBUTING.md +2 -2
  4. data/README.md +31 -12
  5. data/node_modules/prettier/bin-prettier.js +13702 -11629
  6. data/node_modules/prettier/index.js +19198 -16572
  7. data/node_modules/prettier/parser-angular.js +61 -40
  8. data/node_modules/prettier/parser-babel.js +22 -1
  9. data/node_modules/prettier/parser-espree.js +22 -1
  10. data/node_modules/prettier/parser-flow.js +22 -1
  11. data/node_modules/prettier/parser-glimmer.js +1 -1
  12. data/node_modules/prettier/parser-graphql.js +1 -1
  13. data/node_modules/prettier/parser-html.js +82 -63
  14. data/node_modules/prettier/parser-markdown.js +24 -9
  15. data/node_modules/prettier/parser-meriyah.js +22 -1
  16. data/node_modules/prettier/parser-postcss.js +22 -1
  17. data/node_modules/prettier/parser-typescript.js +22 -1
  18. data/node_modules/prettier/parser-yaml.js +2 -2
  19. data/node_modules/prettier/third-party.js +1042 -833
  20. data/package.json +3 -3
  21. data/rubocop.yml +9 -0
  22. data/src/haml/parser.js +5 -4
  23. data/src/haml/printer.js +428 -18
  24. data/src/parser/parseSync.js +8 -6
  25. data/src/plugin.js +1 -1
  26. data/src/rbs/parser.js +1 -3
  27. data/src/rbs/printer.js +35 -7
  28. data/src/ruby/nodes/args.js +66 -22
  29. data/src/ruby/nodes/calls.js +8 -1
  30. data/src/ruby/nodes/conditionals.js +47 -45
  31. data/src/ruby/nodes/hashes.js +5 -14
  32. data/src/ruby/nodes/params.js +2 -9
  33. data/src/ruby/nodes/strings.js +95 -2
  34. data/src/ruby/parser.js +1 -3
  35. data/src/ruby/parser.rb +52 -29
  36. data/src/ruby/printer.js +10 -1
  37. data/src/utils/inlineEnsureParens.js +1 -0
  38. data/src/utils/skipAssignIndent.js +8 -1
  39. metadata +3 -12
  40. data/src/haml/nodes/comment.js +0 -27
  41. data/src/haml/nodes/doctype.js +0 -34
  42. data/src/haml/nodes/filter.js +0 -16
  43. data/src/haml/nodes/hamlComment.js +0 -21
  44. data/src/haml/nodes/plain.js +0 -6
  45. data/src/haml/nodes/root.js +0 -8
  46. data/src/haml/nodes/script.js +0 -33
  47. data/src/haml/nodes/silentScript.js +0 -59
  48. data/src/haml/nodes/tag.js +0 -232
@@ -135,15 +135,17 @@ function parseSync(parser, source, opts) {
135
135
  // using unix sockets.
136
136
  if (
137
137
  stderr.includes("invalid option -- U") ||
138
+ stderr.includes("invalid option -- 'u'") ||
138
139
  stderr.includes("Protocol not supported")
139
140
  ) {
140
141
  throw new Error(`
141
- @prettier/plugin-ruby uses netcat to communicate over unix sockets between
142
- the node.js process running prettier and an underlying Ruby process used
143
- for parsing. Unfortunately the version of netcat that you have installed
144
- does not support unix sockets. To solve this either uninstall the version
145
- of netcat that you're using and use a different implementation, or change
146
- the value of the rubyNetcatCommand option in your prettier configuration.
142
+ @prettier/plugin-ruby uses unix sockets to communicate between the node.js
143
+ process running prettier and an underlying Ruby process used for parsing.
144
+ Unfortunately the command that it tried to use to do that
145
+ (${netcat.command}) does not support unix sockets. To solve this either
146
+ uninstall the version of ${netcat.command} that you're using and use a
147
+ different implementation, or change the value of the rubyNetcatCommand
148
+ option in your prettier configuration.
147
149
  `);
148
150
  }
149
151
 
data/src/plugin.js CHANGED
@@ -10,7 +10,7 @@ const hamlParser = require("./haml/parser");
10
10
  /*
11
11
  * metadata mostly pulled from linguist and rubocop:
12
12
  * https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
13
- * https://github.com/rubocop-hq/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
13
+ * https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
14
14
  */
15
15
 
16
16
  module.exports = {
data/src/rbs/parser.js CHANGED
@@ -8,12 +8,10 @@ function parse(text, _parsers, opts) {
8
8
  return parseSync("rbs", text, opts);
9
9
  }
10
10
 
11
- const pragmaPattern = /#\s*@(prettier|format)/;
12
-
13
11
  // This function handles checking whether or not the source string has the
14
12
  // pragma for prettier. This is an optional workflow for incremental adoption.
15
13
  function hasPragma(text) {
16
- return pragmaPattern.test(text);
14
+ return /^\s*#[^\S\n]*@(format|prettier)\s*(\n|$)/.test(text);
17
15
  }
18
16
 
19
17
  // This function is critical for comments and cursor support, and is responsible
data/src/rbs/printer.js CHANGED
@@ -147,7 +147,7 @@ function printNode(path, opts, print) {
147
147
 
148
148
  // This is the big function that prints out any individual type, which can
149
149
  // look like all kinds of things, listed in the case statement below.
150
- function printType(path, { forceUnionParens = false } = {}) {
150
+ function printType(path, { forceParens = false } = {}) {
151
151
  const node = path.getValue();
152
152
 
153
153
  switch (node.class) {
@@ -157,7 +157,13 @@ function printNode(path, opts, print) {
157
157
  }
158
158
  return node.literal;
159
159
  case "optional":
160
- return concat([path.call(printType, "type"), "?"]);
160
+ return concat([
161
+ path.call(
162
+ (typePath) => printType(typePath, { forceParens: true }),
163
+ "type"
164
+ ),
165
+ "?"
166
+ ]);
161
167
  case "tuple":
162
168
  // If we don't have any sub types, we explicitly need the space in between
163
169
  // the brackets to not confuse the parser.
@@ -173,14 +179,29 @@ function printNode(path, opts, print) {
173
179
  join(concat([line, "| "]), path.map(printType, "types"))
174
180
  );
175
181
 
176
- if (forceUnionParens || path.getParentNode().class === "intersection") {
182
+ if (forceParens) {
183
+ return concat(["(", doc, ")"]);
184
+ }
185
+
186
+ return doc;
187
+ }
188
+ case "intersection": {
189
+ const doc = group(
190
+ join(
191
+ concat([line, "& "]),
192
+ path.map(
193
+ (typePath) => printType(typePath, { forceParens: true }),
194
+ "types"
195
+ )
196
+ )
197
+ );
198
+
199
+ if (forceParens) {
177
200
  return concat(["(", doc, ")"]);
178
201
  }
179
202
 
180
203
  return doc;
181
204
  }
182
- case "intersection":
183
- return group(join(concat([line, "& "]), path.map(printType, "types")));
184
205
  case "class_singleton":
185
206
  return concat(["singleton(", node.name, ")"]);
186
207
  case "proc":
@@ -521,7 +542,7 @@ function printNode(path, opts, print) {
521
542
  parts.push(
522
543
  "-> ",
523
544
  path.call(
524
- (typePath) => printType(typePath, { forceUnionParens: true }),
545
+ (typePath) => printType(typePath, { forceParens: true }),
525
546
  "type",
526
547
  "return_type"
527
548
  )
@@ -609,7 +630,14 @@ function hasPrettierIgnore(path) {
609
630
  return node.comment && node.comment.string.includes("prettier-ignore");
610
631
  }
611
632
 
633
+ // This function handles adding the format pragma to a source string. This is an
634
+ // optional workflow for incremental adoption.
635
+ function insertPragma(text) {
636
+ return `# @format\n${text}`;
637
+ }
638
+
612
639
  module.exports = {
613
640
  print: printNode,
614
- hasPrettierIgnore
641
+ hasPrettierIgnore,
642
+ insertPragma
615
643
  };
@@ -108,12 +108,18 @@ function printArgs(path, { rubyToProc }, print) {
108
108
 
109
109
  function printArgsAddBlock(path, opts, print) {
110
110
  const node = path.getValue();
111
+ const blockNode = node.body[1];
112
+
111
113
  const parts = path.call(print, "body", 0);
112
114
 
113
- if (node.body[1]) {
115
+ if (blockNode) {
114
116
  let blockDoc = path.call(print, "body", 1);
115
117
 
116
- if (node.body[1].comments) {
118
+ if (!(blockNode.comments || []).some(({ leading }) => leading)) {
119
+ // If we don't have any leading comments, we can just prepend the
120
+ // operator.
121
+ blockDoc = concat(["&", blockDoc]);
122
+ } else if (Array.isArray(blockDoc[0])) {
117
123
  // If we have a method call like:
118
124
  //
119
125
  // foo(
@@ -123,10 +129,20 @@ function printArgsAddBlock(path, opts, print) {
123
129
  //
124
130
  // then we need to make sure we don't accidentally prepend the operator
125
131
  // before the comment.
126
- blockDoc.parts[2] = concat(["&", blockDoc.parts[2]]);
132
+ //
133
+ // In prettier >= 2.3.0, the comments are printed as an array before the
134
+ // content. I don't love this kind of reflection, but it's the simplest
135
+ // way at the moment to get this right.
136
+ blockDoc = blockDoc[0].concat(
137
+ concat(["&", blockDoc[1]]),
138
+ blockDoc.slice(2)
139
+ );
127
140
  } else {
128
- // If we don't have any comments, we can just prepend the operator
129
- blockDoc = concat(["&", blockDoc]);
141
+ // In prettier < 2.3.0, the comments are printed as part of a concat, so
142
+ // we can reflect on how many leading comments there are to determine
143
+ // which doc node we should modify.
144
+ const index = blockNode.comments.filter(({ leading }) => leading).length;
145
+ blockDoc.parts[index] = concat(["&", blockDoc.parts[index]]);
130
146
  }
131
147
 
132
148
  parts.push(blockDoc);
@@ -136,10 +152,34 @@ function printArgsAddBlock(path, opts, print) {
136
152
  }
137
153
 
138
154
  function printArgsAddStar(path, opts, print) {
139
- const node = path.getValue();
140
- const docs = path.map(print, "body");
155
+ let docs = [];
156
+
157
+ path.each((argPath, argIndex) => {
158
+ const doc = print(argPath);
159
+
160
+ // If it's the first child, then it's an array of args, so we're going to
161
+ // concat that onto the existing docs if there are any.
162
+ if (argIndex === 0) {
163
+ if (doc.length > 0) {
164
+ docs = docs.concat(doc);
165
+ }
166
+ return;
167
+ }
168
+
169
+ // If it's after the splat, then it's an individual arg, so we're just going
170
+ // to push it onto the array.
171
+ if (argIndex !== 1) {
172
+ docs.push(doc);
173
+ return;
174
+ }
175
+
176
+ // If we don't have any leading comments, we can just prepend the operator.
177
+ const argsNode = argPath.getValue();
178
+ if (!(argsNode.comments || []).some(({ leading }) => leading)) {
179
+ docs.push(concat(["*", doc]));
180
+ return;
181
+ }
141
182
 
142
- if (node.body[1].comments) {
143
183
  // If we have an array like:
144
184
  //
145
185
  // [
@@ -147,22 +187,26 @@ function printArgsAddStar(path, opts, print) {
147
187
  // *values
148
188
  // ]
149
189
  //
150
- // or if we have an array like:
151
- //
152
- // [
153
- // *values # comment
154
- // ]
155
- //
156
190
  // 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
- }
191
+ // before the comment(s).
192
+ //
193
+ // In prettier >= 2.3.0, the comments are printed as an array before the
194
+ // content. I don't love this kind of reflection, but it's the simplest way
195
+ // at the moment to get this right.
196
+ if (Array.isArray(doc[0])) {
197
+ docs.push(doc[0].concat(concat(["*", doc[1]]), doc.slice(2)));
198
+ return;
199
+ }
200
+
201
+ // In prettier < 2.3.0, the comments are printed as part of a concat, so
202
+ // we can reflect on how many leading comments there are to determine which
203
+ // doc node we should modify.
204
+ const index = argsNode.comments.filter(({ leading }) => leading).length;
205
+ doc.parts[index] = concat(["*", doc.parts[index]]);
206
+ docs = docs.concat(doc);
207
+ }, "body");
164
208
 
165
- return docs[0].concat(docs[1]).concat(docs.slice(2));
209
+ return docs;
166
210
  }
167
211
 
168
212
  function printBlockArg(path, opts, print) {
@@ -4,6 +4,7 @@ const {
4
4
  hardline,
5
5
  ifBreak,
6
6
  indent,
7
+ join,
7
8
  softline
8
9
  } = require("../../prettier");
9
10
  const { makeCall, noIndent } = require("../../utils");
@@ -133,7 +134,13 @@ function printMethodAddArg(path, opts, print) {
133
134
  );
134
135
  }
135
136
 
136
- return concat([methodDoc, argsDoc]);
137
+ // If there are already parentheses, then we can just use the doc that's
138
+ // already printed.
139
+ if (argNode.type == "arg_paren") {
140
+ return concat([methodDoc, argsDoc]);
141
+ }
142
+
143
+ return concat([methodDoc, " ", join(", ", argsDoc), " "]);
137
144
  }
138
145
 
139
146
  function printMethodAddBlock(path, opts, print) {
@@ -190,57 +190,59 @@ const canTernary = (path) => {
190
190
  };
191
191
 
192
192
  // A normalized print function for both `if` and `unless` nodes.
193
- const printConditional = (keyword) => (path, { rubyModifier }, print) => {
194
- if (canTernary(path)) {
195
- let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
196
- printTernaryClauses(
197
- keyword,
198
- path.call(print, "body", 1),
199
- path.call(print, "body", 2, "body", 0)
200
- )
201
- );
202
-
203
- if (["binary", "call"].includes(path.getParentNode().type)) {
204
- ternaryParts = ["("].concat(ternaryParts).concat(")");
193
+ const printConditional =
194
+ (keyword) =>
195
+ (path, { rubyModifier }, print) => {
196
+ if (canTernary(path)) {
197
+ let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
198
+ printTernaryClauses(
199
+ keyword,
200
+ path.call(print, "body", 1),
201
+ path.call(print, "body", 2, "body", 0)
202
+ )
203
+ );
204
+
205
+ if (["binary", "call"].includes(path.getParentNode().type)) {
206
+ ternaryParts = ["("].concat(ternaryParts).concat(")");
207
+ }
208
+
209
+ return group(
210
+ ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
211
+ );
205
212
  }
206
213
 
207
- return group(
208
- ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
209
- );
210
- }
211
-
212
- const [predicate, statements, addition] = path.getValue().body;
214
+ const [predicate, statements, addition] = path.getValue().body;
213
215
 
214
- // If there's an additional clause that wasn't matched earlier, we know we
215
- // can't go for the inline option.
216
- if (addition) {
217
- return group(printWithAddition(keyword, path, print, { breaking: true }));
218
- }
216
+ // If there's an additional clause that wasn't matched earlier, we know we
217
+ // can't go for the inline option.
218
+ if (addition) {
219
+ return group(printWithAddition(keyword, path, print, { breaking: true }));
220
+ }
219
221
 
220
- // If the body of the conditional is empty, then we explicitly have to use the
221
- // block form.
222
- if (isEmptyStmts(statements)) {
223
- return concat([
224
- `${keyword} `,
225
- align(keyword.length + 1, path.call(print, "body", 0)),
226
- concat([hardline, "end"])
227
- ]);
228
- }
222
+ // If the body of the conditional is empty, then we explicitly have to use the
223
+ // block form.
224
+ if (isEmptyStmts(statements)) {
225
+ return concat([
226
+ `${keyword} `,
227
+ align(keyword.length + 1, path.call(print, "body", 0)),
228
+ concat([hardline, "end"])
229
+ ]);
230
+ }
229
231
 
230
- // If the predicate of the conditional contains an assignment, then we can't
231
- // know for sure that it doesn't impact the body of the conditional, so we
232
- // have to default to the block form.
233
- if (containsAssignment(predicate)) {
234
- return concat([
235
- `${keyword} `,
236
- align(keyword.length + 1, path.call(print, "body", 0)),
237
- indent(concat([hardline, path.call(print, "body", 1)])),
238
- concat([hardline, "end"])
239
- ]);
240
- }
232
+ // If the predicate of the conditional contains an assignment, then we can't
233
+ // know for sure that it doesn't impact the body of the conditional, so we
234
+ // have to default to the block form.
235
+ if (containsAssignment(predicate)) {
236
+ return concat([
237
+ `${keyword} `,
238
+ align(keyword.length + 1, path.call(print, "body", 0)),
239
+ indent(concat([hardline, path.call(print, "body", 1)])),
240
+ concat([hardline, "end"])
241
+ ]);
242
+ }
241
243
 
242
- return printSingle(keyword)(path, { rubyModifier }, print);
243
- };
244
+ return printSingle(keyword)(path, { rubyModifier }, print);
245
+ };
244
246
 
245
247
  module.exports = {
246
248
  else: (path, opts, print) => {
@@ -56,28 +56,19 @@ function printHashKeyLabel(path, print) {
56
56
  case "symbol_literal":
57
57
  return concat([path.call(print, "body", 0), ":"]);
58
58
  case "dyna_symbol": {
59
- const { parts } = print(path);
60
-
61
- // We're going to slice off the starting colon character so that we can
62
- // move it to the end. If there are comments, then we're going to go
63
- // further into the printed doc nodes.
64
- if (parts[0] === ":") {
65
- parts.splice(0, 1);
66
- } else {
67
- parts[1].parts.splice(0, 1);
68
- }
69
-
70
- return concat(parts.concat(":"));
59
+ return concat([print(path), ":"]);
71
60
  }
72
61
  }
73
62
  }
74
63
 
75
64
  function printHashKeyRocket(path, print) {
76
65
  const node = path.getValue();
77
- const doc = print(path);
66
+ let doc = print(path);
78
67
 
79
68
  if (node.type === "@label") {
80
- return `:${doc.slice(0, doc.length - 1)} =>`;
69
+ doc = concat([":", doc.slice(0, doc.length - 1)]);
70
+ } else if (node.type === "dyna_symbol") {
71
+ doc = concat([":", doc]);
81
72
  }
82
73
 
83
74
  return concat([doc, " =>"]);
@@ -17,15 +17,8 @@ function printRestParam(symbol) {
17
17
  }
18
18
 
19
19
  function printParams(path, opts, print) {
20
- const [
21
- reqs,
22
- optls,
23
- rest,
24
- post,
25
- kwargs,
26
- kwargRest,
27
- block
28
- ] = path.getValue().body;
20
+ const [reqs, optls, rest, post, kwargs, kwargRest, block] =
21
+ path.getValue().body;
29
22
  let parts = [];
30
23
 
31
24
  if (reqs) {
@@ -82,13 +82,106 @@ function printChar(path, { rubySingleQuote }, _print) {
82
82
  return concat([quote, body.slice(1), quote]);
83
83
  }
84
84
 
85
+ function printPercentSDynaSymbol(path, opts, print) {
86
+ const node = path.getValue();
87
+ const parts = [];
88
+
89
+ // Push on the quote, which includes the opening character.
90
+ parts.push(node.quote);
91
+
92
+ path.each((childPath) => {
93
+ const childNode = childPath.getValue();
94
+
95
+ if (childNode.type !== "@tstring_content") {
96
+ // Here we are printing an embedded variable or expression.
97
+ parts.push(print(childPath));
98
+ } else {
99
+ // Here we are printing plain string content.
100
+ parts.push(join(literalline, childNode.body.split("\n")));
101
+ }
102
+ }, "body");
103
+
104
+ // Push on the closing character, which is the opposite of the third
105
+ // character from the opening.
106
+ parts.push(quotePairs[node.quote[2]]);
107
+
108
+ return concat(parts);
109
+ }
110
+
111
+ // We don't actually want to print %s symbols, as they're much more rarely seen
112
+ // in the wild. But we're going to be forced into it if it's a multi-line symbol
113
+ // or if the quoting would get super complicated.
114
+ function shouldPrintPercentSDynaSymbol(node) {
115
+ // We shouldn't print a %s dyna symbol if it was not already that way in the
116
+ // original source.
117
+ if (node.quote[0] !== "%") {
118
+ return false;
119
+ }
120
+
121
+ // Here we're going to check if there is a closing character, a new line, or a
122
+ // quote in the content of the dyna symbol. If there is, then quoting could
123
+ // get weird, so just bail out and stick to the original bounds in the source.
124
+ const closing = quotePairs[node.quote[2]];
125
+
126
+ return node.body.some(
127
+ (child) =>
128
+ child.type === "@tstring_content" &&
129
+ (child.body.includes("\n") ||
130
+ child.body.includes(closing) ||
131
+ child.body.includes("'") ||
132
+ child.body.includes('"'))
133
+ );
134
+ }
135
+
85
136
  // Prints a dynamic symbol. Assumes there's a quote property attached to the
86
137
  // node that will tell us which quote to use when printing. We're just going to
87
138
  // use whatever quote was provided.
139
+ //
140
+ // In the case of a plain dyna symbol, node.quote will be either :" or :'
141
+ // For %s dyna symbols, node.quote will be %s[, %s(, %s{, or %s<
88
142
  function printDynaSymbol(path, opts, print) {
89
- const { quote } = path.getValue();
143
+ const node = path.getValue();
144
+
145
+ if (shouldPrintPercentSDynaSymbol(node)) {
146
+ return printPercentSDynaSymbol(path, opts, print);
147
+ }
148
+
149
+ const parts = [];
150
+ let quote;
151
+
152
+ if (isQuoteLocked(node)) {
153
+ if (node.quote.startsWith("%")) {
154
+ quote = opts.rubySingleQuote ? "'" : '"';
155
+ } else {
156
+ quote = node.quote.slice(1);
157
+ }
158
+ } else {
159
+ quote = opts.rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
160
+ }
161
+
162
+ parts.push(quote);
163
+ path.each((childPath) => {
164
+ const child = childPath.getValue();
165
+
166
+ if (child.type !== "@tstring_content") {
167
+ parts.push(print(childPath));
168
+ } else {
169
+ parts.push(
170
+ join(literalline, normalizeQuotes(child.body, quote).split("\n"))
171
+ );
172
+ }
173
+ }, "body");
174
+
175
+ parts.push(quote);
176
+
177
+ // If we're inside of an assoc_new node as the key, then it will handle
178
+ // printing the : on its own since it could change sides.
179
+ const parentNode = path.getParentNode();
180
+ if (parentNode.type !== "assoc_new" || parentNode.body[0] !== node) {
181
+ parts.unshift(":");
182
+ }
90
183
 
91
- return concat([":", quote].concat(path.map(print, "body")).concat(quote));
184
+ return concat(parts);
92
185
  }
93
186
 
94
187
  function printStringConcat(path, opts, print) {