prettier 1.5.5 → 1.6.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 +17 -1
- data/CONTRIBUTING.md +2 -2
- data/README.md +31 -12
- data/node_modules/prettier/bin-prettier.js +13702 -11629
- data/node_modules/prettier/index.js +19198 -16572
- data/node_modules/prettier/parser-angular.js +61 -40
- data/node_modules/prettier/parser-babel.js +22 -1
- data/node_modules/prettier/parser-espree.js +22 -1
- data/node_modules/prettier/parser-flow.js +22 -1
- data/node_modules/prettier/parser-glimmer.js +1 -1
- data/node_modules/prettier/parser-graphql.js +1 -1
- data/node_modules/prettier/parser-html.js +82 -63
- data/node_modules/prettier/parser-markdown.js +24 -9
- data/node_modules/prettier/parser-meriyah.js +22 -1
- data/node_modules/prettier/parser-postcss.js +22 -1
- data/node_modules/prettier/parser-typescript.js +22 -1
- data/node_modules/prettier/parser-yaml.js +2 -2
- data/node_modules/prettier/third-party.js +1042 -833
- data/package.json +3 -3
- data/rubocop.yml +9 -0
- data/src/haml/parser.js +5 -4
- data/src/haml/printer.js +428 -18
- data/src/parser/parseSync.js +8 -6
- data/src/plugin.js +1 -1
- data/src/rbs/parser.js +1 -3
- data/src/rbs/printer.js +35 -7
- data/src/ruby/nodes/args.js +66 -22
- data/src/ruby/nodes/calls.js +8 -1
- data/src/ruby/nodes/conditionals.js +47 -45
- data/src/ruby/nodes/hashes.js +5 -14
- data/src/ruby/nodes/params.js +2 -9
- data/src/ruby/nodes/strings.js +95 -2
- data/src/ruby/parser.js +1 -3
- data/src/ruby/parser.rb +52 -29
- data/src/ruby/printer.js +10 -1
- data/src/utils/inlineEnsureParens.js +1 -0
- data/src/utils/skipAssignIndent.js +8 -1
- metadata +3 -12
- data/src/haml/nodes/comment.js +0 -27
- data/src/haml/nodes/doctype.js +0 -34
- data/src/haml/nodes/filter.js +0 -16
- data/src/haml/nodes/hamlComment.js +0 -21
- data/src/haml/nodes/plain.js +0 -6
- data/src/haml/nodes/root.js +0 -8
- data/src/haml/nodes/script.js +0 -33
- data/src/haml/nodes/silentScript.js +0 -59
- data/src/haml/nodes/tag.js +0 -232
data/src/parser/parseSync.js
CHANGED
@@ -135,15 +135,17 @@ function parseSync(parser, source, opts) {
|
|
135
135
|
// using unix sockets.
|
136
136
|
if (
|
137
137
|
stderr.includes("invalid option -- U") ||
|
138
|
+
stderr.includes("invalid option -- 'u'") ||
|
138
139
|
stderr.includes("Protocol not supported")
|
139
140
|
) {
|
140
141
|
throw new Error(`
|
141
|
-
@prettier/plugin-ruby uses
|
142
|
-
|
143
|
-
|
144
|
-
does not support unix sockets. To solve this either
|
145
|
-
of netcat that you're using and use a
|
146
|
-
the value of the rubyNetcatCommand
|
142
|
+
@prettier/plugin-ruby uses unix sockets to communicate between the node.js
|
143
|
+
process running prettier and an underlying Ruby process used for parsing.
|
144
|
+
Unfortunately the command that it tried to use to do that
|
145
|
+
(${netcat.command}) does not support unix sockets. To solve this either
|
146
|
+
uninstall the version of ${netcat.command} that you're using and use a
|
147
|
+
different implementation, or change the value of the rubyNetcatCommand
|
148
|
+
option in your prettier configuration.
|
147
149
|
`);
|
148
150
|
}
|
149
151
|
|
data/src/plugin.js
CHANGED
@@ -10,7 +10,7 @@ const hamlParser = require("./haml/parser");
|
|
10
10
|
/*
|
11
11
|
* metadata mostly pulled from linguist and rubocop:
|
12
12
|
* https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
|
13
|
-
* https://github.com/rubocop
|
13
|
+
* https://github.com/rubocop/rubocop/blob/master/spec/rubocop/target_finder_spec.rb
|
14
14
|
*/
|
15
15
|
|
16
16
|
module.exports = {
|
data/src/rbs/parser.js
CHANGED
@@ -8,12 +8,10 @@ function parse(text, _parsers, opts) {
|
|
8
8
|
return parseSync("rbs", text, opts);
|
9
9
|
}
|
10
10
|
|
11
|
-
const pragmaPattern = /#\s*@(prettier|format)/;
|
12
|
-
|
13
11
|
// This function handles checking whether or not the source string has the
|
14
12
|
// pragma for prettier. This is an optional workflow for incremental adoption.
|
15
13
|
function hasPragma(text) {
|
16
|
-
return
|
14
|
+
return /^\s*#[^\S\n]*@(format|prettier)\s*(\n|$)/.test(text);
|
17
15
|
}
|
18
16
|
|
19
17
|
// This function is critical for comments and cursor support, and is responsible
|
data/src/rbs/printer.js
CHANGED
@@ -147,7 +147,7 @@ function printNode(path, opts, print) {
|
|
147
147
|
|
148
148
|
// This is the big function that prints out any individual type, which can
|
149
149
|
// look like all kinds of things, listed in the case statement below.
|
150
|
-
function printType(path, {
|
150
|
+
function printType(path, { forceParens = false } = {}) {
|
151
151
|
const node = path.getValue();
|
152
152
|
|
153
153
|
switch (node.class) {
|
@@ -157,7 +157,13 @@ function printNode(path, opts, print) {
|
|
157
157
|
}
|
158
158
|
return node.literal;
|
159
159
|
case "optional":
|
160
|
-
return concat([
|
160
|
+
return concat([
|
161
|
+
path.call(
|
162
|
+
(typePath) => printType(typePath, { forceParens: true }),
|
163
|
+
"type"
|
164
|
+
),
|
165
|
+
"?"
|
166
|
+
]);
|
161
167
|
case "tuple":
|
162
168
|
// If we don't have any sub types, we explicitly need the space in between
|
163
169
|
// the brackets to not confuse the parser.
|
@@ -173,14 +179,29 @@ function printNode(path, opts, print) {
|
|
173
179
|
join(concat([line, "| "]), path.map(printType, "types"))
|
174
180
|
);
|
175
181
|
|
176
|
-
if (
|
182
|
+
if (forceParens) {
|
183
|
+
return concat(["(", doc, ")"]);
|
184
|
+
}
|
185
|
+
|
186
|
+
return doc;
|
187
|
+
}
|
188
|
+
case "intersection": {
|
189
|
+
const doc = group(
|
190
|
+
join(
|
191
|
+
concat([line, "& "]),
|
192
|
+
path.map(
|
193
|
+
(typePath) => printType(typePath, { forceParens: true }),
|
194
|
+
"types"
|
195
|
+
)
|
196
|
+
)
|
197
|
+
);
|
198
|
+
|
199
|
+
if (forceParens) {
|
177
200
|
return concat(["(", doc, ")"]);
|
178
201
|
}
|
179
202
|
|
180
203
|
return doc;
|
181
204
|
}
|
182
|
-
case "intersection":
|
183
|
-
return group(join(concat([line, "& "]), path.map(printType, "types")));
|
184
205
|
case "class_singleton":
|
185
206
|
return concat(["singleton(", node.name, ")"]);
|
186
207
|
case "proc":
|
@@ -521,7 +542,7 @@ function printNode(path, opts, print) {
|
|
521
542
|
parts.push(
|
522
543
|
"-> ",
|
523
544
|
path.call(
|
524
|
-
(typePath) => printType(typePath, {
|
545
|
+
(typePath) => printType(typePath, { forceParens: true }),
|
525
546
|
"type",
|
526
547
|
"return_type"
|
527
548
|
)
|
@@ -609,7 +630,14 @@ function hasPrettierIgnore(path) {
|
|
609
630
|
return node.comment && node.comment.string.includes("prettier-ignore");
|
610
631
|
}
|
611
632
|
|
633
|
+
// This function handles adding the format pragma to a source string. This is an
|
634
|
+
// optional workflow for incremental adoption.
|
635
|
+
function insertPragma(text) {
|
636
|
+
return `# @format\n${text}`;
|
637
|
+
}
|
638
|
+
|
612
639
|
module.exports = {
|
613
640
|
print: printNode,
|
614
|
-
hasPrettierIgnore
|
641
|
+
hasPrettierIgnore,
|
642
|
+
insertPragma
|
615
643
|
};
|
data/src/ruby/nodes/args.js
CHANGED
@@ -108,12 +108,18 @@ function printArgs(path, { rubyToProc }, print) {
|
|
108
108
|
|
109
109
|
function printArgsAddBlock(path, opts, print) {
|
110
110
|
const node = path.getValue();
|
111
|
+
const blockNode = node.body[1];
|
112
|
+
|
111
113
|
const parts = path.call(print, "body", 0);
|
112
114
|
|
113
|
-
if (
|
115
|
+
if (blockNode) {
|
114
116
|
let blockDoc = path.call(print, "body", 1);
|
115
117
|
|
116
|
-
if (
|
118
|
+
if (!(blockNode.comments || []).some(({ leading }) => leading)) {
|
119
|
+
// If we don't have any leading comments, we can just prepend the
|
120
|
+
// operator.
|
121
|
+
blockDoc = concat(["&", blockDoc]);
|
122
|
+
} else if (Array.isArray(blockDoc[0])) {
|
117
123
|
// If we have a method call like:
|
118
124
|
//
|
119
125
|
// foo(
|
@@ -123,10 +129,20 @@ function printArgsAddBlock(path, opts, print) {
|
|
123
129
|
//
|
124
130
|
// then we need to make sure we don't accidentally prepend the operator
|
125
131
|
// before the comment.
|
126
|
-
|
132
|
+
//
|
133
|
+
// In prettier >= 2.3.0, the comments are printed as an array before the
|
134
|
+
// content. I don't love this kind of reflection, but it's the simplest
|
135
|
+
// way at the moment to get this right.
|
136
|
+
blockDoc = blockDoc[0].concat(
|
137
|
+
concat(["&", blockDoc[1]]),
|
138
|
+
blockDoc.slice(2)
|
139
|
+
);
|
127
140
|
} else {
|
128
|
-
//
|
129
|
-
|
141
|
+
// In prettier < 2.3.0, the comments are printed as part of a concat, so
|
142
|
+
// we can reflect on how many leading comments there are to determine
|
143
|
+
// which doc node we should modify.
|
144
|
+
const index = blockNode.comments.filter(({ leading }) => leading).length;
|
145
|
+
blockDoc.parts[index] = concat(["&", blockDoc.parts[index]]);
|
130
146
|
}
|
131
147
|
|
132
148
|
parts.push(blockDoc);
|
@@ -136,10 +152,34 @@ function printArgsAddBlock(path, opts, print) {
|
|
136
152
|
}
|
137
153
|
|
138
154
|
function printArgsAddStar(path, opts, print) {
|
139
|
-
|
140
|
-
|
155
|
+
let docs = [];
|
156
|
+
|
157
|
+
path.each((argPath, argIndex) => {
|
158
|
+
const doc = print(argPath);
|
159
|
+
|
160
|
+
// If it's the first child, then it's an array of args, so we're going to
|
161
|
+
// concat that onto the existing docs if there are any.
|
162
|
+
if (argIndex === 0) {
|
163
|
+
if (doc.length > 0) {
|
164
|
+
docs = docs.concat(doc);
|
165
|
+
}
|
166
|
+
return;
|
167
|
+
}
|
168
|
+
|
169
|
+
// If it's after the splat, then it's an individual arg, so we're just going
|
170
|
+
// to push it onto the array.
|
171
|
+
if (argIndex !== 1) {
|
172
|
+
docs.push(doc);
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
|
176
|
+
// If we don't have any leading comments, we can just prepend the operator.
|
177
|
+
const argsNode = argPath.getValue();
|
178
|
+
if (!(argsNode.comments || []).some(({ leading }) => leading)) {
|
179
|
+
docs.push(concat(["*", doc]));
|
180
|
+
return;
|
181
|
+
}
|
141
182
|
|
142
|
-
if (node.body[1].comments) {
|
143
183
|
// If we have an array like:
|
144
184
|
//
|
145
185
|
// [
|
@@ -147,22 +187,26 @@ function printArgsAddStar(path, opts, print) {
|
|
147
187
|
// *values
|
148
188
|
// ]
|
149
189
|
//
|
150
|
-
// or if we have an array like:
|
151
|
-
//
|
152
|
-
// [
|
153
|
-
// *values # comment
|
154
|
-
// ]
|
155
|
-
//
|
156
190
|
// then we need to make sure we don't accidentally prepend the operator
|
157
|
-
// before the comment.
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
//
|
162
|
-
|
163
|
-
|
191
|
+
// before the comment(s).
|
192
|
+
//
|
193
|
+
// In prettier >= 2.3.0, the comments are printed as an array before the
|
194
|
+
// content. I don't love this kind of reflection, but it's the simplest way
|
195
|
+
// at the moment to get this right.
|
196
|
+
if (Array.isArray(doc[0])) {
|
197
|
+
docs.push(doc[0].concat(concat(["*", doc[1]]), doc.slice(2)));
|
198
|
+
return;
|
199
|
+
}
|
200
|
+
|
201
|
+
// In prettier < 2.3.0, the comments are printed as part of a concat, so
|
202
|
+
// we can reflect on how many leading comments there are to determine which
|
203
|
+
// doc node we should modify.
|
204
|
+
const index = argsNode.comments.filter(({ leading }) => leading).length;
|
205
|
+
doc.parts[index] = concat(["*", doc.parts[index]]);
|
206
|
+
docs = docs.concat(doc);
|
207
|
+
}, "body");
|
164
208
|
|
165
|
-
return docs
|
209
|
+
return docs;
|
166
210
|
}
|
167
211
|
|
168
212
|
function printBlockArg(path, opts, print) {
|
data/src/ruby/nodes/calls.js
CHANGED
@@ -4,6 +4,7 @@ const {
|
|
4
4
|
hardline,
|
5
5
|
ifBreak,
|
6
6
|
indent,
|
7
|
+
join,
|
7
8
|
softline
|
8
9
|
} = require("../../prettier");
|
9
10
|
const { makeCall, noIndent } = require("../../utils");
|
@@ -133,7 +134,13 @@ function printMethodAddArg(path, opts, print) {
|
|
133
134
|
);
|
134
135
|
}
|
135
136
|
|
136
|
-
|
137
|
+
// If there are already parentheses, then we can just use the doc that's
|
138
|
+
// already printed.
|
139
|
+
if (argNode.type == "arg_paren") {
|
140
|
+
return concat([methodDoc, argsDoc]);
|
141
|
+
}
|
142
|
+
|
143
|
+
return concat([methodDoc, " ", join(", ", argsDoc), " "]);
|
137
144
|
}
|
138
145
|
|
139
146
|
function printMethodAddBlock(path, opts, print) {
|
@@ -190,57 +190,59 @@ const canTernary = (path) => {
|
|
190
190
|
};
|
191
191
|
|
192
192
|
// A normalized print function for both `if` and `unless` nodes.
|
193
|
-
const printConditional =
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
193
|
+
const printConditional =
|
194
|
+
(keyword) =>
|
195
|
+
(path, { rubyModifier }, print) => {
|
196
|
+
if (canTernary(path)) {
|
197
|
+
let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
|
198
|
+
printTernaryClauses(
|
199
|
+
keyword,
|
200
|
+
path.call(print, "body", 1),
|
201
|
+
path.call(print, "body", 2, "body", 0)
|
202
|
+
)
|
203
|
+
);
|
204
|
+
|
205
|
+
if (["binary", "call"].includes(path.getParentNode().type)) {
|
206
|
+
ternaryParts = ["("].concat(ternaryParts).concat(")");
|
207
|
+
}
|
208
|
+
|
209
|
+
return group(
|
210
|
+
ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
|
211
|
+
);
|
205
212
|
}
|
206
213
|
|
207
|
-
|
208
|
-
ifBreak(printWithAddition(keyword, path, print), concat(ternaryParts))
|
209
|
-
);
|
210
|
-
}
|
211
|
-
|
212
|
-
const [predicate, statements, addition] = path.getValue().body;
|
214
|
+
const [predicate, statements, addition] = path.getValue().body;
|
213
215
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
216
|
+
// If there's an additional clause that wasn't matched earlier, we know we
|
217
|
+
// can't go for the inline option.
|
218
|
+
if (addition) {
|
219
|
+
return group(printWithAddition(keyword, path, print, { breaking: true }));
|
220
|
+
}
|
219
221
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
222
|
+
// If the body of the conditional is empty, then we explicitly have to use the
|
223
|
+
// block form.
|
224
|
+
if (isEmptyStmts(statements)) {
|
225
|
+
return concat([
|
226
|
+
`${keyword} `,
|
227
|
+
align(keyword.length + 1, path.call(print, "body", 0)),
|
228
|
+
concat([hardline, "end"])
|
229
|
+
]);
|
230
|
+
}
|
229
231
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
232
|
+
// If the predicate of the conditional contains an assignment, then we can't
|
233
|
+
// know for sure that it doesn't impact the body of the conditional, so we
|
234
|
+
// have to default to the block form.
|
235
|
+
if (containsAssignment(predicate)) {
|
236
|
+
return concat([
|
237
|
+
`${keyword} `,
|
238
|
+
align(keyword.length + 1, path.call(print, "body", 0)),
|
239
|
+
indent(concat([hardline, path.call(print, "body", 1)])),
|
240
|
+
concat([hardline, "end"])
|
241
|
+
]);
|
242
|
+
}
|
241
243
|
|
242
|
-
|
243
|
-
};
|
244
|
+
return printSingle(keyword)(path, { rubyModifier }, print);
|
245
|
+
};
|
244
246
|
|
245
247
|
module.exports = {
|
246
248
|
else: (path, opts, print) => {
|
data/src/ruby/nodes/hashes.js
CHANGED
@@ -56,28 +56,19 @@ function printHashKeyLabel(path, print) {
|
|
56
56
|
case "symbol_literal":
|
57
57
|
return concat([path.call(print, "body", 0), ":"]);
|
58
58
|
case "dyna_symbol": {
|
59
|
-
|
60
|
-
|
61
|
-
// We're going to slice off the starting colon character so that we can
|
62
|
-
// move it to the end. If there are comments, then we're going to go
|
63
|
-
// further into the printed doc nodes.
|
64
|
-
if (parts[0] === ":") {
|
65
|
-
parts.splice(0, 1);
|
66
|
-
} else {
|
67
|
-
parts[1].parts.splice(0, 1);
|
68
|
-
}
|
69
|
-
|
70
|
-
return concat(parts.concat(":"));
|
59
|
+
return concat([print(path), ":"]);
|
71
60
|
}
|
72
61
|
}
|
73
62
|
}
|
74
63
|
|
75
64
|
function printHashKeyRocket(path, print) {
|
76
65
|
const node = path.getValue();
|
77
|
-
|
66
|
+
let doc = print(path);
|
78
67
|
|
79
68
|
if (node.type === "@label") {
|
80
|
-
|
69
|
+
doc = concat([":", doc.slice(0, doc.length - 1)]);
|
70
|
+
} else if (node.type === "dyna_symbol") {
|
71
|
+
doc = concat([":", doc]);
|
81
72
|
}
|
82
73
|
|
83
74
|
return concat([doc, " =>"]);
|
data/src/ruby/nodes/params.js
CHANGED
@@ -17,15 +17,8 @@ function printRestParam(symbol) {
|
|
17
17
|
}
|
18
18
|
|
19
19
|
function printParams(path, opts, print) {
|
20
|
-
const [
|
21
|
-
|
22
|
-
optls,
|
23
|
-
rest,
|
24
|
-
post,
|
25
|
-
kwargs,
|
26
|
-
kwargRest,
|
27
|
-
block
|
28
|
-
] = path.getValue().body;
|
20
|
+
const [reqs, optls, rest, post, kwargs, kwargRest, block] =
|
21
|
+
path.getValue().body;
|
29
22
|
let parts = [];
|
30
23
|
|
31
24
|
if (reqs) {
|
data/src/ruby/nodes/strings.js
CHANGED
@@ -82,13 +82,106 @@ function printChar(path, { rubySingleQuote }, _print) {
|
|
82
82
|
return concat([quote, body.slice(1), quote]);
|
83
83
|
}
|
84
84
|
|
85
|
+
function printPercentSDynaSymbol(path, opts, print) {
|
86
|
+
const node = path.getValue();
|
87
|
+
const parts = [];
|
88
|
+
|
89
|
+
// Push on the quote, which includes the opening character.
|
90
|
+
parts.push(node.quote);
|
91
|
+
|
92
|
+
path.each((childPath) => {
|
93
|
+
const childNode = childPath.getValue();
|
94
|
+
|
95
|
+
if (childNode.type !== "@tstring_content") {
|
96
|
+
// Here we are printing an embedded variable or expression.
|
97
|
+
parts.push(print(childPath));
|
98
|
+
} else {
|
99
|
+
// Here we are printing plain string content.
|
100
|
+
parts.push(join(literalline, childNode.body.split("\n")));
|
101
|
+
}
|
102
|
+
}, "body");
|
103
|
+
|
104
|
+
// Push on the closing character, which is the opposite of the third
|
105
|
+
// character from the opening.
|
106
|
+
parts.push(quotePairs[node.quote[2]]);
|
107
|
+
|
108
|
+
return concat(parts);
|
109
|
+
}
|
110
|
+
|
111
|
+
// We don't actually want to print %s symbols, as they're much more rarely seen
|
112
|
+
// in the wild. But we're going to be forced into it if it's a multi-line symbol
|
113
|
+
// or if the quoting would get super complicated.
|
114
|
+
function shouldPrintPercentSDynaSymbol(node) {
|
115
|
+
// We shouldn't print a %s dyna symbol if it was not already that way in the
|
116
|
+
// original source.
|
117
|
+
if (node.quote[0] !== "%") {
|
118
|
+
return false;
|
119
|
+
}
|
120
|
+
|
121
|
+
// Here we're going to check if there is a closing character, a new line, or a
|
122
|
+
// quote in the content of the dyna symbol. If there is, then quoting could
|
123
|
+
// get weird, so just bail out and stick to the original bounds in the source.
|
124
|
+
const closing = quotePairs[node.quote[2]];
|
125
|
+
|
126
|
+
return node.body.some(
|
127
|
+
(child) =>
|
128
|
+
child.type === "@tstring_content" &&
|
129
|
+
(child.body.includes("\n") ||
|
130
|
+
child.body.includes(closing) ||
|
131
|
+
child.body.includes("'") ||
|
132
|
+
child.body.includes('"'))
|
133
|
+
);
|
134
|
+
}
|
135
|
+
|
85
136
|
// Prints a dynamic symbol. Assumes there's a quote property attached to the
|
86
137
|
// node that will tell us which quote to use when printing. We're just going to
|
87
138
|
// use whatever quote was provided.
|
139
|
+
//
|
140
|
+
// In the case of a plain dyna symbol, node.quote will be either :" or :'
|
141
|
+
// For %s dyna symbols, node.quote will be %s[, %s(, %s{, or %s<
|
88
142
|
function printDynaSymbol(path, opts, print) {
|
89
|
-
const
|
143
|
+
const node = path.getValue();
|
144
|
+
|
145
|
+
if (shouldPrintPercentSDynaSymbol(node)) {
|
146
|
+
return printPercentSDynaSymbol(path, opts, print);
|
147
|
+
}
|
148
|
+
|
149
|
+
const parts = [];
|
150
|
+
let quote;
|
151
|
+
|
152
|
+
if (isQuoteLocked(node)) {
|
153
|
+
if (node.quote.startsWith("%")) {
|
154
|
+
quote = opts.rubySingleQuote ? "'" : '"';
|
155
|
+
} else {
|
156
|
+
quote = node.quote.slice(1);
|
157
|
+
}
|
158
|
+
} else {
|
159
|
+
quote = opts.rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
|
160
|
+
}
|
161
|
+
|
162
|
+
parts.push(quote);
|
163
|
+
path.each((childPath) => {
|
164
|
+
const child = childPath.getValue();
|
165
|
+
|
166
|
+
if (child.type !== "@tstring_content") {
|
167
|
+
parts.push(print(childPath));
|
168
|
+
} else {
|
169
|
+
parts.push(
|
170
|
+
join(literalline, normalizeQuotes(child.body, quote).split("\n"))
|
171
|
+
);
|
172
|
+
}
|
173
|
+
}, "body");
|
174
|
+
|
175
|
+
parts.push(quote);
|
176
|
+
|
177
|
+
// If we're inside of an assoc_new node as the key, then it will handle
|
178
|
+
// printing the : on its own since it could change sides.
|
179
|
+
const parentNode = path.getParentNode();
|
180
|
+
if (parentNode.type !== "assoc_new" || parentNode.body[0] !== node) {
|
181
|
+
parts.unshift(":");
|
182
|
+
}
|
90
183
|
|
91
|
-
return concat(
|
184
|
+
return concat(parts);
|
92
185
|
}
|
93
186
|
|
94
187
|
function printStringConcat(path, opts, print) {
|