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 +4 -4
- data/CHANGELOG.md +14 -1
- data/README.md +11 -10
- data/package.json +1 -1
- data/src/embed.js +6 -2
- data/src/nodes/args.js +59 -78
- data/src/nodes/arrays.js +34 -30
- data/src/nodes/assign.js +2 -2
- data/src/nodes/calls.js +65 -45
- data/src/nodes/case.js +11 -7
- data/src/nodes/hashes.js +79 -39
- data/src/nodes/ints.js +0 -6
- data/src/nodes/lambdas.js +6 -22
- data/src/nodes/params.js +15 -3
- data/src/nodes/statements.js +33 -23
- data/src/parser.rb +18 -7
- data/src/ruby.js +11 -4
- data/src/toProc.js +2 -2
- data/src/utils.js +0 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 305f0248dc0f0b4ab56d698f04858e3d25df0f91888a203f75740ae5a06a7e0d
|
4
|
+
data.tar.gz: 192eaf1d3cd95a9dc3693c0346f636f53ae2baede4cd3de5d827bd671571daf3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33a09aa76044b30dde5aa1555bfabc8f178607335ca0af9e4709d0f9fbfdc437c986097da85cc2d0c411326cb25d0672587a0253016bc89e71ca1245a5cfd652
|
7
|
+
data.tar.gz: b293f495c164808c4372dc79cfa801fd1603fe4907eafb374b61ba171cffbf1aaa09b6e0b1a427bc949099091db790a79d74d56c50f72c3ba006acc718b0065c
|
data/CHANGELOG.md
CHANGED
@@ -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-
|
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
|
126
|
-
|
|
127
|
-
| `printWidth`
|
128
|
-
| `requirePragma`
|
129
|
-
| `
|
130
|
-
| `
|
131
|
-
| `
|
132
|
-
| `
|
133
|
-
| `
|
134
|
-
| `
|
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:
|
data/package.json
CHANGED
data/src/embed.js
CHANGED
@@ -67,7 +67,11 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
67
67
|
path.call(print, "beging"),
|
68
68
|
lineSuffix(
|
69
69
|
group(
|
70
|
-
concat([
|
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
|
};
|
data/src/nodes/args.js
CHANGED
@@ -9,80 +9,56 @@ const {
|
|
9
9
|
} = require("../prettier");
|
10
10
|
|
11
11
|
const toProc = require("../toProc");
|
12
|
-
const {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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,
|
78
|
+
const proc = blockNode && toProc(path, blockNode);
|
103
79
|
|
104
|
-
// If we have a successful to_proc transformation, but we're part of an
|
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
|
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
|
-
|
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
|
|
data/src/nodes/arrays.js
CHANGED
@@ -18,32 +18,35 @@ const { getTrailingComma } = require("../utils");
|
|
18
18
|
// ['a', 'b', 'c']
|
19
19
|
//
|
20
20
|
function isStringArray(args) {
|
21
|
-
return
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
return false
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
57
|
-
|
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
|
|
data/src/nodes/assign.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
const { concat, group, indent, join, line } = require("../prettier");
|
2
|
-
const {
|
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:
|
37
|
+
var_field: first,
|
38
38
|
var_ref: first
|
39
39
|
};
|
data/src/nodes/calls.js
CHANGED
@@ -6,15 +6,15 @@ const {
|
|
6
6
|
indent,
|
7
7
|
softline
|
8
8
|
} = require("../prettier");
|
9
|
-
const {
|
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
|
17
|
-
const [receiverNode, _operatorNode, messageNode] =
|
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 =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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 = (
|
50
|
-
parentNode.breakDoc = (
|
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) && (
|
47
|
+
if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
|
58
48
|
return ifBreak(
|
59
|
-
group(concat(
|
49
|
+
group(indent(concat(node.breakDoc.concat(rightSideDoc)))),
|
60
50
|
concat([receiverDoc, group(rightSideDoc)])
|
61
51
|
);
|
62
52
|
}
|
63
53
|
|
64
|
-
|
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
|
69
|
-
const argNode =
|
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 = (
|
92
|
-
parentNode.breakDoc = (
|
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(
|
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
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
-
|
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
|
-
|
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
|
};
|
data/src/nodes/case.js
CHANGED
@@ -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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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);
|
data/src/nodes/hashes.js
CHANGED
@@ -25,35 +25,54 @@ function isValidHashLabel(symbolLiteral) {
|
|
25
25
|
return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
|
26
26
|
}
|
27
27
|
|
28
|
-
function
|
29
|
-
|
30
|
-
|
28
|
+
function canUseHashLabels(contentsNode) {
|
29
|
+
return contentsNode.body.every((assocNode) => {
|
30
|
+
if (assocNode.type === "assoc_splat") {
|
31
|
+
return true;
|
32
|
+
}
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
return
|
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
|
-
|
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
|
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 = {
|
data/src/nodes/ints.js
CHANGED
@@ -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.
|
data/src/nodes/lambdas.js
CHANGED
@@ -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
|
-
|
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 (
|
23
|
-
|
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 (
|
20
|
+
if (node.body.every((type) => !type)) {
|
30
21
|
return "";
|
31
22
|
}
|
32
23
|
|
33
|
-
return
|
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
|
data/src/nodes/params.js
CHANGED
@@ -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
|
-
|
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(
|
90
|
+
return group(concat(contents));
|
79
91
|
}
|
80
92
|
|
81
93
|
module.exports = {
|
data/src/nodes/statements.js
CHANGED
@@ -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:
|
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) => {
|
data/src/parser.rb
CHANGED
@@ -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(
|
570
|
-
|
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,
|
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
|
1215
|
-
|
1216
|
-
|
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,
|
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__
|
data/src/ruby.js
CHANGED
@@ -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: "
|
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: "
|
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: "
|
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: "
|
109
|
+
category: "Ruby",
|
103
110
|
default: false,
|
104
111
|
description:
|
105
112
|
"When possible, convert blocks to the more concise Symbol#to_proc syntax."
|
data/src/toProc.js
CHANGED
@@ -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,
|
15
|
-
if (!node
|
14
|
+
const toProc = (path, node) => {
|
15
|
+
if (!node) {
|
16
16
|
return null;
|
17
17
|
}
|
18
18
|
|
data/src/utils.js
CHANGED
@@ -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.
|
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-
|
11
|
+
date: 2020-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|