prettier 0.18.0 → 0.20.0

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.
@@ -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
  ) ||