prettier 0.20.0 → 1.0.0.pre.rc2

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.
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
  };