prettier 0.21.0 → 1.0.1

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -7
  3. data/CONTRIBUTING.md +4 -4
  4. data/README.md +18 -14
  5. data/package.json +9 -6
  6. data/src/embed.js +27 -8
  7. data/src/nodes.js +5 -2
  8. data/src/nodes/alias.js +29 -31
  9. data/src/nodes/aref.js +26 -26
  10. data/src/nodes/args.js +55 -47
  11. data/src/nodes/arrays.js +132 -106
  12. data/src/nodes/assign.js +32 -32
  13. data/src/nodes/blocks.js +8 -3
  14. data/src/nodes/calls.js +163 -60
  15. data/src/nodes/case.js +11 -7
  16. data/src/nodes/class.js +74 -0
  17. data/src/nodes/commands.js +36 -31
  18. data/src/nodes/conditionals.js +44 -30
  19. data/src/nodes/constants.js +39 -21
  20. data/src/nodes/flow.js +11 -1
  21. data/src/nodes/hashes.js +90 -109
  22. data/src/nodes/heredocs.js +34 -0
  23. data/src/nodes/hooks.js +21 -22
  24. data/src/nodes/ints.js +27 -20
  25. data/src/nodes/lambdas.js +14 -27
  26. data/src/nodes/loops.js +10 -5
  27. data/src/nodes/massign.js +87 -65
  28. data/src/nodes/methods.js +48 -73
  29. data/src/nodes/operators.js +70 -39
  30. data/src/nodes/params.js +26 -16
  31. data/src/nodes/patterns.js +108 -33
  32. data/src/nodes/regexp.js +45 -14
  33. data/src/nodes/rescue.js +72 -59
  34. data/src/nodes/statements.js +86 -44
  35. data/src/nodes/strings.js +95 -85
  36. data/src/nodes/super.js +35 -0
  37. data/src/nodes/undef.js +42 -0
  38. data/src/parser.js +86 -0
  39. data/src/parser.rb +2400 -621
  40. data/src/printer.js +90 -0
  41. data/src/ruby.js +19 -41
  42. data/src/toProc.js +4 -4
  43. data/src/utils.js +24 -88
  44. data/src/utils/literalLineNoBreak.js +7 -0
  45. data/src/utils/printEmptyCollection.js +42 -0
  46. metadata +12 -49
  47. data/src/nodes/scopes.js +0 -61
  48. data/src/parse.js +0 -37
  49. data/src/print.js +0 -23
@@ -1,17 +1,17 @@
1
1
  const { concat, group, indent, join, line, softline } = require("../prettier");
2
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
- */
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
15
  function printAref(path, opts, print) {
16
16
  const indexNode = path.getValue().body[1];
17
17
 
@@ -22,20 +22,20 @@ function printAref(path, opts, print) {
22
22
  return printArefField(path, opts, print);
23
23
  }
24
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
- */
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
39
  function printArefField(path, opts, print) {
40
40
  const [printedArray, printedIndex] = path.map(print, "body");
41
41
 
@@ -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
 
@@ -5,36 +5,74 @@ const {
5
5
  indent,
6
6
  join,
7
7
  line,
8
- literalline,
9
8
  softline
10
9
  } = require("../prettier");
11
10
 
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
- );
11
+ const { getTrailingComma, printEmptyCollection } = require("../utils");
12
+
13
+ // Checks that every argument within this args node is a string_literal node
14
+ // that has no spaces or interpolations. This means we're dealing with an array
15
+ // that looks something like:
16
+ //
17
+ // ['a', 'b', 'c']
18
+ //
19
+ function isStringArray(args) {
20
+ return (
21
+ args.body.length > 1 &&
22
+ args.body.every((arg) => {
23
+ // We want to verify that every node inside of this array is a string
24
+ // literal. We also want to make sure none of them have comments attached.
25
+ if (arg.type !== "string_literal" || arg.comments) {
26
+ return false;
27
+ }
24
28
 
25
- const isSymbolArray = (args) =>
26
- args.body.every((arg) => arg.type === "symbol_literal");
29
+ // If the string has multiple parts (meaning plain string content but also
30
+ // interpolated content) then we know it's not a simple string.
31
+ if (arg.body.length !== 1) {
32
+ return false;
33
+ }
27
34
 
28
- const makeArray = (start) => (path, opts, print) =>
29
- [start].concat(path.map(print, "body"));
35
+ const part = arg.body[0];
30
36
 
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
- );
37
+ // If the only part of this string is not @tstring_content then it's
38
+ // interpolated, so again we can return false.
39
+ if (part.type !== "@tstring_content") {
40
+ return false;
41
+ }
35
42
 
36
- const printSpecialArray = (parts) =>
37
- group(
43
+ // Finally, verify that the string doesn't contain a space, an escape
44
+ // character, or brackets so that we know it can be put into a string
45
+ // literal array.
46
+ return !/[\s\\[\]]/.test(part.body);
47
+ })
48
+ );
49
+ }
50
+
51
+ // Checks that every argument within this args node is a symbol_literal node (as
52
+ // opposed to a dyna_symbol) so it has no interpolation. This means we're
53
+ // dealing with an array that looks something like:
54
+ //
55
+ // [:a, :b, :c]
56
+ //
57
+ function isSymbolArray(args) {
58
+ return (
59
+ args.body.length > 1 &&
60
+ args.body.every((arg) => arg.type === "symbol_literal" && !arg.comments)
61
+ );
62
+ }
63
+
64
+ // Prints out a word that is a part of a special array literal that accepts
65
+ // interpolation. The body is an array of either plain strings or interpolated
66
+ // expressions.
67
+ function printSpecialArrayWord(path, opts, print) {
68
+ return concat(path.map(print, "body"));
69
+ }
70
+
71
+ // Prints out a special array literal. Accepts the parts of the array literal as
72
+ // an argument, where the first element of the parts array is a string that
73
+ // contains the special start.
74
+ function printSpecialArrayParts(parts) {
75
+ return group(
38
76
  concat([
39
77
  parts[0],
40
78
  "[",
@@ -42,93 +80,81 @@ const printSpecialArray = (parts) =>
42
80
  concat([softline, "]"])
43
81
  ])
44
82
  );
83
+ }
84
+
85
+ // Generates a print function with an embedded special start character for the
86
+ // specific type of array literal that we're dealing with. The print function
87
+ // returns an array as it expects to eventually be handed off to
88
+ // printSpecialArrayParts.
89
+ function printSpecialArray(start) {
90
+ return function printSpecialArrayWithStart(path, opts, print) {
91
+ return [start].concat(path.map(print, "body"));
92
+ };
93
+ }
94
+
95
+ // An array node is any literal array in Ruby. This includes all of the special
96
+ // array literals as well as regular arrays. If it is a special array literal
97
+ // then it will have one child that represents the special array, otherwise it
98
+ // will have one child that contains all of the elements of the array.
99
+ function printArray(path, opts, print) {
100
+ const array = path.getValue();
101
+ const args = array.body[0];
102
+
103
+ // If there is no inner arguments node, then we're dealing with an empty
104
+ // array, so we can go ahead and return.
105
+ if (args === null) {
106
+ return printEmptyCollection(path, opts, "[", "]");
107
+ }
45
108
 
46
- // Extract out the actual elements, taking into account nesting with
47
- // `args_add_star` nodes. The only nodes that get passed into this function are
48
- // `args` or `args_add_star`.
49
- const getElements = (node, elementPath) => {
50
- if (node.type === "args") {
51
- return node.body.map((element, index) => ({
52
- element,
53
- elementPath: elementPath.concat(["body", index])
54
- }));
109
+ // If we have an array that contains only simple string literals with no
110
+ // spaces or interpolation, then we're going to print a %w array.
111
+ if (opts.rubyArrayLiteral && isStringArray(args)) {
112
+ const printString = (stringPath) => stringPath.call(print, "body", 0);
113
+ const parts = path.map(printString, "body", 0, "body");
114
+
115
+ return printSpecialArrayParts(["%w"].concat(parts));
55
116
  }
56
117
 
57
- return getElements(node.body[0], elementPath.concat(["body", 0])).concat(
58
- node.body.slice(1).map((element, index) => ({
59
- element,
60
- elementPath: elementPath.concat(["body", index + 1])
61
- }))
118
+ // If we have an array that contains only simple symbol literals with no
119
+ // interpolation, then we're going to print a %i array.
120
+ if (opts.rubyArrayLiteral && isSymbolArray(args)) {
121
+ const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
122
+ const parts = path.map(printSymbol, "body", 0, "body");
123
+
124
+ return printSpecialArrayParts(["%i"].concat(parts));
125
+ }
126
+
127
+ // If we don't have a regular args node at this point then we have a special
128
+ // array literal. In that case we're going to print out the body (which will
129
+ // return to us an array with the first one being the start of the array) and
130
+ // send that over to the printSpecialArrayParts function.
131
+ if (!["args", "args_add_star"].includes(args.type)) {
132
+ return printSpecialArrayParts(path.call(print, "body", 0));
133
+ }
134
+
135
+ // Here we have a normal array of any type of object with no special literal
136
+ // types or anything.
137
+ return group(
138
+ concat([
139
+ "[",
140
+ indent(
141
+ concat([
142
+ softline,
143
+ join(concat([",", line]), path.call(print, "body", 0)),
144
+ getTrailingComma(opts) ? ifBreak(",", "") : ""
145
+ ])
146
+ ),
147
+ softline,
148
+ "]"
149
+ ])
62
150
  );
63
- };
151
+ }
64
152
 
65
153
  module.exports = {
66
- array: (path, { addTrailingCommas }, print) => {
67
- const args = path.getValue().body[0];
68
-
69
- if (args === null) {
70
- return "[]";
71
- }
72
-
73
- if (isStringArray(args)) {
74
- return printSpecialArray(
75
- ["%w"].concat(getSpecialArrayParts(path, print, args))
76
- );
77
- }
78
-
79
- if (isSymbolArray(args)) {
80
- return printSpecialArray(
81
- ["%i"].concat(getSpecialArrayParts(path, print, args))
82
- );
83
- }
84
-
85
- if (!["args", "args_add_star"].includes(args.type)) {
86
- return printSpecialArray(path.call(print, "body", 0));
87
- }
88
-
89
- const normalDocs = [];
90
-
91
- const elementDocs = path.call(print, "body", 0);
92
- const elements = getElements(path.getValue().body[0], ["body", 0]);
93
-
94
- // We need to manually loop through the elements in the array in order to
95
- // take care of heredocs printing (their commas go after the opening, as
96
- // opposed to at the end).
97
- elements.forEach(({ element, elementPath }, index) => {
98
- const isInner = index !== elements.length - 1;
99
-
100
- if (element.type === "heredoc") {
101
- normalDocs.push(
102
- element.beging,
103
- isInner || addTrailingCommas ? "," : "",
104
- literalline,
105
- concat(
106
- path.map.apply(path, [print].concat(elementPath).concat("body"))
107
- ),
108
- element.ending,
109
- isInner ? line : ""
110
- );
111
- } else {
112
- normalDocs.push(elementDocs[index]);
113
-
114
- if (isInner) {
115
- normalDocs.push(concat([",", line]));
116
- } else if (addTrailingCommas) {
117
- normalDocs.push(ifBreak(",", ""));
118
- }
119
- }
120
- });
121
-
122
- return group(
123
- concat([
124
- "[",
125
- indent(concat([softline].concat(normalDocs))),
126
- concat([softline, "]"])
127
- ])
128
- );
129
- },
130
- qsymbols: makeArray("%i"),
131
- qwords: makeArray("%w"),
132
- symbols: makeArray("%I"),
133
- words: makeArray("%W")
154
+ array: printArray,
155
+ qsymbols: printSpecialArray("%i"),
156
+ qwords: printSpecialArray("%w"),
157
+ symbols: printSpecialArray("%I"),
158
+ word: printSpecialArrayWord,
159
+ words: printSpecialArray("%W")
134
160
  };