prettier 0.16.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +408 -198
- data/CONTRIBUTING.md +1 -1
- data/LICENSE +1 -1
- data/README.md +64 -40
- data/node_modules/prettier/bin-prettier.js +33712 -25689
- data/node_modules/prettier/index.js +32389 -24963
- data/node_modules/prettier/third-party.js +10733 -3849
- data/package.json +35 -7
- data/src/haml.js +3 -3
- data/src/haml/embed.js +2 -2
- data/src/haml/nodes/silentScript.js +32 -3
- data/src/haml/nodes/tag.js +14 -8
- data/src/haml/parse.rb +6 -7
- data/src/nodes/alias.js +2 -2
- data/src/nodes/args.js +9 -2
- data/src/nodes/arrays.js +6 -6
- data/src/nodes/blocks.js +7 -8
- data/src/nodes/calls.js +45 -10
- data/src/nodes/case.js +1 -1
- data/src/nodes/commands.js +21 -6
- data/src/nodes/conditionals.js +19 -15
- data/src/nodes/hashes.js +2 -2
- data/src/nodes/hooks.js +1 -1
- data/src/nodes/ints.js +1 -1
- data/src/nodes/lambdas.js +1 -1
- data/src/nodes/loops.js +16 -0
- data/src/nodes/methods.js +2 -2
- data/src/nodes/operators.js +11 -3
- data/src/nodes/params.js +4 -2
- data/src/nodes/regexp.js +1 -1
- data/src/nodes/rescue.js +5 -1
- data/src/nodes/return.js +16 -4
- data/src/nodes/strings.js +54 -77
- data/src/parse.js +8 -3
- data/src/prettier.js +0 -1
- data/src/ripper.rb +29 -21
- data/src/ruby.js +12 -4
- data/src/toProc.js +30 -3
- data/src/utils.js +6 -6
- metadata +7 -8
- data/src/escapePattern.js +0 -45
data/src/nodes/case.js
CHANGED
@@ -42,7 +42,7 @@ module.exports = {
|
|
42
42
|
|
43
43
|
// It's possible in a when to just have empty void statements, in which case
|
44
44
|
// we would skip adding the body.
|
45
|
-
if (!stmts.parts.every(part => !part)) {
|
45
|
+
if (!stmts.parts.every((part) => !part)) {
|
46
46
|
parts.push(indent(concat([hardline, stmts])));
|
47
47
|
}
|
48
48
|
|
data/src/nodes/commands.js
CHANGED
@@ -1,11 +1,27 @@
|
|
1
1
|
const { align, concat, group, ifBreak, join, line } = require("../prettier");
|
2
2
|
const { docLength, makeArgs, makeCall } = require("../utils");
|
3
3
|
|
4
|
-
const hasDef = node =>
|
4
|
+
const hasDef = (node) =>
|
5
5
|
node.body[1].type === "args_add_block" &&
|
6
6
|
node.body[1].body[0].type === "args" &&
|
7
7
|
node.body[1].body[0].body[0] &&
|
8
|
-
node.body[1].body[0].body[0].type
|
8
|
+
["def", "defs"].includes(node.body[1].body[0].body[0].type);
|
9
|
+
|
10
|
+
// Very special handling case for rspec matchers. In general with rspec matchers
|
11
|
+
// you expect to see something like:
|
12
|
+
//
|
13
|
+
// expect(foo).to receive(:bar).with(
|
14
|
+
// 'one',
|
15
|
+
// 'two',
|
16
|
+
// 'three',
|
17
|
+
// 'four',
|
18
|
+
// 'five'
|
19
|
+
// )
|
20
|
+
//
|
21
|
+
// In this case the arguments are aligned to the left side as opposed to being
|
22
|
+
// aligned with the `receive` call.
|
23
|
+
const skipArgsAlign = (path) =>
|
24
|
+
["to", "not_to"].includes(path.getValue().body[2].body);
|
9
25
|
|
10
26
|
module.exports = {
|
11
27
|
command: (path, opts, print) => {
|
@@ -53,10 +69,9 @@ module.exports = {
|
|
53
69
|
}
|
54
70
|
|
55
71
|
const joinedArgs = join(concat([",", line]), args);
|
56
|
-
const breakArgs =
|
57
|
-
|
58
|
-
|
59
|
-
: align(docLength(concat(parts)), joinedArgs);
|
72
|
+
const breakArgs = skipArgsAlign(path)
|
73
|
+
? joinedArgs
|
74
|
+
: align(docLength(concat(parts)), joinedArgs);
|
60
75
|
|
61
76
|
const commandDoc = group(
|
62
77
|
ifBreak(concat(parts.concat(breakArgs)), concat(parts.concat(joinedArgs)))
|
data/src/nodes/conditionals.js
CHANGED
@@ -23,7 +23,7 @@ const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
|
|
23
23
|
// For the unary `not` operator, we need to explicitly add parentheses to it in
|
24
24
|
// order for it to be valid from within a ternary. Otherwise if the clause of
|
25
25
|
// the ternary isn't a unary `not`, we can just pass it along.
|
26
|
-
const printTernaryClause = clause => {
|
26
|
+
const printTernaryClause = (clause) => {
|
27
27
|
if (clause.type === "concat") {
|
28
28
|
const [part] = clause.parts;
|
29
29
|
|
@@ -75,23 +75,21 @@ const printTernary = (path, _opts, print) => {
|
|
75
75
|
);
|
76
76
|
};
|
77
77
|
|
78
|
-
|
79
|
-
|
78
|
+
// Prints an `if_mod` or `unless_mod` node. Because it was previously in the
|
79
|
+
// modifier form, we're guaranteed to not have an additional node, so we can
|
80
|
+
// just work with the predicate and the body.
|
81
|
+
const printSingle = (keyword) => (path, { inlineConditionals }, print) => {
|
82
|
+
const multiline = concat([
|
80
83
|
`${keyword} `,
|
81
84
|
align(keyword.length + 1, path.call(print, "body", 0)),
|
82
85
|
indent(concat([softline, path.call(print, "body", 1)])),
|
83
86
|
concat([softline, "end"])
|
84
87
|
]);
|
85
88
|
|
86
|
-
// Prints an `if_mod` or `unless_mod` node. Because it was previously in the
|
87
|
-
// modifier form, we're guaranteed to not have an additional node, so we can
|
88
|
-
// just work with the predicate and the body.
|
89
|
-
const printSingle = keyword => (path, { inlineConditionals }, print) => {
|
90
|
-
const multiline = makeSingleBlockForm(keyword, path, print);
|
91
|
-
|
92
89
|
const [_predicate, stmts] = path.getValue().body;
|
93
90
|
const hasComments =
|
94
|
-
stmts.type === "stmts" &&
|
91
|
+
stmts.type === "stmts" &&
|
92
|
+
stmts.body.some((stmt) => stmt.type === "@comment");
|
95
93
|
|
96
94
|
if (!inlineConditionals || hasComments) {
|
97
95
|
return multiline;
|
@@ -155,7 +153,7 @@ const noTernary = [
|
|
155
153
|
// Certain expressions cannot be reduced to a ternary without adding parens
|
156
154
|
// around them. In this case we say they cannot be ternaried and default instead
|
157
155
|
// to breaking them into multiple lines.
|
158
|
-
const canTernaryStmts = stmts => {
|
156
|
+
const canTernaryStmts = (stmts) => {
|
159
157
|
if (stmts.body.length !== 1) {
|
160
158
|
return false;
|
161
159
|
}
|
@@ -178,10 +176,11 @@ const canTernaryStmts = stmts => {
|
|
178
176
|
// is of the "else" type. Both the body of the main node and the body of the
|
179
177
|
// additional node must have only one statement, and that statement list must
|
180
178
|
// pass the `canTernaryStmts` check.
|
181
|
-
const canTernary = path => {
|
182
|
-
const [
|
179
|
+
const canTernary = (path) => {
|
180
|
+
const [predicate, stmts, addition] = path.getValue().body;
|
183
181
|
|
184
182
|
return (
|
183
|
+
!["assign", "opassign"].includes(predicate.type) &&
|
185
184
|
addition &&
|
186
185
|
addition.type === "else" &&
|
187
186
|
[stmts, addition.body[0]].every(canTernaryStmts)
|
@@ -189,7 +188,7 @@ const canTernary = path => {
|
|
189
188
|
};
|
190
189
|
|
191
190
|
// A normalized print function for both `if` and `unless` nodes.
|
192
|
-
const printConditional = keyword => (path, { inlineConditionals }, print) => {
|
191
|
+
const printConditional = (keyword) => (path, { inlineConditionals }, print) => {
|
193
192
|
if (canTernary(path)) {
|
194
193
|
let ternaryParts = [path.call(print, "body", 0), " ? "].concat(
|
195
194
|
printTernaryClauses(
|
@@ -230,7 +229,12 @@ const printConditional = keyword => (path, { inlineConditionals }, print) => {
|
|
230
229
|
// know for sure that it doesn't impact the body of the conditional, so we
|
231
230
|
// have to default to the block form.
|
232
231
|
if (containsAssignment(predicate)) {
|
233
|
-
return
|
232
|
+
return concat([
|
233
|
+
`${keyword} `,
|
234
|
+
align(keyword.length + 1, path.call(print, "body", 0)),
|
235
|
+
indent(concat([hardline, path.call(print, "body", 1)])),
|
236
|
+
concat([hardline, "end"])
|
237
|
+
]);
|
234
238
|
}
|
235
239
|
|
236
240
|
return printSingle(keyword)(path, { inlineConditionals }, print);
|
data/src/nodes/hashes.js
CHANGED
@@ -12,7 +12,7 @@ const { prefix, skipAssignIndent } = require("../utils");
|
|
12
12
|
const nodeDive = (node, steps) => {
|
13
13
|
let current = node;
|
14
14
|
|
15
|
-
steps.forEach(step => {
|
15
|
+
steps.forEach((step) => {
|
16
16
|
current = current[step];
|
17
17
|
});
|
18
18
|
|
@@ -29,7 +29,7 @@ const nodeDive = (node, steps) => {
|
|
29
29
|
//
|
30
30
|
// This function represents that check, as it determines if it can convert the
|
31
31
|
// symbol node into a hash label.
|
32
|
-
const isValidHashLabel = symbolLiteral => {
|
32
|
+
const isValidHashLabel = (symbolLiteral) => {
|
33
33
|
const label = symbolLiteral.body[0].body[0].body;
|
34
34
|
return label.match(/^[_A-Za-z]/) && !label.endsWith("=");
|
35
35
|
};
|
data/src/nodes/hooks.js
CHANGED
data/src/nodes/ints.js
CHANGED
@@ -11,7 +11,7 @@ module.exports = {
|
|
11
11
|
// If the number is a base 10 number, is sufficiently large, and is not
|
12
12
|
// already formatted with underscores, then add them in in between the
|
13
13
|
// numbers every three characters starting from the right.
|
14
|
-
if (!body.startsWith("0") && body.length >=
|
14
|
+
if (!body.startsWith("0") && body.length >= 5 && !body.includes("_")) {
|
15
15
|
return ` ${body}`
|
16
16
|
.slice((body.length + 2) % 3)
|
17
17
|
.match(/.{3}/g)
|
data/src/nodes/lambdas.js
CHANGED
@@ -21,7 +21,7 @@ module.exports = {
|
|
21
21
|
paramsConcat = path.call(print, "body", 0, "body", 0);
|
22
22
|
}
|
23
23
|
|
24
|
-
const noParams = params.body.every(type => !type);
|
24
|
+
const noParams = params.body.every((type) => !type);
|
25
25
|
const inlineLambda = concat([
|
26
26
|
"->",
|
27
27
|
noParams ? "" : concat(["(", paramsConcat, ")"]),
|
data/src/nodes/loops.js
CHANGED
@@ -10,6 +10,22 @@ const {
|
|
10
10
|
const { containsAssignment } = require("../utils");
|
11
11
|
|
12
12
|
const printLoop = (keyword, modifier) => (path, { inlineLoops }, print) => {
|
13
|
+
const [_predicate, statements] = path.getValue().body;
|
14
|
+
|
15
|
+
// If the only statement inside this while loop is a void statement, then we
|
16
|
+
// can shorten to just displaying the predicate and then a semicolon.
|
17
|
+
if (statements.body.length === 1 && statements.body[0].type === "void_stmt") {
|
18
|
+
return group(
|
19
|
+
concat([
|
20
|
+
keyword,
|
21
|
+
" ",
|
22
|
+
path.call(print, "body", 0),
|
23
|
+
ifBreak(softline, "; "),
|
24
|
+
"end"
|
25
|
+
])
|
26
|
+
);
|
27
|
+
}
|
28
|
+
|
13
29
|
let inlineParts = [
|
14
30
|
path.call(print, "body", 1),
|
15
31
|
` ${keyword} `,
|
data/src/nodes/methods.js
CHANGED
@@ -9,7 +9,7 @@ const {
|
|
9
9
|
} = require("../prettier");
|
10
10
|
const { first, literal } = require("../utils");
|
11
11
|
|
12
|
-
const printMethod = offset => (path, opts, print) => {
|
12
|
+
const printMethod = (offset) => (path, opts, print) => {
|
13
13
|
const [_name, params, body] = path.getValue().body.slice(offset);
|
14
14
|
const declaration = ["def "];
|
15
15
|
|
@@ -21,7 +21,7 @@ const printMethod = offset => (path, opts, print) => {
|
|
21
21
|
|
22
22
|
// In case there are no parens but there are arguments
|
23
23
|
const parens =
|
24
|
-
params.type === "params" && params.body.some(paramType => paramType);
|
24
|
+
params.type === "params" && params.body.some((paramType) => paramType);
|
25
25
|
|
26
26
|
declaration.push(
|
27
27
|
path.call(print, "body", offset),
|
data/src/nodes/operators.js
CHANGED
@@ -7,10 +7,18 @@ module.exports = {
|
|
7
7
|
|
8
8
|
return group(
|
9
9
|
concat([
|
10
|
-
|
11
|
-
operator,
|
10
|
+
group(path.call(print, "body", 0)),
|
12
11
|
indent(
|
13
|
-
concat([
|
12
|
+
concat([
|
13
|
+
useNoSpace ? "" : " ",
|
14
|
+
group(
|
15
|
+
concat([
|
16
|
+
operator,
|
17
|
+
useNoSpace ? softline : line,
|
18
|
+
path.call(print, "body", 2)
|
19
|
+
])
|
20
|
+
)
|
21
|
+
])
|
14
22
|
)
|
15
23
|
])
|
16
24
|
);
|
data/src/nodes/params.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
const { concat, group, join, line } = require("../prettier");
|
2
|
+
const { literal } = require("../utils");
|
2
3
|
|
3
|
-
const printGenericRestParam = symbol => (path, opts, print) =>
|
4
|
+
const printGenericRestParam = (symbol) => (path, opts, print) =>
|
4
5
|
path.getValue().body[0]
|
5
6
|
? concat([symbol, path.call(print, "body", 0)])
|
6
7
|
: symbol;
|
@@ -53,7 +54,7 @@ const printParams = (path, opts, print) => {
|
|
53
54
|
}
|
54
55
|
|
55
56
|
if (kwargRest) {
|
56
|
-
parts.push(path.call(print, "body", 5));
|
57
|
+
parts.push(kwargRest === "nil" ? "**nil" : path.call(print, "body", 5));
|
57
58
|
}
|
58
59
|
|
59
60
|
if (block) {
|
@@ -79,6 +80,7 @@ const paramError = () => {
|
|
79
80
|
};
|
80
81
|
|
81
82
|
module.exports = {
|
83
|
+
args_forward: literal("..."),
|
82
84
|
kwrest_param: printGenericRestParam("**"),
|
83
85
|
rest_param: printGenericRestParam("*"),
|
84
86
|
params: printParams,
|
data/src/nodes/regexp.js
CHANGED
@@ -7,7 +7,7 @@ module.exports = {
|
|
7
7
|
const [contents, ending] = path.map(print, "body");
|
8
8
|
|
9
9
|
const useBraces = contents.some(
|
10
|
-
content => typeof content === "string" && content.includes("/")
|
10
|
+
(content) => typeof content === "string" && content.includes("/")
|
11
11
|
);
|
12
12
|
const parts = [useBraces ? "%r{" : "/"]
|
13
13
|
.concat(contents)
|
data/src/nodes/rescue.js
CHANGED
@@ -53,7 +53,11 @@ module.exports = {
|
|
53
53
|
parts.push(" StandardError");
|
54
54
|
}
|
55
55
|
|
56
|
-
|
56
|
+
const rescueBody = path.call(print, "body", 2);
|
57
|
+
|
58
|
+
if (rescueBody.parts.length > 0) {
|
59
|
+
parts.push(indent(concat([hardline, rescueBody])));
|
60
|
+
}
|
57
61
|
|
58
62
|
// This is the next clause on the `begin` statement, either another
|
59
63
|
// `rescue`, and `ensure`, or an `else` clause.
|
data/src/nodes/return.js
CHANGED
@@ -9,6 +9,16 @@ const {
|
|
9
9
|
} = require("../prettier");
|
10
10
|
const { literal } = require("../utils");
|
11
11
|
|
12
|
+
// You can't skip the parentheses if you have the `and` or `or` operator,
|
13
|
+
// because they have low enough operator precedence that you need to explicitly
|
14
|
+
// keep them in there.
|
15
|
+
const canSkipParens = (args) => {
|
16
|
+
const statement = args.body[0].body[0].body[0];
|
17
|
+
return (
|
18
|
+
statement.type !== "binary" || !["and", "or"].includes(statement.body[1])
|
19
|
+
);
|
20
|
+
};
|
21
|
+
|
12
22
|
const printReturn = (path, opts, print) => {
|
13
23
|
let args = path.getValue().body[0].body[0];
|
14
24
|
let steps = ["body", 0, "body", 0];
|
@@ -20,17 +30,19 @@ const printReturn = (path, opts, print) => {
|
|
20
30
|
// If the body of the return contains parens, then just skip directly to the
|
21
31
|
// content of the parens so that we can skip printing parens if we don't
|
22
32
|
// want them.
|
23
|
-
if (args.body[0] && args.body[0].type === "paren") {
|
33
|
+
if (args.body[0] && args.body[0].type === "paren" && canSkipParens(args)) {
|
24
34
|
args = args.body[0].body[0];
|
25
35
|
steps = steps.concat("body", 0, "body", 0);
|
26
36
|
}
|
27
37
|
|
28
|
-
// If we're returning an array literal that isn't a special array,
|
29
|
-
//
|
30
|
-
// normal return arguments.
|
38
|
+
// If we're returning an array literal that isn't a special array, single
|
39
|
+
// element array, or an empty array, then we want to grab the arguments so
|
40
|
+
// that we can print them out as if they were normal return arguments.
|
31
41
|
if (
|
32
42
|
args.body[0] &&
|
33
43
|
args.body[0].type === "array" &&
|
44
|
+
args.body[0].body[0] &&
|
45
|
+
args.body[0].body[0].body.length > 1 &&
|
34
46
|
["args", "args_add_star"].includes(args.body[0].body[0].type)
|
35
47
|
) {
|
36
48
|
steps = steps.concat("body", 0, "body", 0);
|
data/src/nodes/strings.js
CHANGED
@@ -7,39 +7,31 @@ const {
|
|
7
7
|
softline,
|
8
8
|
join
|
9
9
|
} = require("../prettier");
|
10
|
+
|
10
11
|
const { concatBody, empty, makeList, prefix, surround } = require("../utils");
|
11
|
-
const escapePattern = require("../escapePattern");
|
12
12
|
|
13
13
|
// If there is some part of this string that matches an escape sequence or that
|
14
14
|
// contains the interpolation pattern ("#{"), then we are locked into whichever
|
15
15
|
// quote the user chose. (If they chose single quotes, then double quoting
|
16
16
|
// would activate the escape sequence, and if they chose double quotes, then
|
17
17
|
// single quotes would deactivate it.)
|
18
|
-
const isQuoteLocked = string =>
|
18
|
+
const isQuoteLocked = (string) =>
|
19
19
|
string.body.some(
|
20
|
-
part =>
|
20
|
+
(part) =>
|
21
21
|
part.type === "@tstring_content" &&
|
22
|
-
(
|
22
|
+
(part.body.includes("#{") || part.body.includes("\\"))
|
23
23
|
);
|
24
24
|
|
25
25
|
// A string is considered to be able to use single quotes if it contains only
|
26
26
|
// plain string content and that content does not contain a single quote.
|
27
|
-
const isSingleQuotable = string =>
|
27
|
+
const isSingleQuotable = (string) =>
|
28
28
|
string.body.every(
|
29
|
-
part => part.type === "@tstring_content" && !part.body.includes("'")
|
29
|
+
(part) => part.type === "@tstring_content" && !part.body.includes("'")
|
30
30
|
);
|
31
31
|
|
32
|
-
const getStringQuote = (string, preferSingleQuotes) => {
|
33
|
-
if (isQuoteLocked(string)) {
|
34
|
-
return string.quote;
|
35
|
-
}
|
36
|
-
|
37
|
-
return preferSingleQuotes && isSingleQuotable(string) ? "'" : '"';
|
38
|
-
};
|
39
|
-
|
40
32
|
const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
|
41
33
|
|
42
|
-
const
|
34
|
+
const normalizeQuotes = (content, enclosingQuote, originalQuote) => {
|
43
35
|
const replaceOther = ["'", '"'].includes(originalQuote);
|
44
36
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
45
37
|
|
@@ -62,51 +54,24 @@ const makeString = (content, enclosingQuote, originalQuote) => {
|
|
62
54
|
});
|
63
55
|
};
|
64
56
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
const makeHeredocLines = parts => {
|
72
|
-
let lines = [];
|
73
|
-
let currentLine = [];
|
74
|
-
|
75
|
-
parts.forEach(part => {
|
76
|
-
if (part.type === "group" || !part.includes("\n")) {
|
77
|
-
// In this case we've either hit an embedded expression or the piece of
|
78
|
-
// the current line that we're looking at is in the middle of two of them,
|
79
|
-
// so we just push onto the current line and continue on.
|
80
|
-
currentLine.push(part);
|
81
|
-
return;
|
82
|
-
}
|
83
|
-
|
84
|
-
let splits = part.split("\n");
|
85
|
-
if (splits[splits.length - 1] === "") {
|
86
|
-
// If a line ends with a newline, then we end up with an empty string at
|
87
|
-
// the end of the splits, we just pop it off here since we'll handle the
|
88
|
-
// newlines later.
|
89
|
-
splits = splits.slice(0, -1);
|
90
|
-
}
|
91
|
-
|
92
|
-
if (currentLine.length > 0) {
|
93
|
-
lines.push(concat(currentLine.concat(splits[0])));
|
94
|
-
currentLine = [];
|
95
|
-
} else {
|
96
|
-
lines.push(splits[0]);
|
97
|
-
}
|
57
|
+
const quotePairs = {
|
58
|
+
"(": ")",
|
59
|
+
"[": "]",
|
60
|
+
"{": "}",
|
61
|
+
"<": ">"
|
62
|
+
};
|
98
63
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
});
|
64
|
+
const getClosingQuote = (quote) => {
|
65
|
+
if (!quote.startsWith("%")) {
|
66
|
+
return quote;
|
67
|
+
}
|
104
68
|
|
105
|
-
|
106
|
-
|
69
|
+
const boundary = /%q?(.)/.exec(quote)[1];
|
70
|
+
if (boundary in quotePairs) {
|
71
|
+
return quotePairs[boundary];
|
107
72
|
}
|
108
73
|
|
109
|
-
return
|
74
|
+
return boundary;
|
110
75
|
};
|
111
76
|
|
112
77
|
module.exports = {
|
@@ -121,21 +86,24 @@ module.exports = {
|
|
121
86
|
return body.length === 2 ? concat([quote, body.slice(1), quote]) : body;
|
122
87
|
},
|
123
88
|
dyna_symbol: (path, opts, print) => {
|
124
|
-
const { quote } = path.getValue()
|
89
|
+
const { quote } = path.getValue();
|
125
90
|
|
126
91
|
return concat([":", quote, concat(path.call(print, "body", 0)), quote]);
|
127
92
|
},
|
128
93
|
heredoc: (path, opts, print) => {
|
129
|
-
const { beging, ending } = path.getValue();
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
94
|
+
const { beging, body, ending } = path.getValue();
|
95
|
+
|
96
|
+
const parts = body.map((part, index) => {
|
97
|
+
if (part.type !== "@tstring_content") {
|
98
|
+
// In this case, the part of the string is an embedded expression
|
99
|
+
return path.call(print, "body", index);
|
100
|
+
}
|
101
|
+
|
102
|
+
// In this case, the part of the string is just regular string content
|
103
|
+
return join(literalline, part.body.split("\n"));
|
104
|
+
});
|
105
|
+
|
106
|
+
return concat([beging, literalline, concat(parts), ending]);
|
139
107
|
},
|
140
108
|
string: makeList,
|
141
109
|
string_concat: (path, opts, print) =>
|
@@ -162,7 +130,8 @@ module.exports = {
|
|
162
130
|
);
|
163
131
|
},
|
164
132
|
string_literal: (path, { preferSingleQuotes }, print) => {
|
165
|
-
const
|
133
|
+
const stringLiteral = path.getValue();
|
134
|
+
const string = stringLiteral.body[0];
|
166
135
|
|
167
136
|
// If this string is actually a heredoc, bail out and return to the print
|
168
137
|
// function for heredocs
|
@@ -176,20 +145,28 @@ module.exports = {
|
|
176
145
|
return preferSingleQuotes ? "''" : '""';
|
177
146
|
}
|
178
147
|
|
179
|
-
|
180
|
-
|
148
|
+
// Determine the quote that should enclose the new string
|
149
|
+
let quote;
|
150
|
+
if (isQuoteLocked(string)) {
|
151
|
+
({ quote } = stringLiteral);
|
152
|
+
} else {
|
153
|
+
quote = preferSingleQuotes && isSingleQuotable(string) ? "'" : '"';
|
154
|
+
}
|
181
155
|
|
182
|
-
string.body.
|
183
|
-
if (part.type
|
184
|
-
// In this case, the part of the string is just regular string content
|
185
|
-
parts.push(makeString(part.body, quote, string.quote));
|
186
|
-
} else {
|
156
|
+
const parts = string.body.map((part, index) => {
|
157
|
+
if (part.type !== "@tstring_content") {
|
187
158
|
// In this case, the part of the string is an embedded expression
|
188
|
-
|
159
|
+
return path.call(print, "body", 0, "body", index);
|
189
160
|
}
|
161
|
+
|
162
|
+
// In this case, the part of the string is just regular string content
|
163
|
+
return join(
|
164
|
+
literalline,
|
165
|
+
normalizeQuotes(part.body, quote, stringLiteral.quote).split("\n")
|
166
|
+
);
|
190
167
|
});
|
191
168
|
|
192
|
-
return concat([quote].concat(parts).concat(
|
169
|
+
return concat([quote].concat(parts).concat(getClosingQuote(quote)));
|
193
170
|
},
|
194
171
|
symbol: prefix(":"),
|
195
172
|
symbol_literal: concatBody,
|