prettier 1.0.0.pre.rc1 → 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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2bdbafe8bc7bd8ebc0f77727584bfa80773721ca5e77f820df84b8e7c7e0951
4
- data.tar.gz: 53e157e6880b56e642690fb90f6244ad669739a2e5fb697cde0c8082facc2c20
3
+ metadata.gz: 305f0248dc0f0b4ab56d698f04858e3d25df0f91888a203f75740ae5a06a7e0d
4
+ data.tar.gz: 192eaf1d3cd95a9dc3693c0346f636f53ae2baede4cd3de5d827bd671571daf3
5
5
  SHA512:
6
- metadata.gz: 854c61aa25de0a708606f89cb4bd3dde7dee10ecec89cfc7660bf03722ea12603bbb4dbf5f8d8b25f14359de55b419f2758522198cae9926d03fe77fab7aa001
7
- data.tar.gz: d9c821b5367084de39bbe51c60a58642210caa5da04757c6fafbabec6e204adfdd9d7b656e34c3a94e50af26d5c6ceda9236edf68e96c98ed1e941957fe47c4a
6
+ metadata.gz: 33a09aa76044b30dde5aa1555bfabc8f178607335ca0af9e4709d0f9fbfdc437c986097da85cc2d0c411326cb25d0672587a0253016bc89e71ca1245a5cfd652
7
+ data.tar.gz: b293f495c164808c4372dc79cfa801fd1603fe4907eafb374b61ba171cffbf1aaa09b6e0b1a427bc949099091db790a79d74d56c50f72c3ba006acc718b0065c
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.0.0-rc2] - 2020-12-10
10
+
11
+ ### Changed
12
+
13
+ - [@kddeisz] - Print hashes with consistent keys (e.g., if one key cannot be a hash label, use all hash rockets).
14
+ - [@kddeisz] - Respect using `o` or not using `o` for octal numbers.
15
+ - [@kddeisz] - Ensure `when` clauses with multiple predicates that can be split into multiple lines are split correctly.
16
+ - [@kddeisz] - Ensure hash literal is split correctly when only its contents would fit on one line.
17
+ - [@kddeisz] - Simplify `toProc` checks by not calling if the option is disabled.
18
+ - [@johncsnyder], [@kddeisz] - Add `method_add_block` to the potential like of method calls that can be chained.
19
+ - [@kddeisz] - Add the `rubyArrayLiteral` option for disabling automatically turning into array literals.
20
+
9
21
  ## [1.0.0-rc1] - 2020-12-09
10
22
 
11
23
  ### Changed
@@ -928,7 +940,8 @@ would previously result in `array[]`, but now prints properly.
928
940
 
929
941
  - Initial release 🎉
930
942
 
931
- [unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.0.0-rc1...HEAD
943
+ [unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.0.0-rc2...HEAD
944
+ [1.0.0-rc2]: https://github.com/prettier/plugin-ruby/compare/v1.0.0-rc1...v1.0.0-rc2
932
945
  [1.0.0-rc1]: https://github.com/prettier/plugin-ruby/compare/v0.22.0...v1.0.0-rc1
933
946
  [0.22.0]: https://github.com/prettier/plugin-ruby/compare/v0.21.0...v0.22.0
934
947
  [0.21.0]: https://github.com/prettier/plugin-ruby/compare/v0.20.1...v0.21.0
data/README.md CHANGED
@@ -122,16 +122,17 @@ The `prettier` executable is now installed and ready for use:
122
122
 
123
123
  Below are the options (from [`src/ruby.js`](src/ruby.js)) that `@prettier/plugin-ruby` currently supports:
124
124
 
125
- | API Option | CLI Option | Default | Description |
126
- | ----------------- | --------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------ |
127
- | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
128
- | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
129
- | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
130
- | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
131
- | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
132
- | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
133
- | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
134
- | `trailingComma` | `--trailing-comma` | `"es5"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
125
+ | API Option | CLI Option | Default | Description |
126
+ | ------------------ | ---------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------ |
127
+ | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
128
+ | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
129
+ | `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
130
+ | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
131
+ | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
132
+ | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
133
+ | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
134
+ | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
135
+ | `trailingComma` | `--trailing-comma` | `"es5"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
135
136
 
136
137
  Any of these can be added to your existing [prettier configuration
137
138
  file](https://prettier.io/docs/en/configuration.html). For example:
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prettier/plugin-ruby",
3
- "version": "1.0.0-rc1",
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": {
@@ -67,7 +67,11 @@ const embed = (path, print, textToDoc, _opts) => {
67
67
  path.call(print, "beging"),
68
68
  lineSuffix(
69
69
  group(
70
- concat([indent(markAsRoot(formatted)), literalLineNoBreak, ending])
70
+ concat([
71
+ indent(markAsRoot(formatted)),
72
+ literalLineNoBreak,
73
+ ending.trim()
74
+ ])
71
75
  )
72
76
  )
73
77
  ]);
@@ -78,7 +82,7 @@ const embed = (path, print, textToDoc, _opts) => {
78
82
  return markAsRoot(
79
83
  concat([
80
84
  path.call(print, "beging"),
81
- lineSuffix(group(concat([formatted, literalLineNoBreak, ending])))
85
+ lineSuffix(group(concat([formatted, literalLineNoBreak, ending.trim()])))
82
86
  ])
83
87
  );
84
88
  };
@@ -9,80 +9,56 @@ const {
9
9
  } = require("../prettier");
10
10
 
11
11
  const toProc = require("../toProc");
12
- const { docLength, getTrailingComma } = require("../utils");
13
-
14
- module.exports = {
15
- arg_paren: (path, opts, print) => {
16
- const argsNode = path.getValue().body[0];
17
-
18
- if (argsNode === null) {
19
- return "";
20
- }
21
-
22
- // Here we can skip the entire rest of the method by just checking if it's
23
- // an args_forward node, as we're guaranteed that there are no other arg
24
- // nodes.
25
- if (argsNode.type === "args_forward") {
26
- return group(
12
+ const { getTrailingComma } = require("../utils");
13
+
14
+ function printArgParen(path, opts, print) {
15
+ const argsNode = path.getValue().body[0];
16
+
17
+ if (argsNode === null) {
18
+ return "";
19
+ }
20
+
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(
26
+ concat([
27
+ "(",
28
+ indent(concat([softline, path.call(print, "body", 0)])),
29
+ softline,
30
+ ")"
31
+ ])
32
+ );
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(
27
44
  concat([
28
- "(",
29
- indent(concat([softline, path.call(print, "body", 0)])),
30
45
  softline,
31
- ")"
46
+ join(concat([",", line]), args),
47
+ getTrailingComma(opts) && !hasBlock ? ifBreak(",", "") : ""
32
48
  ])
33
- );
34
- }
35
-
36
- const args = path.call(print, "body", 0);
37
- const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
38
-
39
- // These are the docs representing the actual arguments, without any
40
- // parentheses or surrounding lines yet added.
41
- let argsDocs = [
42
- join(concat([",", line]), args),
43
- getTrailingComma(opts) && !hasBlock ? ifBreak(",", "") : ""
44
- ];
45
-
46
- // Here we're going to make a determination on whether or not we should put
47
- // a newline before the first argument. In some cases this makes the
48
- // appearance a little better. For example, instead of taking this input:
49
- //
50
- // foo(arg1, arg2).bar(arg1, arg2).baz(arg1)
51
- //
52
- // and transforming it into this:
53
- //
54
- // foo(arg1, arg2).bar(arg1, arg2).baz(
55
- // arg1
56
- // )
57
- //
58
- // it instead gets transformed into this:
59
- //
60
- // foo(arg1, arg2).bar(arg1, arg2)
61
- // .baz(arg1)
62
- //
63
- const maxDocLength = 15;
64
- const firstArgDoc = args[0];
65
-
66
- // prettier-ignore
67
- const shouldWrapLine =
68
- (args.reduce((sum, arg) => sum + docLength(arg), 0) > maxDocLength) ||
69
- (args.length == 1 && firstArgDoc.type === "group" && docLength(firstArgDoc) > maxDocLength) ||
70
- (firstArgDoc.type === "concat" && firstArgDoc.parts.some((part) => part.type === "group"));
71
-
72
- // Here we're going to get all of the docs representing the doc that's
73
- // inside the parentheses.
74
- if (shouldWrapLine) {
75
- argsDocs = [indent(concat([softline].concat(argsDocs))), softline];
76
- } else {
77
- argsDocs = [indent(concat(argsDocs))];
78
- }
79
-
80
- // Now here we return a doc that represents the whole grouped expression,
81
- // including the surrouding parentheses.
82
- return group(concat(["("].concat(argsDocs).concat(")")));
83
- },
84
- args: (path, opts, print) => {
85
- const args = path.map(print, "body");
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) {
86
62
  let blockNode = null;
87
63
 
88
64
  // Look up the chain to see if these arguments are contained within a
@@ -99,21 +75,26 @@ module.exports = {
99
75
  return blockNode;
100
76
  });
101
77
 
102
- const proc = blockNode && toProc(path, opts, blockNode);
78
+ const proc = blockNode && toProc(path, blockNode);
103
79
 
104
- // If we have a successful to_proc transformation, but we're part of an aref
105
- // 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
106
82
  //
107
83
  // foo[:bar].each(&:to_s)
108
84
  //
109
- // In this case we need to just return regular arguments, otherwise we would
110
- // 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.
111
87
  if (proc && path.getParentNode(1).type !== "aref") {
112
88
  args.push(proc);
113
89
  }
90
+ }
114
91
 
115
- return args;
116
- },
92
+ return args;
93
+ }
94
+
95
+ module.exports = {
96
+ arg_paren: printArgParen,
97
+ args: printArgs,
117
98
  args_add_block: (path, opts, print) => {
118
99
  const parts = path.call(print, "body", 0);
119
100
 
@@ -18,32 +18,35 @@ const { getTrailingComma } = require("../utils");
18
18
  // ['a', 'b', 'c']
19
19
  //
20
20
  function isStringArray(args) {
21
- return args.body.every((arg) => {
22
- // We want to verify that every node inside of this array is a string
23
- // literal. We also want to make sure none of them have comments attached.
24
- if (arg.type !== "string_literal" || arg.comments) {
25
- return false;
26
- }
27
-
28
- // If the string has multiple parts (meaning plain string content but also
29
- // interpolated content) then we know it's not a simple string.
30
- if (arg.body.length !== 1) {
31
- return false;
32
- }
33
-
34
- const part = arg.body[0];
35
-
36
- // If the only part of this string is not @tstring_content then it's
37
- // interpolated, so again we can return false.
38
- if (part.type !== "@tstring_content") {
39
- return false;
40
- }
41
-
42
- // Finally, verify that the string doesn't contain a space, an escape
43
- // character, or brackets so that we know it can be put into a string
44
- // literal array.
45
- return !/[\s\\[\]]/.test(part.body);
46
- });
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
+ }
29
+
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
+ }
35
+
36
+ const part = arg.body[0];
37
+
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
+ }
43
+
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
+ );
47
50
  }
48
51
 
49
52
  // Checks that every argument within this args node is a symbol_literal node (as
@@ -53,8 +56,9 @@ function isStringArray(args) {
53
56
  // [:a, :b, :c]
54
57
  //
55
58
  function isSymbolArray(args) {
56
- return args.body.every(
57
- (arg) => arg.type === "symbol_literal" && !arg.comments
59
+ return (
60
+ args.body.length > 1 &&
61
+ args.body.every((arg) => arg.type === "symbol_literal" && !arg.comments)
58
62
  );
59
63
  }
60
64
 
@@ -123,7 +127,7 @@ function printArray(path, opts, print) {
123
127
 
124
128
  // If we have an array that contains only simple string literals with no
125
129
  // spaces or interpolation, then we're going to print a %w array.
126
- if (isStringArray(args)) {
130
+ if (opts.rubyArrayLiteral && isStringArray(args)) {
127
131
  const printString = (stringPath) => stringPath.call(print, "body", 0);
128
132
  const parts = path.map(printString, "body", 0, "body");
129
133
 
@@ -132,7 +136,7 @@ function printArray(path, opts, print) {
132
136
 
133
137
  // If we have an array that contains only simple symbol literals with no
134
138
  // interpolation, then we're going to print a %i array.
135
- if (isSymbolArray(args)) {
139
+ if (opts.rubyArrayLiteral && isSymbolArray(args)) {
136
140
  const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
137
141
  const parts = path.map(printSymbol, "body", 0, "body");
138
142
 
@@ -1,5 +1,5 @@
1
1
  const { concat, group, indent, join, line } = require("../prettier");
2
- const { concatBody, first, skipAssignIndent } = require("../utils");
2
+ const { first, skipAssignIndent } = require("../utils");
3
3
 
4
4
  function printAssign(path, opts, print) {
5
5
  const [_targetNode, valueNode] = path.getValue().body;
@@ -34,6 +34,6 @@ function printOpAssign(path, opts, print) {
34
34
  module.exports = {
35
35
  assign: printAssign,
36
36
  opassign: printOpAssign,
37
- var_field: concatBody,
37
+ var_field: first,
38
38
  var_ref: first
39
39
  };
@@ -6,15 +6,15 @@ const {
6
6
  indent,
7
7
  softline
8
8
  } = require("../prettier");
9
- const { concatBody, first, makeCall, noIndent } = require("../utils");
9
+ const { first, makeCall, noIndent } = require("../utils");
10
10
 
11
11
  const toProc = require("../toProc");
12
12
 
13
- const chained = ["call", "method_add_arg"];
13
+ const chained = ["call", "method_add_arg", "method_add_block"];
14
14
 
15
15
  function printCall(path, opts, print) {
16
- const callNode = path.getValue();
17
- const [receiverNode, _operatorNode, messageNode] = callNode.body;
16
+ const node = path.getValue();
17
+ const [receiverNode, _operatorNode, messageNode] = node.body;
18
18
 
19
19
  const receiverDoc = path.call(print, "body", 0);
20
20
  const operatorDoc = makeCall(path, opts, print);
@@ -24,21 +24,13 @@ function printCall(path, opts, print) {
24
24
  // call syntax so if `call` is implicit, we don't print it out.
25
25
  const messageDoc = messageNode === "call" ? "" : path.call(print, "body", 2);
26
26
 
27
- // For certain left sides of the call nodes, we want to attach directly to
28
- // the } or end.
29
- if (noIndent.includes(receiverNode.type)) {
30
- return concat([receiverDoc, operatorDoc, messageDoc]);
31
- }
32
-
33
27
  // The right side of the call node, as in everything including the operator
34
28
  // and beyond.
35
- const rightSideDoc = indent(
36
- concat([
37
- receiverNode.comments ? hardline : softline,
38
- operatorDoc,
39
- messageDoc
40
- ])
41
- );
29
+ const rightSideDoc = concat([
30
+ receiverNode.comments ? hardline : softline,
31
+ operatorDoc,
32
+ messageDoc
33
+ ]);
42
34
 
43
35
  // Get a reference to the parent node so we can check if we're inside a chain
44
36
  const parentNode = path.getParentNode();
@@ -46,27 +38,31 @@ function printCall(path, opts, print) {
46
38
  // If our parent node is a chained node then we're not going to group the
47
39
  // right side of the expression, as we want to have a nice multi-line layout.
48
40
  if (chained.includes(parentNode.type)) {
49
- parentNode.chain = (callNode.chain || 0) + 1;
50
- parentNode.breakDoc = (callNode.breakDoc || [receiverDoc]).concat(
51
- rightSideDoc
52
- );
41
+ parentNode.chain = (node.chain || 0) + 1;
42
+ parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc);
53
43
  }
54
44
 
55
45
  // If we're at the top of a chain, then we're going to print out a nice
56
46
  // multi-line layout if this doesn't break into multiple lines.
57
- if (!chained.includes(parentNode.type) && (callNode.chain || 0) >= 3) {
47
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
58
48
  return ifBreak(
59
- group(concat(callNode.breakDoc.concat(rightSideDoc))),
49
+ group(indent(concat(node.breakDoc.concat(rightSideDoc)))),
60
50
  concat([receiverDoc, group(rightSideDoc)])
61
51
  );
62
52
  }
63
53
 
64
- return group(concat([receiverDoc, group(rightSideDoc)]));
54
+ // For certain left sides of the call nodes, we want to attach directly to
55
+ // the } or end.
56
+ if (noIndent.includes(receiverNode.type)) {
57
+ return concat([receiverDoc, operatorDoc, messageDoc]);
58
+ }
59
+
60
+ return group(concat([receiverDoc, group(indent(rightSideDoc))]));
65
61
  }
66
62
 
67
63
  function printMethodAddArg(path, opts, print) {
68
- const methodAddArgNode = path.getValue();
69
- const argNode = methodAddArgNode.body[1];
64
+ const node = path.getValue();
65
+ const argNode = node.body[1];
70
66
 
71
67
  const [methodDoc, argsDoc] = path.map(print, "body");
72
68
 
@@ -88,20 +84,15 @@ function printMethodAddArg(path, opts, print) {
88
84
  // If our parent node is a chained node then we're not going to group the
89
85
  // right side of the expression, as we want to have a nice multi-line layout.
90
86
  if (chained.includes(parentNode.type)) {
91
- parentNode.chain = (methodAddArgNode.chain || 0) + 1;
92
- parentNode.breakDoc = (methodAddArgNode.breakDoc || [methodDoc]).concat(
93
- argsDoc
94
- );
87
+ parentNode.chain = (node.chain || 0) + 1;
88
+ parentNode.breakDoc = (node.breakDoc || [methodDoc]).concat(argsDoc);
95
89
  }
96
90
 
97
91
  // If we're at the top of a chain, then we're going to print out a nice
98
92
  // multi-line layout if this doesn't break into multiple lines.
99
- if (
100
- !chained.includes(parentNode.type) &&
101
- (methodAddArgNode.chain || 0) >= 3
102
- ) {
93
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
103
94
  return ifBreak(
104
- group(concat(methodAddArgNode.breakDoc.concat(argsDoc))),
95
+ group(indent(concat(node.breakDoc.concat(argsDoc)))),
105
96
  concat([methodDoc, argsDoc])
106
97
  );
107
98
  }
@@ -109,15 +100,18 @@ function printMethodAddArg(path, opts, print) {
109
100
  return concat([methodDoc, argsDoc]);
110
101
  }
111
102
 
112
- module.exports = {
113
- call: printCall,
114
- fcall: concatBody,
115
- method_add_arg: printMethodAddArg,
116
- method_add_block: (path, opts, print) => {
117
- const [method, block] = path.getValue().body;
118
- const proc = toProc(path, opts, block);
103
+ function printMethodAddBlock(path, { rubyToProc }, print) {
104
+ const node = path.getValue();
105
+
106
+ const [callNode, blockNode] = node.body;
107
+ const [callDoc, blockDoc] = path.map(print, "body");
119
108
 
120
- if (proc && method.type === "call") {
109
+ // Don't bother trying to do any kind of fancy toProc transform if the option
110
+ // is disabled.
111
+ if (rubyToProc) {
112
+ const proc = toProc(path, blockNode);
113
+
114
+ if (proc && callNode.type === "call") {
121
115
  return group(
122
116
  concat([
123
117
  path.call(print, "body", 0),
@@ -131,8 +125,34 @@ module.exports = {
131
125
  if (proc) {
132
126
  return path.call(print, "body", 0);
133
127
  }
128
+ }
134
129
 
135
- return concat(path.map(print, "body"));
136
- },
130
+ // Get a reference to the parent node so we can check if we're inside a chain
131
+ const parentNode = path.getParentNode();
132
+
133
+ // If our parent node is a chained node then we're not going to group the
134
+ // right side of the expression, as we want to have a nice multi-line layout.
135
+ if (chained.includes(parentNode.type)) {
136
+ parentNode.chain = (node.chain || 0) + 1;
137
+ parentNode.breakDoc = (node.breakDoc || [callDoc]).concat(blockDoc);
138
+ }
139
+
140
+ // If we're at the top of a chain, then we're going to print out a nice
141
+ // multi-line layout if this doesn't break into multiple lines.
142
+ if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
143
+ return ifBreak(
144
+ group(indent(concat(node.breakDoc.concat(blockDoc)))),
145
+ concat([callDoc, blockDoc])
146
+ );
147
+ }
148
+
149
+ return concat([callDoc, blockDoc]);
150
+ }
151
+
152
+ module.exports = {
153
+ call: printCall,
154
+ fcall: first,
155
+ method_add_arg: printMethodAddArg,
156
+ method_add_block: printMethodAddBlock,
137
157
  vcall: first
138
158
  };
@@ -28,13 +28,17 @@ module.exports = {
28
28
  // The `fill` builder command expects an array of docs alternating with
29
29
  // line breaks. This is so it can loop through and determine where to break.
30
30
  const preds = fill(
31
- path
32
- .call(print, "body", 0)
33
- .reduce(
34
- (accum, pred, index) =>
35
- index === 0 ? [pred] : accum.concat([",", line, pred]),
36
- null
37
- )
31
+ path.call(print, "body", 0).reduce((accum, pred, index) => {
32
+ if (index === 0) {
33
+ return [pred];
34
+ }
35
+
36
+ // Pull off the last element and make it concat with a comma so that
37
+ // we can maintain alternating lines and docs.
38
+ return accum
39
+ .slice(0, -1)
40
+ .concat([concat([accum[accum.length - 1], ","]), line, pred]);
41
+ }, null)
38
42
  );
39
43
 
40
44
  const stmts = path.call(print, "body", 1);
@@ -25,35 +25,54 @@ function isValidHashLabel(symbolLiteral) {
25
25
  return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
26
26
  }
27
27
 
28
- function printHashKey(path, { rubyHashLabel }, print) {
29
- const labelNode = path.getValue().body[0];
30
- const labelDoc = path.call(print, "body", 0);
28
+ function canUseHashLabels(contentsNode) {
29
+ return contentsNode.body.every((assocNode) => {
30
+ if (assocNode.type === "assoc_splat") {
31
+ return true;
32
+ }
31
33
 
32
- switch (labelNode.type) {
33
- case "@label":
34
- if (rubyHashLabel) {
35
- return labelDoc;
36
- }
37
- return `:${labelDoc.slice(0, labelDoc.length - 1)} =>`;
38
- case "symbol_literal": {
39
- if (rubyHashLabel && isValidHashLabel(labelNode)) {
40
- return concat([path.call(print, "body", 0, "body", 0), ":"]);
41
- }
42
- return concat([labelDoc, " =>"]);
34
+ switch (assocNode.body[0].type) {
35
+ case "@label":
36
+ return true;
37
+ case "symbol_literal":
38
+ return isValidHashLabel(assocNode.body[0]);
39
+ case "dyna_symbol":
40
+ return true;
41
+ default:
42
+ return false;
43
43
  }
44
+ });
45
+ }
46
+
47
+ function printHashKeyLabel(path, print) {
48
+ const node = path.getValue();
49
+
50
+ switch (node.type) {
51
+ case "@label":
52
+ return print(path);
53
+ case "symbol_literal":
54
+ return concat([path.call(print, "body", 0), ":"]);
44
55
  case "dyna_symbol":
45
- if (rubyHashLabel) {
46
- return concat(labelDoc.parts.slice(1).concat(":"));
47
- }
48
- return concat([labelDoc, " =>"]);
49
- default:
50
- return concat([labelDoc, " =>"]);
56
+ return concat(print(path).parts.slice(1).concat(":"));
51
57
  }
52
58
  }
53
59
 
60
+ function printHashKeyRocket(path, print) {
61
+ const node = path.getValue();
62
+ const doc = print(path);
63
+
64
+ if (node.type === "@label") {
65
+ return `:${doc.slice(0, doc.length - 1)} =>`;
66
+ }
67
+
68
+ return concat([doc, " =>"]);
69
+ }
70
+
54
71
  function printAssocNew(path, opts, print) {
72
+ const { keyPrinter } = path.getParentNode();
73
+
74
+ const parts = [path.call((keyPath) => keyPrinter(keyPath, print), "body", 0)];
55
75
  const valueDoc = path.call(print, "body", 1);
56
- const parts = [printHashKey(path, opts, print)];
57
76
 
58
77
  if (skipAssignIndent(path.getValue().body[1])) {
59
78
  parts.push(" ", valueDoc);
@@ -64,6 +83,44 @@ function printAssocNew(path, opts, print) {
64
83
  return group(concat(parts));
65
84
  }
66
85
 
86
+ function printHashContents(path, opts, print) {
87
+ const node = path.getValue();
88
+
89
+ // First determine which key printer we're going to use, so that the child
90
+ // nodes can reference it when they go to get printed.
91
+ node.keyPrinter =
92
+ opts.rubyHashLabel && canUseHashLabels(path.getValue())
93
+ ? printHashKeyLabel
94
+ : printHashKeyRocket;
95
+
96
+ const contents = join(concat([",", line]), path.map(print, "body"));
97
+
98
+ // If we're inside a hash literal, then we want to add the braces at this
99
+ // level so that the grouping is correct. Otherwise you could end up with
100
+ // opening and closing braces being split up, but the contents not being split
101
+ // correctly.
102
+ if (path.getParentNode().type === "hash") {
103
+ return group(
104
+ concat([
105
+ "{",
106
+ indent(
107
+ concat([
108
+ line,
109
+ contents,
110
+ getTrailingComma(opts) ? ifBreak(",", "") : ""
111
+ ])
112
+ ),
113
+ line,
114
+ "}"
115
+ ])
116
+ );
117
+ }
118
+
119
+ // Otherwise, we're inside a bare_assoc_hash, so we don't want to print
120
+ // braces at all.
121
+ return group(contents);
122
+ }
123
+
67
124
  function printEmptyHashWithComments(path, opts) {
68
125
  const hashNode = path.getValue();
69
126
 
@@ -92,24 +149,7 @@ function printHash(path, opts, print) {
92
149
  return hashNode.comments ? printEmptyHashWithComments(path, opts) : "{}";
93
150
  }
94
151
 
95
- return group(
96
- concat([
97
- "{",
98
- indent(
99
- concat([
100
- line,
101
- path.call(print, "body", 0),
102
- getTrailingComma(opts) ? ifBreak(",", "") : ""
103
- ])
104
- ),
105
- line,
106
- "}"
107
- ])
108
- );
109
- }
110
-
111
- function printHashContents(path, opts, print) {
112
- return group(join(concat([",", line]), path.map(print, "body")));
152
+ return path.call(print, "body", 0);
113
153
  }
114
154
 
115
155
  module.exports = {
@@ -12,12 +12,6 @@
12
12
  function printInt(path, _opts, _print) {
13
13
  const { body } = path.getValue();
14
14
 
15
- // If the number is octal and does not contain the optional "o" character
16
- // after the leading 0, add it in.
17
- if (/^0[0-9]/.test(body)) {
18
- return `0o${body.slice(1)}`;
19
- }
20
-
21
15
  // If the number is a base 10 number, is sufficiently large, and is not
22
16
  // already formatted with underscores, then add them in in between the
23
17
  // numbers every three characters starting from the right.
@@ -1,11 +1,4 @@
1
- const {
2
- concat,
3
- group,
4
- ifBreak,
5
- indent,
6
- line,
7
- softline
8
- } = require("../prettier");
1
+ const { concat, group, ifBreak, indent, line } = require("../prettier");
9
2
  const { hasAncestor } = require("../utils");
10
3
 
11
4
  // We can have our params coming in as the first child of the main lambda node,
@@ -13,31 +6,22 @@ const { hasAncestor } = require("../utils");
13
6
  // though it's possible to omit the parens if you only have one argument, we're
14
7
  // going to keep them in no matter what for consistency.
15
8
  function printLambdaParams(path, print) {
16
- const paramsPath = [print, "body", 0];
17
- let paramsNode = path.getValue().body[0];
9
+ let node = path.getValue().body[0];
18
10
 
19
11
  // In this case we had something like -> (foo) { bar } which would mean that
20
12
  // we're looking at a paren node, so we'll descend one level deeper to get at
21
13
  // the actual params node.
22
- if (paramsNode.type !== "params") {
23
- paramsPath.push("body", 0);
24
- paramsNode = paramsNode.body[0];
14
+ if (node.type !== "params") {
15
+ node = node.body[0];
25
16
  }
26
17
 
27
18
  // If we don't have any params at all, then we're just going to bail out and
28
19
  // print nothing. This is to avoid printing an empty set of parentheses.
29
- if (paramsNode.body.every((type) => !type)) {
20
+ if (node.body.every((type) => !type)) {
30
21
  return "";
31
22
  }
32
23
 
33
- return group(
34
- concat([
35
- "(",
36
- indent(concat([softline, path.call.apply(path, paramsPath)])),
37
- softline,
38
- ")"
39
- ])
40
- );
24
+ return path.call(print, "body", 0);
41
25
  }
42
26
 
43
27
  // Lambda nodes represent stabby lambda literals, which can come in a couple of
@@ -1,4 +1,4 @@
1
- const { concat, group, join, line } = require("../prettier");
1
+ const { concat, group, join, indent, line, softline } = require("../prettier");
2
2
  const { literal } = require("../utils");
3
3
 
4
4
  function printRestParam(symbol) {
@@ -64,6 +64,8 @@ function printParams(path, opts, print) {
64
64
  parts.push(path.call(print, "body", 6));
65
65
  }
66
66
 
67
+ const contents = [join(concat([",", line]), parts)];
68
+
67
69
  // You can put an extra comma at the end of block args between pipes to
68
70
  // change what it does. Below is the difference:
69
71
  //
@@ -73,9 +75,19 @@ function printParams(path, opts, print) {
73
75
  // In ruby 2.5, the excessed comma is indicated by having a 0 in the rest
74
76
  // param position. In ruby 2.6+ it's indicated by having an "excessed_comma"
75
77
  // node in the rest position. Seems odd, but it's true.
76
- const comma = rest === 0 || (rest && rest.type === "excessed_comma");
78
+ if (rest === 0 || (rest && rest.type === "excessed_comma")) {
79
+ contents.push(",");
80
+ }
81
+
82
+ // If the parent node is a paren then we skipped printing the parentheses so
83
+ // that we could handle them here and get nicer formatting.
84
+ if (["lambda", "paren"].includes(path.getParentNode().type)) {
85
+ return group(
86
+ concat(["(", indent(concat([softline].concat(contents))), softline, ")"])
87
+ );
88
+ }
77
89
 
78
- return group(concat([join(concat([",", line]), parts), comma ? "," : ""]));
90
+ return group(concat(contents));
79
91
  }
80
92
 
81
93
  module.exports = {
@@ -45,35 +45,45 @@ function printBodyStmt(path, opts, print) {
45
45
  return group(concat(parts));
46
46
  }
47
47
 
48
+ const argNodeTypes = ["args", "args_add_star", "args_add_block"];
49
+
50
+ function printParen(path, opts, print) {
51
+ const contentNode = path.getValue().body[0];
52
+
53
+ if (!contentNode) {
54
+ return "()";
55
+ }
56
+
57
+ let contentDoc = path.call(print, "body", 0);
58
+
59
+ // If the content is params, we're going to let it handle its own parentheses
60
+ // so that it breaks nicely.
61
+ if (contentNode.type === "params") {
62
+ return contentDoc;
63
+ }
64
+
65
+ // If we have an arg type node as the contents, then it's going to return an
66
+ // array, so we need to explicitly join that content here.
67
+ if (argNodeTypes.includes(contentNode.type)) {
68
+ contentDoc = join(concat([",", line]), contentDoc);
69
+ }
70
+
71
+ return group(
72
+ concat([
73
+ "(",
74
+ indent(concat([softline, contentDoc])),
75
+ concat([softline, ")"])
76
+ ])
77
+ );
78
+ }
79
+
48
80
  module.exports = {
49
81
  "@__end__": (path, _opts, _print) => {
50
82
  const { body } = path.getValue();
51
83
  return concat([trim, "__END__", literalline, body]);
52
84
  },
53
85
  bodystmt: printBodyStmt,
54
- paren: (path, opts, print) => {
55
- if (!path.getValue().body[0]) {
56
- return "()";
57
- }
58
-
59
- let content = path.call(print, "body", 0);
60
-
61
- if (
62
- ["args", "args_add_star", "args_add_block"].includes(
63
- path.getValue().body[0].type
64
- )
65
- ) {
66
- content = join(concat([",", line]), content);
67
- }
68
-
69
- return group(
70
- concat([
71
- "(",
72
- indent(concat([softline, content])),
73
- concat([softline, ")"])
74
- ])
75
- );
76
- },
86
+ paren: printParen,
77
87
  program: (path, opts, print) =>
78
88
  concat([join(hardline, path.map(print, "body")), hardline]),
79
89
  stmts: (path, opts, print) => {
@@ -566,8 +566,10 @@ class Prettier::Parser < Ripper
566
566
 
567
567
  # Here we're going to determine the bounds for the stmts
568
568
  consequent = parts[1..-1].compact.first
569
- self[:body][0].bind(char_start,
570
- consequent ? consequent[:char_start] : char_end)
569
+ self[:body][0].bind(
570
+ char_start,
571
+ consequent ? consequent[:char_start] : char_end
572
+ )
571
573
 
572
574
  # Next we're going to determine the rescue clause if there is one
573
575
  if parts[1]
@@ -1064,7 +1066,10 @@ class Prettier::Parser < Ripper
1064
1066
  # event, so here we'll initialize the current embdoc.
1065
1067
  def on_embdoc_beg(value)
1066
1068
  @embdoc = {
1067
- type: :@embdoc, value: value, start: lineno, char_start: char_pos
1069
+ type: :@embdoc,
1070
+ value: value,
1071
+ start: lineno,
1072
+ char_start: char_pos
1068
1073
  }
1069
1074
  end
1070
1075
 
@@ -1211,9 +1216,12 @@ class Prettier::Parser < Ripper
1211
1216
 
1212
1217
  # Here we're going to artificially create an extra node type so that if
1213
1218
  # there are comments after the declaration of a heredoc, they get printed.
1214
- location.merge(
1215
- type: :heredoc, beging: location.merge(type: :@heredoc_beg, body: beging)
1216
- ).tap { |node| @heredocs << node }
1219
+ location
1220
+ .merge(
1221
+ type: :heredoc,
1222
+ beging: location.merge(type: :@heredoc_beg, body: beging)
1223
+ )
1224
+ .tap { |node| @heredocs << node }
1217
1225
  end
1218
1226
 
1219
1227
  # This is a parser event that occurs when you're using a heredoc with a
@@ -1644,7 +1652,10 @@ class Prettier::Parser < Ripper
1644
1652
  # some found at the end of the source string.
1645
1653
  def on_program(stmts)
1646
1654
  range = {
1647
- start: 1, end: lines.length, char_start: 0, char_end: source.length
1655
+ start: 1,
1656
+ end: lines.length,
1657
+ char_start: 0,
1658
+ char_end: source.length
1648
1659
  }
1649
1660
 
1650
1661
  stmts[:body] << @__end__ if @__end__
@@ -76,30 +76,37 @@ module.exports = {
76
76
  ruby: printer
77
77
  },
78
78
  options: {
79
+ rubyArrayLiteral: {
80
+ type: "boolean",
81
+ category: "Ruby",
82
+ default: true,
83
+ description:
84
+ "When possible, favor the use of string and symbol array literals."
85
+ },
79
86
  rubyHashLabel: {
80
87
  type: "boolean",
81
- category: "Global",
88
+ category: "Ruby",
82
89
  default: true,
83
90
  description:
84
91
  "When possible, uses the shortened hash key syntax, as opposed to hash rockets."
85
92
  },
86
93
  rubyModifier: {
87
94
  type: "boolean",
88
- category: "Global",
95
+ category: "Ruby",
89
96
  default: true,
90
97
  description:
91
98
  "When it fits on one line, allows if, unless, while, and until statements to use the modifier form."
92
99
  },
93
100
  rubySingleQuote: {
94
101
  type: "boolean",
95
- category: "Global",
102
+ category: "Ruby",
96
103
  default: true,
97
104
  description:
98
105
  "When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals."
99
106
  },
100
107
  rubyToProc: {
101
108
  type: "boolean",
102
- category: "Global",
109
+ category: "Ruby",
103
110
  default: false,
104
111
  description:
105
112
  "When possible, convert blocks to the more concise Symbol#to_proc syntax."
@@ -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 = (path, opts, node) => {
15
- if (!node || !opts.rubyToProc) {
14
+ const toProc = (path, node) => {
15
+ if (!node) {
16
16
  return null;
17
17
  }
18
18
 
@@ -2,8 +2,6 @@ const { concat } = require("./prettier");
2
2
  const isEmptyStmts = require("./utils/isEmptyStmts");
3
3
  const literalLineNoBreak = require("./utils/literalLineNoBreak");
4
4
 
5
- const concatBody = (path, opts, print) => concat(path.map(print, "body"));
6
-
7
5
  // If the node is a type of assignment or if the node is a paren and nested
8
6
  // inside that paren is a node that is a type of assignment.
9
7
  const containsAssignment = (node) =>
@@ -79,7 +77,6 @@ const skipAssignIndent = (node) =>
79
77
  (node.type === "call" && skipAssignIndent(node.body[0]));
80
78
 
81
79
  module.exports = {
82
- concatBody,
83
80
  containsAssignment,
84
81
  docLength,
85
82
  empty,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prettier
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.pre.rc1
4
+ version: 1.0.0.pre.rc2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Deisz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-09 00:00:00.000000000 Z
11
+ date: 2020-12-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler