prettier 1.3.0 → 1.5.3
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 +355 -371
- data/README.md +1 -1
- data/package.json +5 -5
- data/rubocop.yml +3 -0
- data/src/haml/parser.js +2 -13
- data/src/haml/parser.rb +19 -24
- data/src/parser/getLang.js +32 -0
- data/src/parser/getNetcat.js +50 -0
- data/src/parser/netcat.js +15 -0
- data/src/parser/parseSync.js +28 -0
- data/src/parser/requestParse.js +74 -0
- data/src/parser/server.rb +66 -0
- data/src/plugin.js +1 -0
- data/src/rbs/parser.js +2 -14
- data/src/rbs/parser.rb +28 -27
- data/src/ruby/embed.js +61 -13
- data/src/ruby/nodes/args.js +79 -24
- data/src/ruby/nodes/arrays.js +36 -33
- data/src/ruby/nodes/calls.js +2 -31
- data/src/ruby/nodes/class.js +17 -27
- data/src/ruby/nodes/commands.js +1 -1
- data/src/ruby/nodes/conditionals.js +1 -1
- data/src/ruby/nodes/hashes.js +28 -14
- data/src/ruby/nodes/heredocs.js +5 -3
- data/src/ruby/nodes/loops.js +4 -10
- data/src/ruby/nodes/methods.js +4 -11
- data/src/ruby/nodes/rescue.js +32 -25
- data/src/ruby/nodes/statements.js +6 -5
- data/src/ruby/nodes/strings.js +7 -6
- data/src/ruby/parser.js +2 -50
- data/src/ruby/parser.rb +132 -31
- data/src/ruby/printer.js +8 -5
- data/src/utils.js +2 -1
- data/src/utils/inlineEnsureParens.js +8 -1
- data/src/utils/isEmptyBodyStmt.js +7 -0
- data/src/utils/isEmptyStmts.js +9 -5
- data/src/utils/literallineWithoutBreakParent.js +7 -0
- data/src/utils/printEmptyCollection.js +9 -2
- metadata +11 -4
- data/src/utils/literalLineNoBreak.js +0 -7
data/src/ruby/embed.js
CHANGED
@@ -8,7 +8,7 @@ const {
|
|
8
8
|
stripTrailingHardline
|
9
9
|
} = require("../prettier");
|
10
10
|
|
11
|
-
const {
|
11
|
+
const { literallineWithoutBreakParent } = require("../utils");
|
12
12
|
|
13
13
|
const parsers = {
|
14
14
|
css: "css",
|
@@ -24,18 +24,54 @@ const parsers = {
|
|
24
24
|
// have a test that exercises it because I'm not sure for which parser it is
|
25
25
|
// necessary, but since it's in prettier core I'm keeping it here.
|
26
26
|
/* istanbul ignore next */
|
27
|
-
|
28
|
-
mapDoc(doc, (currentDoc) =>
|
27
|
+
function replaceNewlines(doc) {
|
28
|
+
return mapDoc(doc, (currentDoc) =>
|
29
29
|
typeof currentDoc === "string" && currentDoc.includes("\n")
|
30
30
|
? concat(
|
31
31
|
currentDoc
|
32
32
|
.split(/(\n)/g)
|
33
|
-
.map((v, i) => (i % 2 === 0 ? v :
|
33
|
+
.map((v, i) => (i % 2 === 0 ? v : literallineWithoutBreakParent))
|
34
34
|
)
|
35
35
|
: currentDoc
|
36
36
|
);
|
37
|
+
}
|
37
38
|
|
38
|
-
|
39
|
+
// Returns a number that represents the minimum amount of leading whitespace
|
40
|
+
// that is present on every line in the given string. So for example if you have
|
41
|
+
// the following heredoc:
|
42
|
+
//
|
43
|
+
// <<~HERE
|
44
|
+
// my
|
45
|
+
// content
|
46
|
+
// here
|
47
|
+
// HERE
|
48
|
+
//
|
49
|
+
// then the return value of this function would be 2. If you indented every line
|
50
|
+
// of the inner content 2 more spaces then this function would return 4.
|
51
|
+
function getCommonLeadingWhitespace(content) {
|
52
|
+
const pattern = /^\s+/;
|
53
|
+
|
54
|
+
return content
|
55
|
+
.split("\n")
|
56
|
+
.slice(0, -1)
|
57
|
+
.reduce((minimum, line) => {
|
58
|
+
const matched = pattern.exec(line);
|
59
|
+
const length = matched ? matched[0].length : 0;
|
60
|
+
|
61
|
+
return minimum === null ? length : Math.min(minimum, length);
|
62
|
+
}, null);
|
63
|
+
}
|
64
|
+
|
65
|
+
// Returns a new string with the common whitespace stripped out. Effectively it
|
66
|
+
// emulates what a squiggly heredoc does in Ruby.
|
67
|
+
function stripCommonLeadingWhitespace(content) {
|
68
|
+
const lines = content.split("\n");
|
69
|
+
const minimum = getCommonLeadingWhitespace(content);
|
70
|
+
|
71
|
+
return lines.map((line) => line.slice(minimum)).join("\n");
|
72
|
+
}
|
73
|
+
|
74
|
+
function embed(path, print, textToDoc, _opts) {
|
39
75
|
const node = path.getValue();
|
40
76
|
|
41
77
|
// Currently we only support embedded formatting on heredoc nodes
|
@@ -45,6 +81,8 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
45
81
|
|
46
82
|
// First, ensure that we don't have any interpolation
|
47
83
|
const { beging, body, ending } = node;
|
84
|
+
const isSquiggly = beging.body[2] === "~";
|
85
|
+
|
48
86
|
if (body.some((part) => part.type !== "@tstring_content")) {
|
49
87
|
return null;
|
50
88
|
}
|
@@ -56,24 +94,32 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
56
94
|
return null;
|
57
95
|
}
|
58
96
|
|
59
|
-
// Get the content as if it were a source string
|
60
|
-
|
61
|
-
|
97
|
+
// Get the content as if it were a source string.
|
98
|
+
let content = body.map((part) => part.body).join("");
|
99
|
+
|
100
|
+
// If we're using a squiggly heredoc, then we're going to manually strip off
|
101
|
+
// the leading whitespace of each line up to the minimum leading whitespace so
|
102
|
+
// that the embedded parser can handle that for us.
|
103
|
+
if (isSquiggly) {
|
104
|
+
content = stripCommonLeadingWhitespace(content);
|
105
|
+
}
|
106
|
+
|
107
|
+
// Pass that content into the embedded parser. Get back the doc node.
|
62
108
|
const formatted = concat([
|
63
|
-
|
109
|
+
literallineWithoutBreakParent,
|
64
110
|
replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
|
65
111
|
]);
|
66
112
|
|
67
113
|
// If we're using a squiggly heredoc, then we can properly handle indentation
|
68
114
|
// ourselves.
|
69
|
-
if (
|
115
|
+
if (isSquiggly) {
|
70
116
|
return concat([
|
71
117
|
path.call(print, "beging"),
|
72
118
|
lineSuffix(
|
73
119
|
group(
|
74
120
|
concat([
|
75
121
|
indent(markAsRoot(formatted)),
|
76
|
-
|
122
|
+
literallineWithoutBreakParent,
|
77
123
|
ending.trim()
|
78
124
|
])
|
79
125
|
)
|
@@ -86,9 +132,11 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
86
132
|
return markAsRoot(
|
87
133
|
concat([
|
88
134
|
path.call(print, "beging"),
|
89
|
-
lineSuffix(
|
135
|
+
lineSuffix(
|
136
|
+
group(concat([formatted, literallineWithoutBreakParent, ending.trim()]))
|
137
|
+
)
|
90
138
|
])
|
91
139
|
);
|
92
|
-
}
|
140
|
+
}
|
93
141
|
|
94
142
|
module.exports = embed;
|
data/src/ruby/nodes/args.js
CHANGED
@@ -8,9 +8,26 @@ const {
|
|
8
8
|
softline
|
9
9
|
} = require("../../prettier");
|
10
10
|
const { getTrailingComma } = require("../../utils");
|
11
|
-
|
12
11
|
const toProc = require("../toProc");
|
13
12
|
|
13
|
+
const noTrailingComma = ["command", "command_call"];
|
14
|
+
|
15
|
+
function getArgParenTrailingComma(node) {
|
16
|
+
// If we have a block, then we don't want to add a trailing comma.
|
17
|
+
if (node.type === "args_add_block" && node.body[1]) {
|
18
|
+
return "";
|
19
|
+
}
|
20
|
+
|
21
|
+
// If we only have one argument and that first argument necessitates that we
|
22
|
+
// skip putting a comma (because it would interfere with parsing the argument)
|
23
|
+
// then we don't want to add a trailing comma.
|
24
|
+
if (node.body.length === 1 && noTrailingComma.includes(node.body[0].type)) {
|
25
|
+
return "";
|
26
|
+
}
|
27
|
+
|
28
|
+
return ifBreak(",", "");
|
29
|
+
}
|
30
|
+
|
14
31
|
function printArgParen(path, opts, print) {
|
15
32
|
const argsNode = path.getValue().body[0];
|
16
33
|
|
@@ -32,9 +49,6 @@ function printArgParen(path, opts, print) {
|
|
32
49
|
);
|
33
50
|
}
|
34
51
|
|
35
|
-
const args = path.call(print, "body", 0);
|
36
|
-
const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
|
37
|
-
|
38
52
|
// Now here we return a doc that represents the whole grouped expression,
|
39
53
|
// including the surrouding parentheses.
|
40
54
|
return group(
|
@@ -43,8 +57,8 @@ function printArgParen(path, opts, print) {
|
|
43
57
|
indent(
|
44
58
|
concat([
|
45
59
|
softline,
|
46
|
-
join(concat([",", line]),
|
47
|
-
getTrailingComma(opts) &&
|
60
|
+
join(concat([",", line]), path.call(print, "body", 0)),
|
61
|
+
getTrailingComma(opts) && getArgParenTrailingComma(argsNode)
|
48
62
|
])
|
49
63
|
),
|
50
64
|
softline,
|
@@ -92,25 +106,66 @@ function printArgs(path, { rubyToProc }, print) {
|
|
92
106
|
return args;
|
93
107
|
}
|
94
108
|
|
109
|
+
function printArgsAddBlock(path, opts, print) {
|
110
|
+
const node = path.getValue();
|
111
|
+
const parts = path.call(print, "body", 0);
|
112
|
+
|
113
|
+
if (node.body[1]) {
|
114
|
+
let blockDoc = path.call(print, "body", 1);
|
115
|
+
|
116
|
+
if (node.body[1].comments) {
|
117
|
+
// If we have a method call like:
|
118
|
+
//
|
119
|
+
// foo(
|
120
|
+
// # comment
|
121
|
+
// &block
|
122
|
+
// )
|
123
|
+
//
|
124
|
+
// then we need to make sure we don't accidentally prepend the operator
|
125
|
+
// before the comment.
|
126
|
+
blockDoc.parts[2] = concat(["&", blockDoc.parts[2]]);
|
127
|
+
} else {
|
128
|
+
// If we don't have any comments, we can just prepend the operator
|
129
|
+
blockDoc = concat(["&", blockDoc]);
|
130
|
+
}
|
131
|
+
|
132
|
+
parts.push(blockDoc);
|
133
|
+
}
|
134
|
+
|
135
|
+
return parts;
|
136
|
+
}
|
137
|
+
|
138
|
+
function printArgsAddStar(path, opts, print) {
|
139
|
+
const node = path.getValue();
|
140
|
+
const docs = path.map(print, "body");
|
141
|
+
|
142
|
+
if (node.body[1].comments) {
|
143
|
+
// If we have an array like:
|
144
|
+
//
|
145
|
+
// [
|
146
|
+
// # comment
|
147
|
+
// *values
|
148
|
+
// ]
|
149
|
+
//
|
150
|
+
// then we need to make sure we don't accidentally prepend the operator
|
151
|
+
// before the comment.
|
152
|
+
docs[1].parts[2] = concat(["*", docs[1].parts[2]]);
|
153
|
+
} else {
|
154
|
+
// If we don't have any comments, we can just prepend the operator
|
155
|
+
docs[1] = concat(["*", docs[1]]);
|
156
|
+
}
|
157
|
+
|
158
|
+
return docs[0].concat(docs[1]).concat(docs.slice(2));
|
159
|
+
}
|
160
|
+
|
161
|
+
function printBlockArg(path, opts, print) {
|
162
|
+
return concat(["&", path.call(print, "body", 0)]);
|
163
|
+
}
|
164
|
+
|
95
165
|
module.exports = {
|
96
166
|
arg_paren: printArgParen,
|
97
167
|
args: printArgs,
|
98
|
-
args_add_block:
|
99
|
-
|
100
|
-
|
101
|
-
if (path.getValue().body[1]) {
|
102
|
-
parts.push(concat(["&", path.call(print, "body", 1)]));
|
103
|
-
}
|
104
|
-
|
105
|
-
return parts;
|
106
|
-
},
|
107
|
-
args_add_star: (path, opts, print) => {
|
108
|
-
const printed = path.map(print, "body");
|
109
|
-
const parts = printed[0]
|
110
|
-
.concat([concat(["*", printed[1]])])
|
111
|
-
.concat(printed.slice(2));
|
112
|
-
|
113
|
-
return parts;
|
114
|
-
},
|
115
|
-
blockarg: (path, opts, print) => concat(["&", path.call(print, "body", 0)])
|
168
|
+
args_add_block: printArgsAddBlock,
|
169
|
+
args_add_star: printArgsAddStar,
|
170
|
+
blockarg: printBlockArg
|
116
171
|
};
|
data/src/ruby/nodes/arrays.js
CHANGED
@@ -63,33 +63,30 @@ function isSymbolArray(args) {
|
|
63
63
|
// Prints out a word that is a part of a special array literal that accepts
|
64
64
|
// interpolation. The body is an array of either plain strings or interpolated
|
65
65
|
// expressions.
|
66
|
-
function
|
66
|
+
function printArrayLiteralWord(path, opts, print) {
|
67
67
|
return concat(path.map(print, "body"));
|
68
68
|
}
|
69
69
|
|
70
70
|
// Prints out a special array literal. Accepts the parts of the array literal as
|
71
71
|
// an argument, where the first element of the parts array is a string that
|
72
72
|
// contains the special start.
|
73
|
-
function
|
73
|
+
function printArrayLiteral(start, parts) {
|
74
74
|
return group(
|
75
75
|
concat([
|
76
|
-
|
76
|
+
start,
|
77
77
|
"[",
|
78
|
-
indent(concat([softline, join(line, parts
|
78
|
+
indent(concat([softline, join(line, parts)])),
|
79
79
|
concat([softline, "]"])
|
80
80
|
])
|
81
81
|
);
|
82
82
|
}
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
return [start].concat(path.map(print, "body"));
|
91
|
-
};
|
92
|
-
}
|
84
|
+
const arrayLiteralStarts = {
|
85
|
+
qsymbols: "%i",
|
86
|
+
qwords: "%w",
|
87
|
+
symbols: "%I",
|
88
|
+
words: "%W"
|
89
|
+
};
|
93
90
|
|
94
91
|
// An array node is any literal array in Ruby. This includes all of the special
|
95
92
|
// array literals as well as regular arrays. If it is a special array literal
|
@@ -105,30 +102,40 @@ function printArray(path, opts, print) {
|
|
105
102
|
return printEmptyCollection(path, opts, "[", "]");
|
106
103
|
}
|
107
104
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
105
|
+
if (opts.rubyArrayLiteral) {
|
106
|
+
// If we have an array that contains only simple string literals with no
|
107
|
+
// spaces or interpolation, then we're going to print a %w array.
|
108
|
+
if (isStringArray(args)) {
|
109
|
+
const printString = (stringPath) => stringPath.call(print, "body", 0);
|
110
|
+
const parts = path.map(printString, "body", 0, "body");
|
113
111
|
|
114
|
-
|
115
|
-
|
112
|
+
return printArrayLiteral("%w", parts);
|
113
|
+
}
|
116
114
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
115
|
+
// If we have an array that contains only simple symbol literals with no
|
116
|
+
// interpolation, then we're going to print a %i array.
|
117
|
+
if (isSymbolArray(args)) {
|
118
|
+
const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
|
119
|
+
const parts = path.map(printSymbol, "body", 0, "body");
|
122
120
|
|
123
|
-
|
121
|
+
return printArrayLiteral("%i", parts);
|
122
|
+
}
|
124
123
|
}
|
125
124
|
|
126
125
|
// If we don't have a regular args node at this point then we have a special
|
127
126
|
// array literal. In that case we're going to print out the body (which will
|
128
127
|
// return to us an array with the first one being the start of the array) and
|
129
|
-
// send that over to the
|
128
|
+
// send that over to the printArrayLiteral function.
|
130
129
|
if (!["args", "args_add_star"].includes(args.type)) {
|
131
|
-
return
|
130
|
+
return path.call(
|
131
|
+
(arrayPath) =>
|
132
|
+
printArrayLiteral(
|
133
|
+
arrayLiteralStarts[arrayPath.getValue().type],
|
134
|
+
arrayPath.map(print, "body")
|
135
|
+
),
|
136
|
+
"body",
|
137
|
+
0
|
138
|
+
);
|
132
139
|
}
|
133
140
|
|
134
141
|
// Here we have a normal array of any type of object with no special literal
|
@@ -151,9 +158,5 @@ function printArray(path, opts, print) {
|
|
151
158
|
|
152
159
|
module.exports = {
|
153
160
|
array: printArray,
|
154
|
-
|
155
|
-
qwords: printSpecialArray("%w"),
|
156
|
-
symbols: printSpecialArray("%I"),
|
157
|
-
word: printSpecialArrayWord,
|
158
|
-
words: printSpecialArray("%W")
|
161
|
+
word: printArrayLiteralWord
|
159
162
|
};
|
data/src/ruby/nodes/calls.js
CHANGED
@@ -43,7 +43,7 @@ function printCall(path, opts, print) {
|
|
43
43
|
|
44
44
|
// If our parent node is a chained node then we're not going to group the
|
45
45
|
// right side of the expression, as we want to have a nice multi-line layout.
|
46
|
-
if (chained.includes(parentNode.type)) {
|
46
|
+
if (chained.includes(parentNode.type) && !node.comments) {
|
47
47
|
parentNode.chain = (node.chain || 0) + 1;
|
48
48
|
parentNode.callChain = (node.callChain || 0) + 1;
|
49
49
|
parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc);
|
@@ -103,7 +103,7 @@ function printMethodAddArg(path, opts, print) {
|
|
103
103
|
|
104
104
|
// If our parent node is a chained node then we're not going to group the
|
105
105
|
// right side of the expression, as we want to have a nice multi-line layout.
|
106
|
-
if (chained.includes(parentNode.type)) {
|
106
|
+
if (chained.includes(parentNode.type) && !node.comments) {
|
107
107
|
parentNode.chain = (node.chain || 0) + 1;
|
108
108
|
parentNode.breakDoc = (node.breakDoc || [methodDoc]).concat(argsDoc);
|
109
109
|
parentNode.firstReceiverType = node.firstReceiverType;
|
@@ -136,41 +136,12 @@ function printMethodAddArg(path, opts, print) {
|
|
136
136
|
return concat([methodDoc, argsDoc]);
|
137
137
|
}
|
138
138
|
|
139
|
-
// Sorbet type annotations look like the following:
|
140
|
-
//
|
141
|
-
// {method_add_block
|
142
|
-
// [{method_add_arg
|
143
|
-
// [{fcall
|
144
|
-
// [{@ident "sig"}]},
|
145
|
-
// {args []}]},
|
146
|
-
// {brace_block [nil, {stmts}]}}]}
|
147
|
-
//
|
148
|
-
function isSorbetTypeAnnotation(node) {
|
149
|
-
const [callNode, blockNode] = node.body;
|
150
|
-
|
151
|
-
return (
|
152
|
-
callNode.type === "method_add_arg" &&
|
153
|
-
callNode.body[0].type === "fcall" &&
|
154
|
-
callNode.body[0].body[0].body === "sig" &&
|
155
|
-
callNode.body[1].type === "args" &&
|
156
|
-
callNode.body[1].body.length === 0 &&
|
157
|
-
blockNode
|
158
|
-
);
|
159
|
-
}
|
160
|
-
|
161
139
|
function printMethodAddBlock(path, opts, print) {
|
162
140
|
const node = path.getValue();
|
163
141
|
|
164
142
|
const [callNode, blockNode] = node.body;
|
165
143
|
const [callDoc, blockDoc] = path.map(print, "body");
|
166
144
|
|
167
|
-
// Very special handling here for sorbet type annotations. They look like Ruby
|
168
|
-
// code, but they're not actually Ruby code, so we're not going to mess with
|
169
|
-
// them at all.
|
170
|
-
if (isSorbetTypeAnnotation(node)) {
|
171
|
-
return opts.originalText.slice(opts.locStart(node), opts.locEnd(node));
|
172
|
-
}
|
173
|
-
|
174
145
|
// Don't bother trying to do any kind of fancy toProc transform if the option
|
175
146
|
// is disabled.
|
176
147
|
if (opts.rubyToProc) {
|
data/src/ruby/nodes/class.js
CHANGED
@@ -1,34 +1,22 @@
|
|
1
|
-
const {
|
2
|
-
|
3
|
-
group,
|
4
|
-
hardline,
|
5
|
-
ifBreak,
|
6
|
-
indent,
|
7
|
-
line
|
8
|
-
} = require("../../prettier");
|
1
|
+
const { concat, group, hardline, indent } = require("../../prettier");
|
2
|
+
const { isEmptyBodyStmt } = require("../../utils");
|
9
3
|
|
10
4
|
function printClass(path, opts, print) {
|
11
5
|
const [_constant, superclass, bodystmt] = path.getValue().body;
|
12
|
-
const stmts = bodystmt.body[0];
|
13
6
|
|
14
7
|
const parts = ["class ", path.call(print, "body", 0)];
|
15
8
|
if (superclass) {
|
16
9
|
parts.push(" < ", path.call(print, "body", 1));
|
17
10
|
}
|
18
11
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
stmts.body.length === 1 &&
|
23
|
-
stmts.body[0].type === "void_stmt" &&
|
24
|
-
!stmts.body[0].comments
|
25
|
-
) {
|
26
|
-
return group(concat([concat(parts), ifBreak(line, "; "), "end"]));
|
12
|
+
const declaration = group(concat(parts));
|
13
|
+
if (isEmptyBodyStmt(bodystmt)) {
|
14
|
+
return group(concat([declaration, hardline, "end"]));
|
27
15
|
}
|
28
16
|
|
29
17
|
return group(
|
30
18
|
concat([
|
31
|
-
|
19
|
+
declaration,
|
32
20
|
indent(concat([hardline, path.call(print, "body", 2)])),
|
33
21
|
concat([hardline, "end"])
|
34
22
|
])
|
@@ -36,16 +24,11 @@ function printClass(path, opts, print) {
|
|
36
24
|
}
|
37
25
|
|
38
26
|
function printModule(path, opts, print) {
|
27
|
+
const node = path.getValue();
|
39
28
|
const declaration = group(concat(["module ", path.call(print, "body", 0)]));
|
40
29
|
|
41
|
-
|
42
|
-
|
43
|
-
if (
|
44
|
-
stmts.body.length === 1 &&
|
45
|
-
stmts.body[0].type === "void_stmt" &&
|
46
|
-
!stmts.body[0].comments
|
47
|
-
) {
|
48
|
-
return group(concat([declaration, ifBreak(line, "; "), "end"]));
|
30
|
+
if (isEmptyBodyStmt(node.body[1])) {
|
31
|
+
return group(concat([declaration, hardline, "end"]));
|
49
32
|
}
|
50
33
|
|
51
34
|
return group(
|
@@ -58,9 +41,16 @@ function printModule(path, opts, print) {
|
|
58
41
|
}
|
59
42
|
|
60
43
|
function printSClass(path, opts, print) {
|
44
|
+
const bodystmt = path.getValue().body[1];
|
45
|
+
const declaration = concat(["class << ", path.call(print, "body", 0)]);
|
46
|
+
|
47
|
+
if (isEmptyBodyStmt(bodystmt)) {
|
48
|
+
return group(concat([declaration, hardline, "end"]));
|
49
|
+
}
|
50
|
+
|
61
51
|
return group(
|
62
52
|
concat([
|
63
|
-
|
53
|
+
declaration,
|
64
54
|
indent(concat([hardline, path.call(print, "body", 1)])),
|
65
55
|
concat([hardline, "end"])
|
66
56
|
])
|