prettier 1.0.0.pre.rc1 → 1.0.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
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