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,6 +1,6 @@
1
1
  {
2
2
  "name": "@prettier/plugin-ruby",
3
- "version": "0.20.0",
3
+ "version": "1.0.0-rc2",
4
4
  "description": "prettier plugin for the Ruby programming language",
5
5
  "main": "src/ruby.js",
6
6
  "scripts": {
@@ -23,9 +23,9 @@
23
23
  },
24
24
  "devDependencies": {
25
25
  "all-contributors-cli": "^6.14.1",
26
- "eslint": "^7.1.0",
27
- "eslint-config-prettier": "^6.10.1",
28
- "husky": "^4.2.5",
26
+ "eslint": "^7.8.1",
27
+ "eslint-config-prettier": "^7.0.0",
28
+ "husky": "^5.0.4",
29
29
  "jest": "^26.0.0",
30
30
  "pretty-quick": "^3.0.0"
31
31
  },
@@ -1,12 +1,15 @@
1
1
  const {
2
2
  concat,
3
+ group,
3
4
  indent,
4
- literalline,
5
+ lineSuffix,
5
6
  mapDoc,
6
7
  markAsRoot,
7
8
  stripTrailingHardline
8
9
  } = require("./prettier");
9
10
 
11
+ const { literalLineNoBreak } = require("./utils");
12
+
10
13
  const parsers = {
11
14
  css: "css",
12
15
  javascript: "babel",
@@ -23,12 +26,12 @@ const replaceNewlines = (doc) =>
23
26
  ? concat(
24
27
  currentDoc
25
28
  .split(/(\n)/g)
26
- .map((v, i) => (i % 2 === 0 ? v : literalline))
29
+ .map((v, i) => (i % 2 === 0 ? v : literalLineNoBreak))
27
30
  )
28
31
  : currentDoc
29
32
  );
30
33
 
31
- const embed = (path, _print, textToDoc, _opts) => {
34
+ const embed = (path, print, textToDoc, _opts) => {
32
35
  const node = path.getValue();
33
36
 
34
37
  // Currently we only support embedded formatting on heredoc nodes
@@ -44,7 +47,7 @@ const embed = (path, _print, textToDoc, _opts) => {
44
47
 
45
48
  // Next, find the parser associated with this heredoc (if there is one). For
46
49
  // example, if you use <<~CSS, we'd hook it up to the css parser.
47
- const parser = parsers[beging.slice(3).toLowerCase()];
50
+ const parser = parsers[beging.body.slice(3).toLowerCase()];
48
51
  if (!parser) {
49
52
  return null;
50
53
  }
@@ -53,19 +56,35 @@ const embed = (path, _print, textToDoc, _opts) => {
53
56
  // into the embedded parser. Get back the doc node.
54
57
  const content = body.map((part) => part.body).join("");
55
58
  const formatted = concat([
56
- literalline,
59
+ literalLineNoBreak,
57
60
  replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
58
61
  ]);
59
62
 
60
63
  // If we're using a squiggly heredoc, then we can properly handle indentation
61
64
  // ourselves.
62
- if (beging[2] === "~") {
63
- return concat([beging, indent(markAsRoot(formatted)), literalline, ending]);
65
+ if (beging.body[2] === "~") {
66
+ return concat([
67
+ path.call(print, "beging"),
68
+ lineSuffix(
69
+ group(
70
+ concat([
71
+ indent(markAsRoot(formatted)),
72
+ literalLineNoBreak,
73
+ ending.trim()
74
+ ])
75
+ )
76
+ )
77
+ ]);
64
78
  }
65
79
 
66
80
  // Otherwise, we need to just assume it's formatted correctly and return the
67
81
  // content as it is.
68
- return markAsRoot(concat([beging, formatted, literalline, ending]));
82
+ return markAsRoot(
83
+ concat([
84
+ path.call(print, "beging"),
85
+ lineSuffix(group(concat([formatted, literalLineNoBreak, ending.trim()])))
86
+ ])
87
+ );
69
88
  };
70
89
 
71
90
  module.exports = embed;
@@ -1,17 +1,20 @@
1
1
  module.exports = Object.assign(
2
2
  {},
3
3
  require("./nodes/alias"),
4
+ require("./nodes/aref"),
4
5
  require("./nodes/args"),
5
6
  require("./nodes/arrays"),
6
7
  require("./nodes/assign"),
7
8
  require("./nodes/blocks"),
8
9
  require("./nodes/calls"),
9
10
  require("./nodes/case"),
11
+ require("./nodes/class"),
10
12
  require("./nodes/commands"),
11
13
  require("./nodes/conditionals"),
12
14
  require("./nodes/constants"),
13
15
  require("./nodes/flow"),
14
16
  require("./nodes/hashes"),
17
+ require("./nodes/heredocs"),
15
18
  require("./nodes/hooks"),
16
19
  require("./nodes/ints"),
17
20
  require("./nodes/lambdas"),
@@ -24,7 +27,8 @@ module.exports = Object.assign(
24
27
  require("./nodes/regexp"),
25
28
  require("./nodes/rescue"),
26
29
  require("./nodes/return"),
27
- require("./nodes/scopes"),
28
30
  require("./nodes/statements"),
29
- require("./nodes/strings")
31
+ require("./nodes/strings"),
32
+ require("./nodes/super"),
33
+ require("./nodes/undef")
30
34
  );
@@ -1,32 +1,73 @@
1
- const { concat, join } = require("../prettier");
1
+ const {
2
+ addTrailingComment,
3
+ align,
4
+ concat,
5
+ group,
6
+ hardline,
7
+ line
8
+ } = require("../prettier");
2
9
 
3
- const usingSymbols = (path) => {
4
- const [left, right] = path.getValue().body.map((node) => node.body[0].type);
5
- return left === "symbol" && right === "symbol";
6
- };
7
-
8
- const identFromSymbol = (path, print, index) =>
9
- path.call(print, "body", index, "body", 0, "body", 0);
10
+ // In general, return the printed doc of the argument at the provided index.
11
+ // Special handling is given for symbol literals that are not bare words, as we
12
+ // convert those into bare words by just pulling out the ident node.
13
+ function printAliasArgument(path, print, argIndex) {
14
+ const node = path.getValue().body[argIndex];
10
15
 
11
- const aliasError = (_path, _opts, _print) => {
12
- throw new Error("can't make alias for the number variables");
13
- };
16
+ if (node.type === "symbol_literal") {
17
+ // If we're going to descend into the symbol literal to grab out the ident
18
+ // node, then we need to make sure we copy over any comments as well,
19
+ // otherwise we could accidentally skip printing them.
20
+ if (node.comments) {
21
+ node.comments.forEach((comment) => {
22
+ addTrailingComment(node.body[0], comment);
23
+ });
24
+ }
14
25
 
15
- const aliasVars = (path, opts, print) => {
16
- if (usingSymbols(path)) {
17
- return join(" ", [
18
- identFromSymbol(path, print, 0),
19
- identFromSymbol(path, print, 1)
20
- ]);
26
+ return path.call(print, "body", argIndex, "body", 0);
21
27
  }
22
- return join(" ", path.map(print, "body"));
23
- };
24
28
 
25
- const alias = (path, opts, print) =>
26
- concat(["alias ", aliasVars(path, opts, print)]);
29
+ return path.call(print, "body", argIndex);
30
+ }
31
+
32
+ // The `alias` keyword is used to make a method respond to another name as well
33
+ // as the current one. For example, to get the method `foo` to also respond to
34
+ // `bar`, you would:
35
+ //
36
+ // alias bar foo
37
+ //
38
+ // Now, in the current context you can call `bar` and it will execute the `foo`
39
+ // method.
40
+ //
41
+ // When you're aliasing two methods, you can either provide bare words (like the
42
+ // example above) or you can provide symbols (note that this includes dynamic
43
+ // symbols like :"foo-#{bar}-baz"). In general, to be consistent with the ruby
44
+ // style guide, we prefer bare words:
45
+ //
46
+ // https://github.com/rubocop-hq/ruby-style-guide#alias-method-lexically
47
+ //
48
+ // The `alias` node contains two children. The left and right align with the
49
+ // arguments passed to the keyword. So, for the above example the left would be
50
+ // the symbol literal `bar` and the right could be the symbol literal `foo`.
51
+ function printAlias(path, opts, print) {
52
+ const keyword = "alias ";
53
+
54
+ const rightSide = concat([
55
+ // If the left child has any comments, then we need to explicitly break this
56
+ // into two lines
57
+ path.getValue().body[0].comments ? hardline : line,
58
+ printAliasArgument(path, print, 1)
59
+ ]);
60
+
61
+ return group(
62
+ concat([
63
+ keyword,
64
+ printAliasArgument(path, print, 0),
65
+ group(align(keyword.length, rightSide))
66
+ ])
67
+ );
68
+ }
27
69
 
28
70
  module.exports = {
29
- alias,
30
- alias_error: aliasError,
31
- var_alias: alias
71
+ alias: printAlias,
72
+ var_alias: printAlias
32
73
  };
@@ -0,0 +1,55 @@
1
+ const { concat, group, indent, join, line, softline } = require("../prettier");
2
+
3
+ // `aref` nodes are when you're pulling a value out of a collection at a
4
+ // specific index. Put another way, it's any time you're calling the method
5
+ // `#[]`.
6
+ //
7
+ // The nodes usually contains two children, details below in the
8
+ // `printArefField` function. In some cases, you don't necessarily have the
9
+ // second child node, because you can call procs with a pretty esoteric syntax.
10
+ // In the following example, you wouldn't have a second child, and `"foo"` would
11
+ // be the first child.
12
+ //
13
+ // foo[]
14
+ //
15
+ function printAref(path, opts, print) {
16
+ const indexNode = path.getValue().body[1];
17
+
18
+ if (!indexNode) {
19
+ return concat([path.call(print, "body", 0), "[]"]);
20
+ }
21
+
22
+ return printArefField(path, opts, print);
23
+ }
24
+
25
+ // `aref_field` nodes are for assigning values into collections at specific
26
+ // indices. Put another way, it's any time you're calling the method `#[]=`.
27
+ // The `aref_field` node itself is just the left side of the assignment, and
28
+ // they're always wrapped in `assign` nodes.
29
+ //
30
+ // The nodes always contain two children, the name of the array (usually a
31
+ // `vcall` node and the index (usually an `args_add_block` node). The
32
+ // `args_add_block` is one of a couple nodes that has special handling where its
33
+ // printed form is actually an array to make joining easier.
34
+ //
35
+ // So in the following example, `"foo"` is the array and `["bar"]` is the index.
36
+ //
37
+ // foo[bar] = baz
38
+ //
39
+ function printArefField(path, opts, print) {
40
+ const [printedArray, printedIndex] = path.map(print, "body");
41
+
42
+ return group(
43
+ concat([
44
+ printedArray,
45
+ "[",
46
+ indent(concat([softline, join(concat([",", line]), printedIndex)])),
47
+ concat([softline, "]"])
48
+ ])
49
+ );
50
+ }
51
+
52
+ module.exports = {
53
+ aref: printAref,
54
+ aref_field: printArefField
55
+ };
@@ -9,53 +9,56 @@ const {
9
9
  } = require("../prettier");
10
10
 
11
11
  const toProc = require("../toProc");
12
- const { makeArgs } = require("../utils");
12
+ const { getTrailingComma } = require("../utils");
13
13
 
14
- module.exports = {
15
- arg_paren: (path, opts, print) => {
16
- if (path.getValue().body[0] === null) {
17
- return "";
18
- }
19
-
20
- // Here we can skip the entire rest of the method by just checking if it's
21
- // an args_forward node, as we're guaranteed that there are no other arg
22
- // nodes.
23
- if (path.getValue().body[0].type === "args_forward") {
24
- return "(...)";
25
- }
14
+ function printArgParen(path, opts, print) {
15
+ const argsNode = path.getValue().body[0];
26
16
 
27
- const { addTrailingCommas } = opts;
28
- const { args, heredocs } = makeArgs(path, opts, print, 0);
29
-
30
- const argsNode = path.getValue().body[0];
31
- const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
32
-
33
- if (heredocs.length > 1) {
34
- return concat(["(", join(", ", args), ")"].concat(heredocs));
35
- }
17
+ if (argsNode === null) {
18
+ return "";
19
+ }
36
20
 
37
- const parenDoc = group(
21
+ // Here we can skip the entire rest of the method by just checking if it's
22
+ // an args_forward node, as we're guaranteed that there are no other arg
23
+ // nodes.
24
+ if (argsNode.type === "args_forward") {
25
+ return group(
38
26
  concat([
39
27
  "(",
40
- indent(
41
- concat([
42
- softline,
43
- join(concat([",", line]), args),
44
- addTrailingCommas && !hasBlock ? ifBreak(",", "") : ""
45
- ])
46
- ),
47
- concat([softline, ")"])
28
+ indent(concat([softline, path.call(print, "body", 0)])),
29
+ softline,
30
+ ")"
48
31
  ])
49
32
  );
50
-
51
- if (heredocs.length === 1) {
52
- return group(concat([parenDoc].concat(heredocs)));
53
- }
54
-
55
- return parenDoc;
56
- },
57
- args: (path, opts, print) => {
58
- const args = path.map(print, "body");
33
+ }
34
+
35
+ const args = path.call(print, "body", 0);
36
+ const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
37
+
38
+ // Now here we return a doc that represents the whole grouped expression,
39
+ // including the surrouding parentheses.
40
+ return group(
41
+ concat([
42
+ "(",
43
+ indent(
44
+ concat([
45
+ softline,
46
+ join(concat([",", line]), args),
47
+ getTrailingComma(opts) && !hasBlock ? ifBreak(",", "") : ""
48
+ ])
49
+ ),
50
+ softline,
51
+ ")"
52
+ ])
53
+ );
54
+ }
55
+
56
+ function printArgs(path, { rubyToProc }, print) {
57
+ const args = path.map(print, "body");
58
+
59
+ // Don't bother trying to do any kind of fancy toProc transform if the
60
+ // option is disabled.
61
+ if (rubyToProc) {
59
62
  let blockNode = null;
60
63
 
61
64
  // Look up the chain to see if these arguments are contained within a
@@ -72,21 +75,26 @@ module.exports = {
72
75
  return blockNode;
73
76
  });
74
77
 
75
- const proc = blockNode && toProc(path, opts, blockNode);
78
+ const proc = blockNode && toProc(path, blockNode);
76
79
 
77
- // If we have a successful to_proc transformation, but we're part of an aref
78
- // node, that means it's something to the effect of
80
+ // If we have a successful to_proc transformation, but we're part of an
81
+ // aref node, that means it's something to the effect of
79
82
  //
80
83
  // foo[:bar].each(&:to_s)
81
84
  //
82
- // In this case we need to just return regular arguments, otherwise we would
83
- // end up putting &:to_s inside the brackets accidentally.
85
+ // In this case we need to just return regular arguments, otherwise we
86
+ // would end up putting &:to_s inside the brackets accidentally.
84
87
  if (proc && path.getParentNode(1).type !== "aref") {
85
88
  args.push(proc);
86
89
  }
90
+ }
87
91
 
88
- return args;
89
- },
92
+ return args;
93
+ }
94
+
95
+ module.exports = {
96
+ arg_paren: printArgParen,
97
+ args: printArgs,
90
98
  args_add_block: (path, opts, print) => {
91
99
  const parts = path.call(print, "body", 0);
92
100
 
@@ -1,55 +1,79 @@
1
1
  const {
2
2
  concat,
3
3
  group,
4
+ hardline,
4
5
  ifBreak,
5
6
  indent,
6
7
  join,
7
8
  line,
8
- literalline,
9
9
  softline
10
10
  } = require("../prettier");
11
11
 
12
- const preserveArraySubstrings = [" ", "\\"];
13
-
14
- const isStringArray = (args) =>
15
- args.body.every(
16
- (arg) =>
17
- arg.type === "string_literal" &&
18
- arg.body[0].body.length === 1 &&
19
- arg.body[0].body[0].type === "@tstring_content" &&
20
- !preserveArraySubstrings.some((str) =>
21
- arg.body[0].body[0].body.includes(str)
22
- )
23
- );
12
+ const { getTrailingComma } = require("../utils");
13
+
14
+ // Checks that every argument within this args node is a string_literal node
15
+ // that has no spaces or interpolations. This means we're dealing with an array
16
+ // that looks something like:
17
+ //
18
+ // ['a', 'b', 'c']
19
+ //
20
+ function isStringArray(args) {
21
+ return (
22
+ args.body.length > 1 &&
23
+ args.body.every((arg) => {
24
+ // We want to verify that every node inside of this array is a string
25
+ // literal. We also want to make sure none of them have comments attached.
26
+ if (arg.type !== "string_literal" || arg.comments) {
27
+ return false;
28
+ }
24
29
 
25
- const isSymbolArray = (args) =>
26
- args.body.every((arg) => arg.type === "symbol_literal");
30
+ // If the string has multiple parts (meaning plain string content but also
31
+ // interpolated content) then we know it's not a simple string.
32
+ if (arg.body.length !== 1) {
33
+ return false;
34
+ }
27
35
 
28
- const makeArray = (start) => (path, opts, print) =>
29
- [start].concat(path.map(print, "body"));
36
+ const part = arg.body[0];
30
37
 
31
- const getSpecialArrayParts = (path, print, args) =>
32
- args.body.map((_arg, index) =>
33
- path.call(print, "body", 0, "body", index, "body", 0, "body", 0)
34
- );
38
+ // If the only part of this string is not @tstring_content then it's
39
+ // interpolated, so again we can return false.
40
+ if (part.type !== "@tstring_content") {
41
+ return false;
42
+ }
35
43
 
36
- const printAref = (path, opts, print) =>
37
- group(
38
- concat([
39
- path.call(print, "body", 0),
40
- "[",
41
- indent(
42
- concat([
43
- softline,
44
- join(concat([",", line]), path.call(print, "body", 1))
45
- ])
46
- ),
47
- concat([softline, "]"])
48
- ])
44
+ // Finally, verify that the string doesn't contain a space, an escape
45
+ // character, or brackets so that we know it can be put into a string
46
+ // literal array.
47
+ return !/[\s\\[\]]/.test(part.body);
48
+ })
49
49
  );
50
-
51
- const printSpecialArray = (parts) =>
52
- group(
50
+ }
51
+
52
+ // Checks that every argument within this args node is a symbol_literal node (as
53
+ // opposed to a dyna_symbol) so it has no interpolation. This means we're
54
+ // dealing with an array that looks something like:
55
+ //
56
+ // [:a, :b, :c]
57
+ //
58
+ function isSymbolArray(args) {
59
+ return (
60
+ args.body.length > 1 &&
61
+ args.body.every((arg) => arg.type === "symbol_literal" && !arg.comments)
62
+ );
63
+ }
64
+
65
+ // Prints out a word that is a part of a special array literal that accepts
66
+ // interpolation. The body is an array of either plain strings or interpolated
67
+ // expressions.
68
+ function printSpecialArrayWord(path, opts, print) {
69
+ return concat(path.map(print, "body"));
70
+ }
71
+
72
+ // Prints out a special array literal. Accepts the parts of the array literal as
73
+ // an argument, where the first element of the parts array is a string that
74
+ // contains the special start.
75
+ function printSpecialArrayParts(parts) {
76
+ return group(
53
77
  concat([
54
78
  parts[0],
55
79
  "[",
@@ -57,110 +81,99 @@ const printSpecialArray = (parts) =>
57
81
  concat([softline, "]"])
58
82
  ])
59
83
  );
84
+ }
85
+
86
+ // Generates a print function with an embedded special start character for the
87
+ // specific type of array literal that we're dealing with. The print function
88
+ // returns an array as it expects to eventually be handed off to
89
+ // printSpecialArrayParts.
90
+ function printSpecialArray(start) {
91
+ return function printSpecialArrayWithStart(path, opts, print) {
92
+ return [start].concat(path.map(print, "body"));
93
+ };
94
+ }
95
+
96
+ function printEmptyArrayWithComments(path, opts) {
97
+ const arrayNode = path.getValue();
98
+
99
+ const printComment = (commentPath, index) => {
100
+ arrayNode.comments[index].printed = true;
101
+ return opts.printer.printComment(commentPath);
102
+ };
103
+
104
+ return concat([
105
+ "[",
106
+ indent(
107
+ concat([hardline, join(hardline, path.map(printComment, "comments"))])
108
+ ),
109
+ line,
110
+ "]"
111
+ ]);
112
+ }
113
+
114
+ // An array node is any literal array in Ruby. This includes all of the special
115
+ // array literals as well as regular arrays. If it is a special array literal
116
+ // then it will have one child that represents the special array, otherwise it
117
+ // will have one child that contains all of the elements of the array.
118
+ function printArray(path, opts, print) {
119
+ const array = path.getValue();
120
+ const args = array.body[0];
121
+
122
+ // If there is no inner arguments node, then we're dealing with an empty
123
+ // array, so we can go ahead and return.
124
+ if (args === null) {
125
+ return array.comments ? printEmptyArrayWithComments(path, opts) : "[]";
126
+ }
60
127
 
61
- // Extract out the actual elements, taking into account nesting with
62
- // `args_add_star` nodes. The only nodes that get passed into this function are
63
- // `args` or `args_add_star`.
64
- const getElements = (node, elementPath) => {
65
- if (node.type === "args") {
66
- return node.body.map((element, index) => ({
67
- element,
68
- elementPath: elementPath.concat(["body", index])
69
- }));
128
+ // If we have an array that contains only simple string literals with no
129
+ // spaces or interpolation, then we're going to print a %w array.
130
+ if (opts.rubyArrayLiteral && isStringArray(args)) {
131
+ const printString = (stringPath) => stringPath.call(print, "body", 0);
132
+ const parts = path.map(printString, "body", 0, "body");
133
+
134
+ return printSpecialArrayParts(["%w"].concat(parts));
135
+ }
136
+
137
+ // If we have an array that contains only simple symbol literals with no
138
+ // interpolation, then we're going to print a %i array.
139
+ if (opts.rubyArrayLiteral && isSymbolArray(args)) {
140
+ const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
141
+ const parts = path.map(printSymbol, "body", 0, "body");
142
+
143
+ return printSpecialArrayParts(["%i"].concat(parts));
70
144
  }
71
145
 
72
- return getElements(node.body[0], elementPath.concat(["body", 0])).concat(
73
- node.body.slice(1).map((element, index) => ({
74
- element,
75
- elementPath: elementPath.concat(["body", index + 1])
76
- }))
146
+ // If we don't have a regular args node at this point then we have a special
147
+ // array literal. In that case we're going to print out the body (which will
148
+ // return to us an array with the first one being the start of the array) and
149
+ // send that over to the printSpecialArrayParts function.
150
+ if (!["args", "args_add_star"].includes(args.type)) {
151
+ return printSpecialArrayParts(path.call(print, "body", 0));
152
+ }
153
+
154
+ // Here we have a normal array of any type of object with no special literal
155
+ // types or anything.
156
+ return group(
157
+ concat([
158
+ "[",
159
+ indent(
160
+ concat([
161
+ softline,
162
+ join(concat([",", line]), path.call(print, "body", 0)),
163
+ getTrailingComma(opts) ? ifBreak(",", "") : ""
164
+ ])
165
+ ),
166
+ softline,
167
+ "]"
168
+ ])
77
169
  );
78
- };
170
+ }
79
171
 
80
172
  module.exports = {
81
- aref: (path, opts, print) => {
82
- if (!path.getValue().body[1]) {
83
- return concat([path.call(print, "body", 0), "[]"]);
84
- }
85
-
86
- return printAref(path, opts, print);
87
- },
88
- aref_field: printAref,
89
- array: (path, { addTrailingCommas }, print) => {
90
- const args = path.getValue().body[0];
91
-
92
- if (args === null) {
93
- return "[]";
94
- }
95
-
96
- if (isStringArray(args)) {
97
- return printSpecialArray(
98
- ["%w"].concat(getSpecialArrayParts(path, print, args))
99
- );
100
- }
101
-
102
- if (isSymbolArray(args)) {
103
- return printSpecialArray(
104
- ["%i"].concat(getSpecialArrayParts(path, print, args))
105
- );
106
- }
107
-
108
- if (!["args", "args_add_star"].includes(args.type)) {
109
- return printSpecialArray(path.call(print, "body", 0));
110
- }
111
-
112
- const normalDocs = [];
113
-
114
- const elementDocs = path.call(print, "body", 0);
115
- const elements = getElements(path.getValue().body[0], ["body", 0]);
116
-
117
- // We need to manually loop through the elements in the array in order to
118
- // take care of heredocs printing (their commas go after the opening, as
119
- // opposed to at the end).
120
- elements.forEach(({ element, elementPath }, index) => {
121
- const isInner = index !== elements.length - 1;
122
-
123
- const isStraightHeredoc = element.type === "heredoc";
124
- const isSquigglyHeredoc =
125
- element.type === "string_literal" && element.body[0].type === "heredoc";
126
-
127
- if (isStraightHeredoc || isSquigglyHeredoc) {
128
- const heredocNode = isStraightHeredoc ? element : element.body[0];
129
- const heredocPath = [print].concat(elementPath);
130
-
131
- if (isSquigglyHeredoc) {
132
- heredocPath.push("body", 0);
133
- }
134
-
135
- normalDocs.push(
136
- heredocNode.beging,
137
- isInner || addTrailingCommas ? "," : "",
138
- literalline,
139
- concat(path.map.apply(path, heredocPath.concat("body"))),
140
- heredocNode.ending,
141
- isInner ? line : ""
142
- );
143
- } else {
144
- normalDocs.push(elementDocs[index]);
145
-
146
- if (isInner) {
147
- normalDocs.push(concat([",", line]));
148
- } else if (addTrailingCommas) {
149
- normalDocs.push(ifBreak(",", ""));
150
- }
151
- }
152
- });
153
-
154
- return group(
155
- concat([
156
- "[",
157
- indent(concat([softline].concat(normalDocs))),
158
- concat([softline, "]"])
159
- ])
160
- );
161
- },
162
- qsymbols: makeArray("%i"),
163
- qwords: makeArray("%w"),
164
- symbols: makeArray("%I"),
165
- words: makeArray("%W")
173
+ array: printArray,
174
+ qsymbols: printSpecialArray("%i"),
175
+ qwords: printSpecialArray("%w"),
176
+ symbols: printSpecialArray("%I"),
177
+ word: printSpecialArrayWord,
178
+ words: printSpecialArray("%W")
166
179
  };