prettier 0.22.0 → 1.1.0
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 +4 -4
- data/CHANGELOG.md +115 -8
- data/CONTRIBUTING.md +1 -1
- data/README.md +17 -14
- data/node_modules/prettier/index.js +54 -54
- data/package.json +10 -5
- data/src/embed.js +11 -7
- data/src/nodes/args.js +59 -79
- data/src/nodes/arrays.js +38 -50
- data/src/nodes/assign.js +2 -2
- data/src/nodes/calls.js +132 -50
- data/src/nodes/case.js +11 -7
- data/src/nodes/conditionals.js +4 -4
- data/src/nodes/hashes.js +59 -51
- data/src/nodes/heredocs.js +2 -2
- data/src/nodes/hooks.js +5 -3
- data/src/nodes/ints.js +0 -6
- data/src/nodes/lambdas.js +6 -22
- data/src/nodes/loops.js +67 -66
- data/src/nodes/methods.js +2 -0
- data/src/nodes/operators.js +24 -13
- data/src/nodes/params.js +15 -3
- data/src/nodes/regexp.js +38 -9
- data/src/nodes/rescue.js +2 -2
- data/src/nodes/statements.js +65 -45
- data/src/nodes/strings.js +12 -11
- data/src/parser.js +27 -12
- data/src/parser.rb +190 -54
- data/src/printer.js +16 -1
- data/src/ruby.js +15 -21
- data/src/toProc.js +2 -2
- data/src/utils.js +15 -3
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +5 -46
data/package.json
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
{
|
2
2
|
"name": "@prettier/plugin-ruby",
|
3
|
-
"version": "
|
3
|
+
"version": "1.1.0",
|
4
4
|
"description": "prettier plugin for the Ruby programming language",
|
5
5
|
"main": "src/ruby.js",
|
6
6
|
"scripts": {
|
7
|
+
"check-format": "prettier --check '**/*'",
|
7
8
|
"lint": "eslint --cache .",
|
8
|
-
"print": "prettier --plugin=.",
|
9
9
|
"test": "jest"
|
10
10
|
},
|
11
11
|
"repository": {
|
@@ -25,9 +25,9 @@
|
|
25
25
|
"all-contributors-cli": "^6.14.1",
|
26
26
|
"eslint": "^7.8.1",
|
27
27
|
"eslint-config-prettier": "^7.0.0",
|
28
|
-
"husky": "^
|
28
|
+
"husky": "^4.3.5",
|
29
29
|
"jest": "^26.0.0",
|
30
|
-
"pretty-quick": "^3.
|
30
|
+
"pretty-quick": "^3.1.0"
|
31
31
|
},
|
32
32
|
"eslintConfig": {
|
33
33
|
"extends": [
|
@@ -50,6 +50,8 @@
|
|
50
50
|
}
|
51
51
|
},
|
52
52
|
"jest": {
|
53
|
+
"globalSetup": "./test/js/globalSetup.js",
|
54
|
+
"globalTeardown": "./test/js/globalTeardown.js",
|
53
55
|
"setupFilesAfterEnv": [
|
54
56
|
"./test/js/setupTests.js"
|
55
57
|
],
|
@@ -61,6 +63,9 @@
|
|
61
63
|
}
|
62
64
|
},
|
63
65
|
"prettier": {
|
64
|
-
"trailingComma": "none"
|
66
|
+
"trailingComma": "none",
|
67
|
+
"plugins": [
|
68
|
+
"."
|
69
|
+
]
|
65
70
|
}
|
66
71
|
}
|
data/src/embed.js
CHANGED
@@ -31,7 +31,7 @@ const replaceNewlines = (doc) =>
|
|
31
31
|
: currentDoc
|
32
32
|
);
|
33
33
|
|
34
|
-
const embed = (path,
|
34
|
+
const embed = (path, print, textToDoc, _opts) => {
|
35
35
|
const node = path.getValue();
|
36
36
|
|
37
37
|
// Currently we only support embedded formatting on heredoc nodes
|
@@ -47,7 +47,7 @@ const embed = (path, _print, textToDoc, _opts) => {
|
|
47
47
|
|
48
48
|
// Next, find the parser associated with this heredoc (if there is one). For
|
49
49
|
// example, if you use <<~CSS, we'd hook it up to the css parser.
|
50
|
-
const parser = parsers[beging.slice(3).toLowerCase()];
|
50
|
+
const parser = parsers[beging.body.slice(3).toLowerCase()];
|
51
51
|
if (!parser) {
|
52
52
|
return null;
|
53
53
|
}
|
@@ -62,12 +62,16 @@ const embed = (path, _print, textToDoc, _opts) => {
|
|
62
62
|
|
63
63
|
// If we're using a squiggly heredoc, then we can properly handle indentation
|
64
64
|
// ourselves.
|
65
|
-
if (beging[2] === "~") {
|
65
|
+
if (beging.body[2] === "~") {
|
66
66
|
return concat([
|
67
|
-
beging,
|
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
|
]);
|
@@ -77,8 +81,8 @@ const embed = (path, _print, textToDoc, _opts) => {
|
|
77
81
|
// content as it is.
|
78
82
|
return markAsRoot(
|
79
83
|
concat([
|
80
|
-
beging,
|
81
|
-
lineSuffix(group(concat([formatted, literalLineNoBreak, ending])))
|
84
|
+
path.call(print, "beging"),
|
85
|
+
lineSuffix(group(concat([formatted, literalLineNoBreak, ending.trim()])))
|
82
86
|
])
|
83
87
|
);
|
84
88
|
};
|
data/src/nodes/args.js
CHANGED
@@ -9,81 +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
|
-
|
27
|
-
|
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(
|
28
44
|
concat([
|
29
|
-
"(",
|
30
|
-
indent(concat([softline, path.call(print, "body", 0)])),
|
31
45
|
softline,
|
32
|
-
")
|
46
|
+
join(concat([",", line]), args),
|
47
|
+
getTrailingComma(opts) && !hasBlock ? ifBreak(",", "") : ""
|
33
48
|
])
|
34
|
-
)
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
// Here we're going to make a determination on whether or not we should put
|
48
|
-
// a newline before the first argument. In some cases this makes the
|
49
|
-
// appearance a little better. For example, instead of taking this input:
|
50
|
-
//
|
51
|
-
// foo(arg1, arg2).bar(arg1, arg2).baz(arg1)
|
52
|
-
//
|
53
|
-
// and transforming it into this:
|
54
|
-
//
|
55
|
-
// foo(arg1, arg2).bar(arg1, arg2).baz(
|
56
|
-
// arg1
|
57
|
-
// )
|
58
|
-
//
|
59
|
-
// it instead gets transformed into this:
|
60
|
-
//
|
61
|
-
// foo(arg1, arg2).bar(arg1, arg2)
|
62
|
-
// .baz(arg1)
|
63
|
-
//
|
64
|
-
const maxDocLength = 15;
|
65
|
-
const firstArgDoc = args[0];
|
66
|
-
|
67
|
-
// prettier-ignore
|
68
|
-
const shouldWrapLine =
|
69
|
-
(args.reduce((sum, arg) => sum + docLength(arg), 0) > maxDocLength) ||
|
70
|
-
(args.length == 1 && firstArgDoc.type === "group" && docLength(firstArgDoc) > maxDocLength) ||
|
71
|
-
(firstArgDoc.type === "concat" && firstArgDoc.parts.some((part) => part.type === "group"));
|
72
|
-
|
73
|
-
// Here we're going to get all of the docs representing the doc that's
|
74
|
-
// inside the parentheses.
|
75
|
-
if (shouldWrapLine) {
|
76
|
-
argsDocs = [indent(concat([softline].concat(argsDocs))), softline];
|
77
|
-
} else {
|
78
|
-
argsDocs = [indent(concat(argsDocs))];
|
79
|
-
}
|
80
|
-
|
81
|
-
// Now here we return a doc that represents the whole grouped expression,
|
82
|
-
// including the surrouding parentheses.
|
83
|
-
return group(concat(["("].concat(argsDocs).concat(")")));
|
84
|
-
},
|
85
|
-
args: (path, opts, print) => {
|
86
|
-
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) {
|
87
62
|
let blockNode = null;
|
88
63
|
|
89
64
|
// Look up the chain to see if these arguments are contained within a
|
@@ -100,21 +75,26 @@ module.exports = {
|
|
100
75
|
return blockNode;
|
101
76
|
});
|
102
77
|
|
103
|
-
const proc = blockNode && toProc(path,
|
78
|
+
const proc = blockNode && toProc(path, blockNode);
|
104
79
|
|
105
|
-
// If we have a successful to_proc transformation, but we're part of an
|
106
|
-
// 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
|
107
82
|
//
|
108
83
|
// foo[:bar].each(&:to_s)
|
109
84
|
//
|
110
|
-
// In this case we need to just return regular arguments, otherwise we
|
111
|
-
// 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.
|
112
87
|
if (proc && path.getParentNode(1).type !== "aref") {
|
113
88
|
args.push(proc);
|
114
89
|
}
|
90
|
+
}
|
115
91
|
|
116
|
-
|
117
|
-
|
92
|
+
return args;
|
93
|
+
}
|
94
|
+
|
95
|
+
module.exports = {
|
96
|
+
arg_paren: printArgParen,
|
97
|
+
args: printArgs,
|
118
98
|
args_add_block: (path, opts, print) => {
|
119
99
|
const parts = path.call(print, "body", 0);
|
120
100
|
|
data/src/nodes/arrays.js
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
const {
|
2
2
|
concat,
|
3
3
|
group,
|
4
|
-
hardline,
|
5
4
|
ifBreak,
|
6
5
|
indent,
|
7
6
|
join,
|
@@ -9,6 +8,8 @@ const {
|
|
9
8
|
softline
|
10
9
|
} = require("../prettier");
|
11
10
|
|
11
|
+
const { getTrailingComma, printEmptyCollection } = require("../utils");
|
12
|
+
|
12
13
|
// Checks that every argument within this args node is a string_literal node
|
13
14
|
// that has no spaces or interpolations. This means we're dealing with an array
|
14
15
|
// that looks something like:
|
@@ -16,31 +17,35 @@ const {
|
|
16
17
|
// ['a', 'b', 'c']
|
17
18
|
//
|
18
19
|
function isStringArray(args) {
|
19
|
-
return
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
return false
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
20
|
+
return (
|
21
|
+
args.body.length > 1 &&
|
22
|
+
args.body.every((arg) => {
|
23
|
+
// We want to verify that every node inside of this array is a string
|
24
|
+
// literal. We also want to make sure none of them have comments attached.
|
25
|
+
if (arg.type !== "string_literal" || arg.comments) {
|
26
|
+
return false;
|
27
|
+
}
|
28
|
+
|
29
|
+
// If the string has multiple parts (meaning plain string content but also
|
30
|
+
// interpolated content) then we know it's not a simple string.
|
31
|
+
if (arg.body.length !== 1) {
|
32
|
+
return false;
|
33
|
+
}
|
34
|
+
|
35
|
+
const part = arg.body[0];
|
36
|
+
|
37
|
+
// If the only part of this string is not @tstring_content then it's
|
38
|
+
// interpolated, so again we can return false.
|
39
|
+
if (part.type !== "@tstring_content") {
|
40
|
+
return false;
|
41
|
+
}
|
42
|
+
|
43
|
+
// Finally, verify that the string doesn't contain a space, an escape
|
44
|
+
// character, or brackets so that we know it can be put into a string
|
45
|
+
// literal array.
|
46
|
+
return !/[\s\\[\]]/.test(part.body);
|
47
|
+
})
|
48
|
+
);
|
44
49
|
}
|
45
50
|
|
46
51
|
// Checks that every argument within this args node is a symbol_literal node (as
|
@@ -50,8 +55,9 @@ function isStringArray(args) {
|
|
50
55
|
// [:a, :b, :c]
|
51
56
|
//
|
52
57
|
function isSymbolArray(args) {
|
53
|
-
return
|
54
|
-
|
58
|
+
return (
|
59
|
+
args.body.length > 1 &&
|
60
|
+
args.body.every((arg) => arg.type === "symbol_literal" && !arg.comments)
|
55
61
|
);
|
56
62
|
}
|
57
63
|
|
@@ -86,24 +92,6 @@ function printSpecialArray(start) {
|
|
86
92
|
};
|
87
93
|
}
|
88
94
|
|
89
|
-
function printEmptyArrayWithComments(path, opts) {
|
90
|
-
const arrayNode = path.getValue();
|
91
|
-
|
92
|
-
const printComment = (commentPath, index) => {
|
93
|
-
arrayNode.comments[index].printed = true;
|
94
|
-
return opts.printer.printComment(commentPath);
|
95
|
-
};
|
96
|
-
|
97
|
-
return concat([
|
98
|
-
"[",
|
99
|
-
indent(
|
100
|
-
concat([hardline, join(hardline, path.map(printComment, "comments"))])
|
101
|
-
),
|
102
|
-
line,
|
103
|
-
"]"
|
104
|
-
]);
|
105
|
-
}
|
106
|
-
|
107
95
|
// An array node is any literal array in Ruby. This includes all of the special
|
108
96
|
// array literals as well as regular arrays. If it is a special array literal
|
109
97
|
// then it will have one child that represents the special array, otherwise it
|
@@ -115,12 +103,12 @@ function printArray(path, opts, print) {
|
|
115
103
|
// If there is no inner arguments node, then we're dealing with an empty
|
116
104
|
// array, so we can go ahead and return.
|
117
105
|
if (args === null) {
|
118
|
-
return
|
106
|
+
return printEmptyCollection(path, opts, "[", "]");
|
119
107
|
}
|
120
108
|
|
121
109
|
// If we have an array that contains only simple string literals with no
|
122
110
|
// spaces or interpolation, then we're going to print a %w array.
|
123
|
-
if (isStringArray(args)) {
|
111
|
+
if (opts.rubyArrayLiteral && isStringArray(args)) {
|
124
112
|
const printString = (stringPath) => stringPath.call(print, "body", 0);
|
125
113
|
const parts = path.map(printString, "body", 0, "body");
|
126
114
|
|
@@ -129,7 +117,7 @@ function printArray(path, opts, print) {
|
|
129
117
|
|
130
118
|
// If we have an array that contains only simple symbol literals with no
|
131
119
|
// interpolation, then we're going to print a %i array.
|
132
|
-
if (isSymbolArray(args)) {
|
120
|
+
if (opts.rubyArrayLiteral && isSymbolArray(args)) {
|
133
121
|
const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
|
134
122
|
const parts = path.map(printSymbol, "body", 0, "body");
|
135
123
|
|
@@ -153,7 +141,7 @@ function printArray(path, opts, print) {
|
|
153
141
|
concat([
|
154
142
|
softline,
|
155
143
|
join(concat([",", line]), path.call(print, "body", 0)),
|
156
|
-
opts
|
144
|
+
getTrailingComma(opts) ? ifBreak(",", "") : ""
|
157
145
|
])
|
158
146
|
),
|
159
147
|
softline,
|
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,40 +6,31 @@ 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"];
|
14
|
-
const noIndent = ["array", "hash", "if", "method_add_block", "xstring_literal"];
|
13
|
+
const chained = ["call", "method_add_arg", "method_add_block"];
|
15
14
|
|
16
15
|
function printCall(path, opts, print) {
|
17
|
-
const
|
18
|
-
const [receiverNode, _operatorNode, messageNode] =
|
16
|
+
const node = path.getValue();
|
17
|
+
const [receiverNode, _operatorNode, messageNode] = node.body;
|
19
18
|
|
20
19
|
const receiverDoc = path.call(print, "body", 0);
|
21
20
|
const operatorDoc = makeCall(path, opts, print);
|
22
21
|
|
23
22
|
// You can call lambdas with a special syntax that looks like func.(*args).
|
24
|
-
// In this case, "call" is returned for the 3rd child node.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
// For certain left sides of the call nodes, we want to attach directly to
|
29
|
-
// the } or end.
|
30
|
-
if (noIndent.includes(receiverNode.type)) {
|
31
|
-
return concat([receiverDoc, operatorDoc, messageDoc]);
|
32
|
-
}
|
23
|
+
// In this case, "call" is returned for the 3rd child node. We don't alter
|
24
|
+
// call syntax so if `call` is implicit, we don't print it out.
|
25
|
+
const messageDoc = messageNode === "call" ? "" : path.call(print, "body", 2);
|
33
26
|
|
34
27
|
// The right side of the call node, as in everything including the operator
|
35
28
|
// and beyond.
|
36
|
-
const rightSideDoc =
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
])
|
42
|
-
);
|
29
|
+
const rightSideDoc = concat([
|
30
|
+
receiverNode.comments ? hardline : softline,
|
31
|
+
operatorDoc,
|
32
|
+
messageDoc
|
33
|
+
]);
|
43
34
|
|
44
35
|
// Get a reference to the parent node so we can check if we're inside a chain
|
45
36
|
const parentNode = path.getParentNode();
|
@@ -47,33 +38,56 @@ function printCall(path, opts, print) {
|
|
47
38
|
// If our parent node is a chained node then we're not going to group the
|
48
39
|
// right side of the expression, as we want to have a nice multi-line layout.
|
49
40
|
if (chained.includes(parentNode.type)) {
|
50
|
-
parentNode.chain = (
|
51
|
-
parentNode.
|
52
|
-
|
53
|
-
);
|
41
|
+
parentNode.chain = (node.chain || 0) + 1;
|
42
|
+
parentNode.callChain = (node.callChain || 0) + 1;
|
43
|
+
parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc);
|
54
44
|
}
|
55
45
|
|
56
46
|
// If we're at the top of a chain, then we're going to print out a nice
|
57
47
|
// multi-line layout if this doesn't break into multiple lines.
|
58
|
-
if (!chained.includes(parentNode.type) && (
|
48
|
+
if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
|
59
49
|
return ifBreak(
|
60
|
-
group(concat(
|
50
|
+
group(indent(concat(node.breakDoc.concat(rightSideDoc)))),
|
61
51
|
concat([receiverDoc, group(rightSideDoc)])
|
62
52
|
);
|
63
53
|
}
|
64
54
|
|
65
|
-
|
55
|
+
// For certain left sides of the call nodes, we want to attach directly to
|
56
|
+
// the } or end.
|
57
|
+
if (noIndent.includes(receiverNode.type)) {
|
58
|
+
return concat([receiverDoc, operatorDoc, messageDoc]);
|
59
|
+
}
|
60
|
+
|
61
|
+
return group(concat([receiverDoc, group(indent(rightSideDoc))]));
|
66
62
|
}
|
67
63
|
|
68
64
|
function printMethodAddArg(path, opts, print) {
|
69
|
-
const
|
70
|
-
const argNode = methodAddArgNode.body[1];
|
65
|
+
const node = path.getValue();
|
71
66
|
|
67
|
+
const [methodNode, argNode] = node.body;
|
72
68
|
const [methodDoc, argsDoc] = path.map(print, "body");
|
73
69
|
|
74
70
|
// You can end up here if you have a method with a ? ending, presumably
|
75
|
-
// because the parser knows that it cannot be a local variable.
|
71
|
+
// because the parser knows that it cannot be a local variable. You can also
|
72
|
+
// end up here if you are explicitly using an empty set of parentheses.
|
76
73
|
if (argsDoc.length === 0) {
|
74
|
+
// If you're using an explicit set of parentheses on something that looks
|
75
|
+
// like a constant, then we need to match that in order to maintain valid
|
76
|
+
// Ruby. For example, you could do something like Foo(), on which we would
|
77
|
+
// need to keep the parentheses to make it look like a method call.
|
78
|
+
if (methodNode.type === "fcall" && methodNode.body[0].type === "@const") {
|
79
|
+
return concat([methodDoc, "()"]);
|
80
|
+
}
|
81
|
+
|
82
|
+
// If you're using an explicit set parentheses with the special call syntax,
|
83
|
+
// then we need to explicitly print out an extra set of parentheses. For
|
84
|
+
// example, if you call something like Foo.new.() (implicitly calling the
|
85
|
+
// #call method on a new instance of the Foo class), then we have to print
|
86
|
+
// out those parentheses, otherwise we'll end up with Foo.new.
|
87
|
+
if (methodNode.type === "call" && methodNode.body[2] === "call") {
|
88
|
+
return concat([methodDoc, "()"]);
|
89
|
+
}
|
90
|
+
|
77
91
|
return methodDoc;
|
78
92
|
}
|
79
93
|
|
@@ -89,20 +103,30 @@ function printMethodAddArg(path, opts, print) {
|
|
89
103
|
// If our parent node is a chained node then we're not going to group the
|
90
104
|
// right side of the expression, as we want to have a nice multi-line layout.
|
91
105
|
if (chained.includes(parentNode.type)) {
|
92
|
-
parentNode.chain = (
|
93
|
-
parentNode.breakDoc = (
|
94
|
-
argsDoc
|
95
|
-
);
|
106
|
+
parentNode.chain = (node.chain || 0) + 1;
|
107
|
+
parentNode.breakDoc = (node.breakDoc || [methodDoc]).concat(argsDoc);
|
96
108
|
}
|
97
109
|
|
98
110
|
// If we're at the top of a chain, then we're going to print out a nice
|
99
111
|
// multi-line layout if this doesn't break into multiple lines.
|
100
|
-
if (
|
101
|
-
|
102
|
-
|
103
|
-
|
112
|
+
if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
|
113
|
+
// This is pretty specialized behavior. Basically if we're at the top of a
|
114
|
+
// chain but we've only had method calls without arguments and now we have
|
115
|
+
// arguments, then we're effectively trying to call a method with arguments
|
116
|
+
// that is nested under a bunch of stuff. So we group together to first part
|
117
|
+
// to make it so just the arguments break. This looks like, for example:
|
118
|
+
//
|
119
|
+
// config.action_dispatch.rescue_responses.merge!(
|
120
|
+
// 'ActiveRecord::ConnectionTimeoutError' => :service_unavailable,
|
121
|
+
// 'ActiveRecord::QueryCanceled' => :service_unavailable
|
122
|
+
// )
|
123
|
+
//
|
124
|
+
if (node.callChain === node.chain) {
|
125
|
+
return concat([group(indent(concat(node.breakDoc))), group(argsDoc)]);
|
126
|
+
}
|
127
|
+
|
104
128
|
return ifBreak(
|
105
|
-
group(concat(
|
129
|
+
group(indent(concat(node.breakDoc.concat(argsDoc)))),
|
106
130
|
concat([methodDoc, argsDoc])
|
107
131
|
);
|
108
132
|
}
|
@@ -110,15 +134,47 @@ function printMethodAddArg(path, opts, print) {
|
|
110
134
|
return concat([methodDoc, argsDoc]);
|
111
135
|
}
|
112
136
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
137
|
+
// Sorbet type annotations look like the following:
|
138
|
+
//
|
139
|
+
// {method_add_block
|
140
|
+
// [{method_add_arg
|
141
|
+
// [{fcall
|
142
|
+
// [{@ident "sig"}]},
|
143
|
+
// {args []}]},
|
144
|
+
// {brace_block [nil, {stmts}]}}]}
|
145
|
+
//
|
146
|
+
function isSorbetTypeAnnotation(node) {
|
147
|
+
const [callNode, blockNode] = node.body;
|
148
|
+
|
149
|
+
return (
|
150
|
+
callNode.type === "method_add_arg" &&
|
151
|
+
callNode.body[0].type === "fcall" &&
|
152
|
+
callNode.body[0].body[0].body === "sig" &&
|
153
|
+
callNode.body[1].type === "args" &&
|
154
|
+
callNode.body[1].body.length === 0 &&
|
155
|
+
blockNode.type === "brace_block"
|
156
|
+
);
|
157
|
+
}
|
120
158
|
|
121
|
-
|
159
|
+
function printMethodAddBlock(path, opts, print) {
|
160
|
+
const node = path.getValue();
|
161
|
+
|
162
|
+
const [callNode, blockNode] = node.body;
|
163
|
+
const [callDoc, blockDoc] = path.map(print, "body");
|
164
|
+
|
165
|
+
// Very special handling here for sorbet type annotations. They look like Ruby
|
166
|
+
// code, but they're not actually Ruby code, so we're not going to mess with
|
167
|
+
// them at all.
|
168
|
+
if (isSorbetTypeAnnotation(node)) {
|
169
|
+
return opts.originalText.slice(opts.locStart(node), opts.locEnd(node));
|
170
|
+
}
|
171
|
+
|
172
|
+
// Don't bother trying to do any kind of fancy toProc transform if the option
|
173
|
+
// is disabled.
|
174
|
+
if (opts.rubyToProc) {
|
175
|
+
const proc = toProc(path, blockNode);
|
176
|
+
|
177
|
+
if (proc && callNode.type === "call") {
|
122
178
|
return group(
|
123
179
|
concat([
|
124
180
|
path.call(print, "body", 0),
|
@@ -132,8 +188,34 @@ module.exports = {
|
|
132
188
|
if (proc) {
|
133
189
|
return path.call(print, "body", 0);
|
134
190
|
}
|
191
|
+
}
|
135
192
|
|
136
|
-
|
137
|
-
|
193
|
+
// Get a reference to the parent node so we can check if we're inside a chain
|
194
|
+
const parentNode = path.getParentNode();
|
195
|
+
|
196
|
+
// If our parent node is a chained node then we're not going to group the
|
197
|
+
// right side of the expression, as we want to have a nice multi-line layout.
|
198
|
+
if (chained.includes(parentNode.type)) {
|
199
|
+
parentNode.chain = (node.chain || 0) + 1;
|
200
|
+
parentNode.breakDoc = (node.breakDoc || [callDoc]).concat(blockDoc);
|
201
|
+
}
|
202
|
+
|
203
|
+
// If we're at the top of a chain, then we're going to print out a nice
|
204
|
+
// multi-line layout if this doesn't break into multiple lines.
|
205
|
+
if (!chained.includes(parentNode.type) && (node.chain || 0) >= 3) {
|
206
|
+
return ifBreak(
|
207
|
+
group(indent(concat(node.breakDoc.concat(blockDoc)))),
|
208
|
+
concat([callDoc, blockDoc])
|
209
|
+
);
|
210
|
+
}
|
211
|
+
|
212
|
+
return concat([callDoc, blockDoc]);
|
213
|
+
}
|
214
|
+
|
215
|
+
module.exports = {
|
216
|
+
call: printCall,
|
217
|
+
fcall: first,
|
218
|
+
method_add_arg: printMethodAddArg,
|
219
|
+
method_add_block: printMethodAddBlock,
|
138
220
|
vcall: first
|
139
221
|
};
|