prettier 0.18.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,7 +42,7 @@ module.exports = {
42
42
 
43
43
  // It's possible in a when to just have empty void statements, in which case
44
44
  // we would skip adding the body.
45
- if (!stmts.parts.every(part => !part)) {
45
+ if (!stmts.parts.every((part) => !part)) {
46
46
  parts.push(indent(concat([hardline, stmts])));
47
47
  }
48
48
 
@@ -1,7 +1,7 @@
1
1
  const { align, concat, group, ifBreak, join, line } = require("../prettier");
2
2
  const { docLength, makeArgs, makeCall } = require("../utils");
3
3
 
4
- const hasDef = node =>
4
+ const hasDef = (node) =>
5
5
  node.body[1].type === "args_add_block" &&
6
6
  node.body[1].body[0].type === "args" &&
7
7
  node.body[1].body[0].body[0] &&
@@ -20,7 +20,7 @@ const hasDef = node =>
20
20
  //
21
21
  // In this case the arguments are aligned to the left side as opposed to being
22
22
  // aligned with the `receive` call.
23
- const skipArgsAlign = path =>
23
+ const skipArgsAlign = (path) =>
24
24
  ["to", "not_to"].includes(path.getValue().body[2].body);
25
25
 
26
26
  module.exports = {
@@ -23,7 +23,7 @@ const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
23
23
  // For the unary `not` operator, we need to explicitly add parentheses to it in
24
24
  // order for it to be valid from within a ternary. Otherwise if the clause of
25
25
  // the ternary isn't a unary `not`, we can just pass it along.
26
- const printTernaryClause = clause => {
26
+ const printTernaryClause = (clause) => {
27
27
  if (clause.type === "concat") {
28
28
  const [part] = clause.parts;
29
29
 
@@ -78,7 +78,7 @@ const printTernary = (path, _opts, print) => {
78
78
  // Prints an `if_mod` or `unless_mod` node. Because it was previously in the
79
79
  // modifier form, we're guaranteed to not have an additional node, so we can
80
80
  // just work with the predicate and the body.
81
- const printSingle = keyword => (path, { inlineConditionals }, print) => {
81
+ const printSingle = (keyword) => (path, { inlineConditionals }, print) => {
82
82
  const multiline = concat([
83
83
  `${keyword} `,
84
84
  align(keyword.length + 1, path.call(print, "body", 0)),
@@ -88,7 +88,8 @@ const printSingle = keyword => (path, { inlineConditionals }, print) => {
88
88
 
89
89
  const [_predicate, stmts] = path.getValue().body;
90
90
  const hasComments =
91
- stmts.type === "stmts" && stmts.body.some(stmt => stmt.type === "@comment");
91
+ stmts.type === "stmts" &&
92
+ stmts.body.some((stmt) => stmt.type === "@comment");
92
93
 
93
94
  if (!inlineConditionals || hasComments) {
94
95
  return multiline;
@@ -152,7 +153,7 @@ const noTernary = [
152
153
  // Certain expressions cannot be reduced to a ternary without adding parens
153
154
  // around them. In this case we say they cannot be ternaried and default instead
154
155
  // to breaking them into multiple lines.
155
- const canTernaryStmts = stmts => {
156
+ const canTernaryStmts = (stmts) => {
156
157
  if (stmts.body.length !== 1) {
157
158
  return false;
158
159
  }
@@ -175,10 +176,11 @@ const canTernaryStmts = stmts => {
175
176
  // is of the "else" type. Both the body of the main node and the body of the
176
177
  // additional node must have only one statement, and that statement list must
177
178
  // pass the `canTernaryStmts` check.
178
- const canTernary = path => {
179
- const [_pred, stmts, addition] = path.getValue().body;
179
+ const canTernary = (path) => {
180
+ const [predicate, stmts, addition] = path.getValue().body;
180
181
 
181
182
  return (
183
+ !["assign", "opassign"].includes(predicate.type) &&
182
184
  addition &&
183
185
  addition.type === "else" &&
184
186
  [stmts, addition.body[0]].every(canTernaryStmts)
@@ -186,7 +188,7 @@ const canTernary = path => {
186
188
  };
187
189
 
188
190
  // A normalized print function for both `if` and `unless` nodes.
189
- const printConditional = keyword => (path, { inlineConditionals }, print) => {
191
+ const printConditional = (keyword) => (path, { inlineConditionals }, print) => {
190
192
  if (canTernary(path)) {
191
193
  let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
192
194
  printTernaryClauses(
@@ -12,7 +12,7 @@ const { prefix, skipAssignIndent } = require("../utils");
12
12
  const nodeDive = (node, steps) => {
13
13
  let current = node;
14
14
 
15
- steps.forEach(step => {
15
+ steps.forEach((step) => {
16
16
  current = current[step];
17
17
  });
18
18
 
@@ -29,7 +29,7 @@ const nodeDive = (node, steps) => {
29
29
  //
30
30
  // This function represents that check, as it determines if it can convert the
31
31
  // symbol node into a hash label.
32
- const isValidHashLabel = symbolLiteral => {
32
+ const isValidHashLabel = (symbolLiteral) => {
33
33
  const label = symbolLiteral.body[0].body[0].body;
34
34
  return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
35
35
  };
@@ -1,6 +1,6 @@
1
1
  const { concat, group, indent, line } = require("../prettier");
2
2
 
3
- const printHook = name => (path, opts, print) =>
3
+ const printHook = (name) => (path, opts, print) =>
4
4
  group(
5
5
  concat([
6
6
  `${name} {`,
@@ -21,7 +21,7 @@ module.exports = {
21
21
  paramsConcat = path.call(print, "body", 0, "body", 0);
22
22
  }
23
23
 
24
- const noParams = params.body.every(type => !type);
24
+ const noParams = params.body.every((type) => !type);
25
25
  const inlineLambda = concat([
26
26
  "->",
27
27
  noParams ? "" : concat(["(", paramsConcat, ")"]),
@@ -9,7 +9,7 @@ const {
9
9
  } = require("../prettier");
10
10
  const { first, literal } = require("../utils");
11
11
 
12
- const printMethod = offset => (path, opts, print) => {
12
+ const printMethod = (offset) => (path, opts, print) => {
13
13
  const [_name, params, body] = path.getValue().body.slice(offset);
14
14
  const declaration = ["def "];
15
15
 
@@ -21,7 +21,7 @@ const printMethod = offset => (path, opts, print) => {
21
21
 
22
22
  // In case there are no parens but there are arguments
23
23
  const parens =
24
- params.type === "params" && params.body.some(paramType => paramType);
24
+ params.type === "params" && params.body.some((paramType) => paramType);
25
25
 
26
26
  declaration.push(
27
27
  path.call(print, "body", offset),
@@ -7,10 +7,18 @@ module.exports = {
7
7
 
8
8
  return group(
9
9
  concat([
10
- concat([path.call(print, "body", 0), useNoSpace ? "" : " "]),
11
- operator,
10
+ group(path.call(print, "body", 0)),
12
11
  indent(
13
- concat([useNoSpace ? softline : line, path.call(print, "body", 2)])
12
+ concat([
13
+ useNoSpace ? "" : " ",
14
+ group(
15
+ concat([
16
+ operator,
17
+ useNoSpace ? softline : line,
18
+ path.call(print, "body", 2)
19
+ ])
20
+ )
21
+ ])
14
22
  )
15
23
  ])
16
24
  );
@@ -1,7 +1,7 @@
1
1
  const { concat, group, join, line } = require("../prettier");
2
2
  const { literal } = require("../utils");
3
3
 
4
- const printGenericRestParam = symbol => (path, opts, print) =>
4
+ const printGenericRestParam = (symbol) => (path, opts, print) =>
5
5
  path.getValue().body[0]
6
6
  ? concat([symbol, path.call(print, "body", 0)])
7
7
  : symbol;
@@ -7,7 +7,7 @@ module.exports = {
7
7
  const [contents, ending] = path.map(print, "body");
8
8
 
9
9
  const useBraces = contents.some(
10
- content => typeof content === "string" && content.includes("/")
10
+ (content) => typeof content === "string" && content.includes("/")
11
11
  );
12
12
  const parts = [useBraces ? "%r{" : "/"]
13
13
  .concat(contents)
@@ -53,7 +53,11 @@ module.exports = {
53
53
  parts.push(" StandardError");
54
54
  }
55
55
 
56
- parts.push(indent(concat([hardline, path.call(print, "body", 2)])));
56
+ const rescueBody = path.call(print, "body", 2);
57
+
58
+ if (rescueBody.parts.length > 0) {
59
+ parts.push(indent(concat([hardline, rescueBody])));
60
+ }
57
61
 
58
62
  // This is the next clause on the `begin` statement, either another
59
63
  // `rescue`, and `ensure`, or an `else` clause.
@@ -9,6 +9,16 @@ const {
9
9
  } = require("../prettier");
10
10
  const { literal } = require("../utils");
11
11
 
12
+ // You can't skip the parentheses if you have the `and` or `or` operator,
13
+ // because they have low enough operator precedence that you need to explicitly
14
+ // keep them in there.
15
+ const canSkipParens = (args) => {
16
+ const statement = args.body[0].body[0].body[0];
17
+ return (
18
+ statement.type !== "binary" || !["and", "or"].includes(statement.body[1])
19
+ );
20
+ };
21
+
12
22
  const printReturn = (path, opts, print) => {
13
23
  let args = path.getValue().body[0].body[0];
14
24
  let steps = ["body", 0, "body", 0];
@@ -20,7 +30,7 @@ const printReturn = (path, opts, print) => {
20
30
  // If the body of the return contains parens, then just skip directly to the
21
31
  // content of the parens so that we can skip printing parens if we don't
22
32
  // want them.
23
- if (args.body[0] && args.body[0].type === "paren") {
33
+ if (args.body[0] && args.body[0].type === "paren" && canSkipParens(args)) {
24
34
  args = args.body[0].body[0];
25
35
  steps = steps.concat("body", 0, "body", 0);
26
36
  }
@@ -15,18 +15,18 @@ const { concatBody, empty, makeList, prefix, surround } = require("../utils");
15
15
  // quote the user chose. (If they chose single quotes, then double quoting
16
16
  // would activate the escape sequence, and if they chose double quotes, then
17
17
  // single quotes would deactivate it.)
18
- const isQuoteLocked = string =>
18
+ const isQuoteLocked = (string) =>
19
19
  string.body.some(
20
- part =>
20
+ (part) =>
21
21
  part.type === "@tstring_content" &&
22
22
  (part.body.includes("#{") || part.body.includes("\\"))
23
23
  );
24
24
 
25
25
  // A string is considered to be able to use single quotes if it contains only
26
26
  // plain string content and that content does not contain a single quote.
27
- const isSingleQuotable = string =>
27
+ const isSingleQuotable = (string) =>
28
28
  string.body.every(
29
- part => part.type === "@tstring_content" && !part.body.includes("'")
29
+ (part) => part.type === "@tstring_content" && !part.body.includes("'")
30
30
  );
31
31
 
32
32
  const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
@@ -61,7 +61,7 @@ const quotePairs = {
61
61
  "<": ">"
62
62
  };
63
63
 
64
- const getClosingQuote = quote => {
64
+ const getClosingQuote = (quote) => {
65
65
  if (!quote.startsWith("%")) {
66
66
  return quote;
67
67
  }
@@ -1,11 +1,27 @@
1
1
  const { spawnSync } = require("child_process");
2
2
  const path = require("path");
3
3
 
4
+ // In order to properly parse ruby code, we need to tell the ruby process to
5
+ // parse using UTF-8. Unfortunately, the way that you accomplish this looks
6
+ // differently depending on your platform. This object below represents all of
7
+ // the possible values of process.platform per:
8
+ // https://nodejs.org/api/process.html#process_process_platform
9
+ const LANG = {
10
+ aix: "C.UTF-8",
11
+ darwin: "en_US.UTF-8",
12
+ freebsd: "C.UTF-8",
13
+ linux: "C.UTF-8",
14
+ openbsd: "C.UTF-8",
15
+ sunos: "C.UTF-8",
16
+ win32: ".UTF-8"
17
+ }[process.platform];
18
+
4
19
  module.exports = (text, _parsers, _opts) => {
5
20
  const child = spawnSync(
6
21
  "ruby",
7
22
  ["--disable-gems", path.join(__dirname, "./ripper.rb")],
8
23
  {
24
+ env: Object.assign({}, process.env, { LANG }),
9
25
  input: text,
10
26
  maxBuffer: 10 * 1024 * 1024 // 10MB
11
27
  }
@@ -3,7 +3,6 @@
3
3
  // directly, as it's been shipped with the gem.
4
4
  const source = process.env.RBPRETTIER ? "../node_modules/prettier" : "prettier";
5
5
 
6
- // eslint-disable-next-line import/no-dynamic-require
7
6
  const prettier = require(source);
8
7
 
9
8
  // Just combine all the things into one big object so that we can import
@@ -3,10 +3,11 @@
3
3
  # We implement our own version checking here instead of using Gem::Version so
4
4
  # that we can use the --disable-gems flag.
5
5
  major, minor, * = RUBY_VERSION.split('.').map(&:to_i)
6
+
6
7
  if (major < 2) || ((major == 2) && (minor < 5))
7
8
  warn(
8
- "Ruby version #{current_version} not supported. " \
9
- "Please upgrade to #{required_version} or above."
9
+ "Ruby version #{RUBY_VERSION} not supported. " \
10
+ 'Please upgrade to 2.5.0 or above.'
10
11
  )
11
12
 
12
13
  exit 1
@@ -1,13 +1,14 @@
1
+ const embed = require("./embed");
1
2
  const parse = require("./parse");
2
3
  const print = require("./print");
3
4
 
4
5
  const haml = require("./haml");
5
6
 
6
7
  const pragmaPattern = /#\s*@(prettier|format)/;
7
- const hasPragma = text => pragmaPattern.test(text);
8
+ const hasPragma = (text) => pragmaPattern.test(text);
8
9
 
9
- const locStart = node => node.char_start;
10
- const locEnd = node => node.char_end;
10
+ const locStart = (node) => node.char_start;
11
+ const locEnd = (node) => node.char_end;
11
12
 
12
13
  /*
13
14
  * metadata mostly pulled from linguist and rubocop:
@@ -49,6 +50,7 @@ module.exports = {
49
50
  filenames: [
50
51
  ".irbrc",
51
52
  ".pryrc",
53
+ ".simplecov",
52
54
  "Appraisals",
53
55
  "Berksfile",
54
56
  "Brewfile",
@@ -78,7 +80,8 @@ module.exports = {
78
80
  {
79
81
  name: "HAML",
80
82
  parsers: ["haml"],
81
- extensions: [".haml"]
83
+ extensions: [".haml"],
84
+ vscodeLanguageIds: ["haml"]
82
85
  }
83
86
  ],
84
87
  parsers: {
@@ -99,6 +102,7 @@ module.exports = {
99
102
  },
100
103
  printers: {
101
104
  ruby: {
105
+ embed,
102
106
  print
103
107
  },
104
108
  haml: {
@@ -141,6 +145,13 @@ module.exports = {
141
145
  default: true,
142
146
  description:
143
147
  "When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals."
148
+ },
149
+ toProcTransform: {
150
+ type: "boolean",
151
+ category: "Global",
152
+ default: true,
153
+ description:
154
+ "When possible, convert blocks to the more concise Symbol#to_proc syntax."
144
155
  }
145
156
  },
146
157
  defaultOptions: {
@@ -1,4 +1,4 @@
1
- const isCall = node => ["::", "."].includes(node) || node.type === "@period";
1
+ const isCall = (node) => ["::", "."].includes(node) || node.type === "@period";
2
2
 
3
3
  // If you have a simple block that only calls a method on the single required
4
4
  // parameter that is passed to it, then you can replace that block with the
@@ -11,8 +11,8 @@ const isCall = node => ["::", "."].includes(node) || node.type === "@period";
11
11
  // [1, 2, 3].map(&:to_s)
12
12
  //
13
13
  // This works with `do` blocks as well.
14
- const toProc = node => {
15
- if (!node) {
14
+ const toProc = (path, opts, node) => {
15
+ if (!node || !opts.toProcTransform) {
16
16
  return null;
17
17
  }
18
18
 
@@ -76,6 +76,33 @@ const toProc = node => {
76
76
  return null;
77
77
  }
78
78
 
79
+ // Ensure that we're not inside of a hash that is being passed to a key that
80
+ // corresponds to `:if` or `:unless` to avoid problems with callbacks with
81
+ // Rails. For more context, see:
82
+ // https://github.com/prettier/plugin-ruby/issues/449
83
+ let assocNode = null;
84
+
85
+ if (path.getValue().type === "method_add_block") {
86
+ assocNode = path.getParentNode();
87
+ } else if (path.getValue().type === "args") {
88
+ assocNode = path.getParentNode(2);
89
+ }
90
+
91
+ if (assocNode && assocNode.type === "assoc_new") {
92
+ const [key] = assocNode.body;
93
+
94
+ if (key.type === "@label" && ["if:", "unless:"].includes(key.body)) {
95
+ return null;
96
+ }
97
+
98
+ if (
99
+ key.type === "symbol_literal" &&
100
+ ["if", "unless"].includes(key.body[0].body[0].body)
101
+ ) {
102
+ return null;
103
+ }
104
+ }
105
+
79
106
  return `&:${method.body}`;
80
107
  };
81
108
 
@@ -10,11 +10,11 @@ const concatBody = (path, opts, print) => concat(path.map(print, "body"));
10
10
 
11
11
  // If the node is a type of assignment or if the node is a paren and nested
12
12
  // inside that paren is a node that is a type of assignment.
13
- const containsAssignment = node =>
13
+ const containsAssignment = (node) =>
14
14
  ["assign", "massign"].includes(node.type) ||
15
15
  (node.type === "paren" && node.body[0].body.some(containsAssignment));
16
16
 
17
- const docLength = doc => {
17
+ const docLength = (doc) => {
18
18
  if (doc.length) {
19
19
  return doc.length;
20
20
  }
@@ -50,7 +50,7 @@ const hasAncestor = (path, types) => {
50
50
  return false;
51
51
  };
52
52
 
53
- const literal = value => () => value;
53
+ const literal = (value) => () => value;
54
54
 
55
55
  const makeArgs = (path, opts, print, argsIndex) => {
56
56
  let argNodes = path.getValue().body[argsIndex];
@@ -103,13 +103,13 @@ const makeCall = (path, opts, print) => {
103
103
 
104
104
  const makeList = (path, opts, print) => path.map(print, "body");
105
105
 
106
- const prefix = value => (path, opts, print) =>
106
+ const prefix = (value) => (path, opts, print) =>
107
107
  concat([value, path.call(print, "body", 0)]);
108
108
 
109
109
  const printComments = (printed, start, comments) => {
110
110
  let node = printed;
111
111
 
112
- comments.forEach(comment => {
112
+ comments.forEach((comment) => {
113
113
  if (comment.start < start) {
114
114
  node = concat([
115
115
  comment.break ? breakParent : "",
@@ -129,7 +129,7 @@ const printComments = (printed, start, comments) => {
129
129
  return node;
130
130
  };
131
131
 
132
- const skipAssignIndent = node =>
132
+ const skipAssignIndent = (node) =>
133
133
  ["array", "hash", "heredoc", "lambda", "regexp_literal"].includes(
134
134
  node.type
135
135
  ) ||