prettier 0.20.0 → 1.0.0.pre.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +118 -4
  3. data/CONTRIBUTING.md +8 -6
  4. data/README.md +67 -60
  5. data/node_modules/prettier/bin-prettier.js +12317 -50112
  6. data/node_modules/prettier/index.js +33352 -27419
  7. data/node_modules/prettier/third-party.js +5678 -7676
  8. data/package.json +4 -4
  9. data/src/embed.js +27 -8
  10. data/src/nodes.js +6 -2
  11. data/src/nodes/alias.js +65 -24
  12. data/src/nodes/aref.js +55 -0
  13. data/src/nodes/args.js +55 -47
  14. data/src/nodes/arrays.js +150 -137
  15. data/src/nodes/assign.js +32 -32
  16. data/src/nodes/blocks.js +8 -3
  17. data/src/nodes/calls.js +129 -70
  18. data/src/nodes/case.js +11 -7
  19. data/src/nodes/class.js +74 -0
  20. data/src/nodes/commands.js +36 -31
  21. data/src/nodes/conditionals.js +48 -46
  22. data/src/nodes/constants.js +39 -21
  23. data/src/nodes/flow.js +45 -17
  24. data/src/nodes/hashes.js +126 -112
  25. data/src/nodes/heredocs.js +34 -0
  26. data/src/nodes/hooks.js +36 -7
  27. data/src/nodes/ints.js +27 -20
  28. data/src/nodes/lambdas.js +69 -52
  29. data/src/nodes/loops.js +19 -29
  30. data/src/nodes/massign.js +87 -65
  31. data/src/nodes/methods.js +48 -73
  32. data/src/nodes/operators.js +70 -39
  33. data/src/nodes/params.js +26 -16
  34. data/src/nodes/patterns.js +108 -33
  35. data/src/nodes/regexp.js +38 -14
  36. data/src/nodes/rescue.js +72 -59
  37. data/src/nodes/statements.js +86 -44
  38. data/src/nodes/strings.js +94 -90
  39. data/src/nodes/super.js +35 -0
  40. data/src/nodes/undef.js +42 -0
  41. data/src/parser.js +71 -0
  42. data/src/parser.rb +2554 -0
  43. data/src/printer.js +90 -0
  44. data/src/ruby.js +20 -61
  45. data/src/toProc.js +4 -4
  46. data/src/utils.js +24 -88
  47. data/src/utils/inlineEnsureParens.js +42 -0
  48. data/src/utils/isEmptyStmts.js +7 -0
  49. data/src/utils/literalLineNoBreak.js +7 -0
  50. metadata +15 -20
  51. data/src/haml.js +0 -21
  52. data/src/haml/embed.js +0 -58
  53. data/src/haml/nodes/comment.js +0 -27
  54. data/src/haml/nodes/doctype.js +0 -32
  55. data/src/haml/nodes/filter.js +0 -16
  56. data/src/haml/nodes/hamlComment.js +0 -21
  57. data/src/haml/nodes/script.js +0 -29
  58. data/src/haml/nodes/silentScript.js +0 -59
  59. data/src/haml/nodes/tag.js +0 -157
  60. data/src/haml/parse.js +0 -18
  61. data/src/haml/parse.rb +0 -64
  62. data/src/haml/print.js +0 -38
  63. data/src/nodes/scopes.js +0 -61
  64. data/src/parse.js +0 -37
  65. data/src/print.js +0 -23
  66. data/src/ripper.rb +0 -811
@@ -1,18 +1,42 @@
1
1
  const { concat } = require("../prettier");
2
- const { makeList } = require("../utils");
3
2
 
4
- module.exports = {
5
- regexp: makeList,
6
- regexp_literal: (path, opts, print) => {
7
- const [contents, ending] = path.map(print, "body");
8
-
9
- const useBraces = contents.some(
10
- (content) => typeof content === "string" && content.includes("/")
11
- );
12
- const parts = [useBraces ? "%r{" : "/"]
13
- .concat(contents)
14
- .concat([useBraces ? "}" : "/", ending.slice(1)]);
15
-
16
- return concat(parts);
3
+ function isStringContent(node) {
4
+ return node.type === "@tstring_content";
5
+ }
6
+
7
+ function shouldUseBraces(node) {
8
+ const first = node.body[0];
9
+
10
+ // If the first part of this regex is plain string content and we have a
11
+ // space or an =, then we want to use braces because otherwise we could end up
12
+ // with an ambiguous operator, e.g. foo / bar/ or foo /=bar/
13
+ if (first && isStringContent(first) && [" ", "="].includes(first.body[0])) {
14
+ return true;
17
15
  }
16
+
17
+ return node.body.some(
18
+ (child) => isStringContent(child) && child.body.includes("/")
19
+ );
20
+ }
21
+
22
+ // This function is responsible for printing out regexp_literal nodes. They can
23
+ // either use the special %r literal syntax or they can use forward slashes. At
24
+ // the end of either of those they can have modifiers like m or x that have
25
+ // special meaning for the regex engine.
26
+ //
27
+ // We favor the use of forward slashes unless the regex contains a forward slash
28
+ // itself. In that case we switch over to using %r with braces.
29
+ function printRegexpLiteral(path, opts, print) {
30
+ const node = path.getValue();
31
+ const useBraces = shouldUseBraces(node);
32
+
33
+ const parts = [useBraces ? "%r{" : "/"]
34
+ .concat(path.map(print, "body"))
35
+ .concat([useBraces ? "}" : "/", node.ending.slice(1)]);
36
+
37
+ return concat(parts);
38
+ }
39
+
40
+ module.exports = {
41
+ regexp_literal: printRegexpLiteral
18
42
  };
@@ -9,73 +9,86 @@ const {
9
9
  } = require("../prettier");
10
10
  const { literal } = require("../utils");
11
11
 
12
- module.exports = {
13
- begin: (path, opts, print) =>
14
- concat([
15
- "begin",
16
- indent(concat([hardline, concat(path.map(print, "body"))])),
17
- hardline,
18
- "end"
19
- ]),
20
- ensure: (path, opts, print) =>
21
- concat([
22
- "ensure",
23
- indent(concat([hardline, concat(path.map(print, "body"))]))
24
- ]),
25
- redo: literal("redo"),
26
- rescue: (path, opts, print) => {
27
- const [exception, variable, _stmts, addition] = path.getValue().body;
28
- const parts = ["rescue"];
12
+ function printBegin(path, opts, print) {
13
+ return concat([
14
+ "begin",
15
+ indent(concat([hardline, concat(path.map(print, "body"))])),
16
+ hardline,
17
+ "end"
18
+ ]);
19
+ }
29
20
 
30
- if (exception || variable) {
31
- if (exception) {
32
- if (Array.isArray(exception)) {
33
- // In this case, it's actually only the one exception (it's an array
34
- // of length 1).
35
- parts.push(" ", path.call(print, "body", 0, 0));
36
- } else {
37
- // Here we have multiple exceptions from which we're rescuing, so we
38
- // need to align them and join them together.
39
- const joiner = concat([",", line]);
40
- const exceptions = group(join(joiner, path.call(print, "body", 0)));
21
+ function printEnsure(path, opts, print) {
22
+ return concat([
23
+ path.call(print, "body", 0),
24
+ indent(concat([hardline, path.call(print, "body", 1)]))
25
+ ]);
26
+ }
41
27
 
42
- parts.push(" ", align("rescue ".length, exceptions));
43
- }
44
- }
28
+ function printRescue(path, opts, print) {
29
+ const [exception, variable, _stmts, addition] = path.getValue().body;
30
+ const parts = ["rescue"];
31
+
32
+ if (exception || variable) {
33
+ if (exception) {
34
+ if (Array.isArray(exception)) {
35
+ // In this case, it's actually only the one exception (it's an array
36
+ // of length 1).
37
+ parts.push(" ", path.call(print, "body", 0, 0));
38
+ } else {
39
+ // Here we have multiple exceptions from which we're rescuing, so we
40
+ // need to align them and join them together.
41
+ const joiner = concat([",", line]);
42
+ const exceptions = group(join(joiner, path.call(print, "body", 0)));
45
43
 
46
- if (variable) {
47
- parts.push(" => ", path.call(print, "body", 1));
44
+ parts.push(" ", align("rescue ".length, exceptions));
48
45
  }
49
- } else {
50
- // If you don't specify an error to rescue in a `begin/rescue` block, then
51
- // implicitly you're rescuing from `StandardError`. In this case, we're
52
- // just going to explicitly add it.
53
- parts.push(" StandardError");
54
46
  }
55
47
 
56
- const rescueBody = path.call(print, "body", 2);
57
-
58
- if (rescueBody.parts.length > 0) {
59
- parts.push(indent(concat([hardline, rescueBody])));
48
+ if (variable) {
49
+ parts.push(" => ", path.call(print, "body", 1));
60
50
  }
51
+ } else {
52
+ // If you don't specify an error to rescue in a `begin/rescue` block, then
53
+ // implicitly you're rescuing from `StandardError`. In this case, we're
54
+ // just going to explicitly add it.
55
+ parts.push(" StandardError");
56
+ }
61
57
 
62
- // This is the next clause on the `begin` statement, either another
63
- // `rescue`, and `ensure`, or an `else` clause.
64
- if (addition) {
65
- parts.push(concat([hardline, path.call(print, "body", 3)]));
66
- }
58
+ const rescueBody = path.call(print, "body", 2);
59
+
60
+ if (rescueBody.parts.length > 0) {
61
+ parts.push(indent(concat([hardline, rescueBody])));
62
+ }
67
63
 
68
- return group(concat(parts));
69
- },
70
- rescue_mod: (path, opts, print) =>
71
- concat([
72
- "begin",
73
- indent(concat([hardline, path.call(print, "body", 0)])),
74
- hardline,
75
- "rescue StandardError",
76
- indent(concat([hardline, path.call(print, "body", 1)])),
77
- hardline,
78
- "end"
79
- ]),
64
+ // This is the next clause on the `begin` statement, either another
65
+ // `rescue`, and `ensure`, or an `else` clause.
66
+ if (addition) {
67
+ parts.push(concat([hardline, path.call(print, "body", 3)]));
68
+ }
69
+
70
+ return group(concat(parts));
71
+ }
72
+
73
+ function printRescueMod(path, opts, print) {
74
+ const [statementDoc, valueDoc] = path.map(print, "body");
75
+
76
+ return concat([
77
+ "begin",
78
+ indent(concat([hardline, statementDoc])),
79
+ hardline,
80
+ "rescue StandardError",
81
+ indent(concat([hardline, valueDoc])),
82
+ hardline,
83
+ "end"
84
+ ]);
85
+ }
86
+
87
+ module.exports = {
88
+ begin: printBegin,
89
+ ensure: printEnsure,
90
+ redo: literal("redo"),
91
+ rescue: printRescue,
92
+ rescue_mod: printRescueMod,
80
93
  retry: literal("retry")
81
94
  };
@@ -1,4 +1,5 @@
1
1
  const {
2
+ breakParent,
2
3
  concat,
3
4
  dedent,
4
5
  group,
@@ -11,63 +12,104 @@ const {
11
12
  trim
12
13
  } = require("../prettier");
13
14
 
14
- module.exports = {
15
- "@__end__": (path, _opts, _print) => {
16
- const { body } = path.getValue();
17
- return concat([trim, "__END__", literalline, body]);
18
- },
19
- bodystmt: (path, opts, print) => {
20
- const [_statements, rescue, elseClause, ensure] = path.getValue().body;
21
- const parts = [path.call(print, "body", 0)];
15
+ function printBodyStmt(path, opts, print) {
16
+ const [stmts, rescue, elseClause, ensure] = path.getValue().body;
17
+ const parts = [];
22
18
 
23
- if (rescue) {
24
- parts.push(dedent(concat([hardline, path.call(print, "body", 1)])));
25
- }
19
+ if (
20
+ stmts.body.length > 1 ||
21
+ stmts.body[0].type != "void_stmt" ||
22
+ stmts.body[0].comments
23
+ ) {
24
+ parts.push(path.call(print, "body", 0));
25
+ }
26
26
 
27
- if (elseClause) {
28
- // Before Ruby 2.6, this piece of bodystmt was an explicit "else" node
29
- const stmts =
30
- elseClause.type === "else"
31
- ? path.call(print, "body", 2, "body", 0)
32
- : path.call(print, "body", 2);
27
+ if (rescue) {
28
+ parts.push(dedent(concat([hardline, path.call(print, "body", 1)])));
29
+ }
33
30
 
34
- parts.push(concat([dedent(concat([hardline, "else"])), hardline, stmts]));
35
- }
31
+ if (elseClause) {
32
+ // Before Ruby 2.6, this piece of bodystmt was an explicit "else" node
33
+ const stmts =
34
+ elseClause.type === "else"
35
+ ? path.call(print, "body", 2, "body", 0)
36
+ : path.call(print, "body", 2);
36
37
 
37
- if (ensure) {
38
- parts.push(dedent(concat([hardline, path.call(print, "body", 3)])));
39
- }
38
+ parts.push(concat([dedent(concat([hardline, "else"])), hardline, stmts]));
39
+ }
40
40
 
41
- return group(concat(parts));
42
- },
43
- embdoc: (path, _opts, _print) => concat([trim, path.getValue().body]),
44
- paren: (path, opts, print) => {
45
- if (!path.getValue().body[0]) {
46
- return "()";
47
- }
41
+ if (ensure) {
42
+ parts.push(dedent(concat([hardline, path.call(print, "body", 3)])));
43
+ }
48
44
 
49
- let content = path.call(print, "body", 0);
45
+ return group(concat(parts));
46
+ }
50
47
 
51
- if (
52
- ["args", "args_add_star", "args_add_block"].includes(
53
- path.getValue().body[0].type
54
- )
55
- ) {
56
- content = join(concat([",", line]), content);
57
- }
48
+ const argNodeTypes = ["args", "args_add_star", "args_add_block"];
49
+
50
+ function printParen(path, opts, print) {
51
+ const contentNode = path.getValue().body[0];
58
52
 
59
- return group(
60
- concat([
61
- "(",
62
- indent(concat([softline, content])),
63
- concat([softline, ")"])
64
- ])
65
- );
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
+
80
+ module.exports = {
81
+ "@__end__": (path, _opts, _print) => {
82
+ const { body } = path.getValue();
83
+ return concat([trim, "__END__", literalline, body]);
66
84
  },
85
+ bodystmt: printBodyStmt,
86
+ paren: printParen,
67
87
  program: (path, opts, print) =>
68
88
  concat([join(hardline, path.map(print, "body")), hardline]),
69
89
  stmts: (path, opts, print) => {
70
90
  const stmts = path.getValue().body;
91
+
92
+ // This is a special case where we have only comments inside a statement
93
+ // list. In this case we want to avoid doing any kind of line number
94
+ // tracking and just print out the comments.
95
+ if (
96
+ stmts.length === 1 &&
97
+ stmts[0].type === "void_stmt" &&
98
+ stmts[0].comments
99
+ ) {
100
+ const comments = path.map(
101
+ (commentPath, index) => {
102
+ stmts[0].comments[index].printed = true;
103
+ return opts.printer.printComment(commentPath);
104
+ },
105
+ "body",
106
+ 0,
107
+ "comments"
108
+ );
109
+
110
+ return concat([breakParent, join(hardline, comments)]);
111
+ }
112
+
71
113
  const parts = [];
72
114
  let lineNo = null;
73
115
 
@@ -8,30 +8,30 @@ const {
8
8
  join
9
9
  } = require("../prettier");
10
10
 
11
- const { concatBody, empty, makeList, prefix, surround } = require("../utils");
12
-
13
11
  // If there is some part of this string that matches an escape sequence or that
14
12
  // contains the interpolation pattern ("#{"), then we are locked into whichever
15
13
  // quote the user chose. (If they chose single quotes, then double quoting
16
14
  // would activate the escape sequence, and if they chose double quotes, then
17
15
  // single quotes would deactivate it.)
18
- const isQuoteLocked = (string) =>
19
- string.body.some(
16
+ function isQuoteLocked(node) {
17
+ return node.body.some(
20
18
  (part) =>
21
19
  part.type === "@tstring_content" &&
22
20
  (part.body.includes("#{") || part.body.includes("\\"))
23
21
  );
22
+ }
24
23
 
25
24
  // A string is considered to be able to use single quotes if it contains only
26
25
  // plain string content and that content does not contain a single quote.
27
- const isSingleQuotable = (string) =>
28
- string.body.every(
26
+ function isSingleQuotable(node) {
27
+ return node.body.every(
29
28
  (part) => part.type === "@tstring_content" && !part.body.includes("'")
30
29
  );
30
+ }
31
31
 
32
32
  const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
33
33
 
34
- const normalizeQuotes = (content, enclosingQuote, originalQuote) => {
34
+ function normalizeQuotes(content, enclosingQuote, originalQuote) {
35
35
  const replaceOther = ["'", '"'].includes(originalQuote);
36
36
  const otherQuote = enclosingQuote === '"' ? "'" : '"';
37
37
 
@@ -52,7 +52,7 @@ const normalizeQuotes = (content, enclosingQuote, originalQuote) => {
52
52
 
53
53
  return `\\${escaped}`;
54
54
  });
55
- };
55
+ }
56
56
 
57
57
  const quotePairs = {
58
58
  "(": ")",
@@ -61,51 +61,100 @@ const quotePairs = {
61
61
  "<": ">"
62
62
  };
63
63
 
64
- const getClosingQuote = (quote) => {
64
+ function getClosingQuote(quote) {
65
65
  if (!quote.startsWith("%")) {
66
66
  return quote;
67
67
  }
68
68
 
69
- const boundary = /%q?(.)/.exec(quote)[1];
69
+ const boundary = /%[Qq]?(.)/.exec(quote)[1];
70
70
  if (boundary in quotePairs) {
71
71
  return quotePairs[boundary];
72
72
  }
73
73
 
74
74
  return boundary;
75
- };
75
+ }
76
76
 
77
- module.exports = {
78
- "@CHAR": (path, { preferSingleQuotes }, _print) => {
79
- const { body } = path.getValue();
77
+ // Prints a @CHAR node. @CHAR nodes are special character strings that usually
78
+ // are strings of length 1. If they're any longer than we'll try to apply the
79
+ // correct quotes.
80
+ function printChar(path, { rubySingleQuote }, _print) {
81
+ const { body } = path.getValue();
82
+
83
+ if (body.length !== 2) {
84
+ return body;
85
+ }
86
+
87
+ const quote = rubySingleQuote ? "'" : '"';
88
+ return concat([quote, body.slice(1), quote]);
89
+ }
90
+
91
+ // Prints a dynamic symbol. Assumes there's a quote property attached to the
92
+ // node that will tell us which quote to use when printing. We're just going to
93
+ // use whatever quote was provided.
94
+ function printDynaSymbol(path, opts, print) {
95
+ const { quote } = path.getValue();
96
+
97
+ return concat([":", quote].concat(path.map(print, "body")).concat(quote));
98
+ }
99
+
100
+ // Prints out an interpolated variable in the string by converting it into an
101
+ // embedded expression.
102
+ function printStringDVar(path, opts, print) {
103
+ return concat(["#{", path.call(print, "body", 0), "}"]);
104
+ }
105
+
106
+ // Prints out a literal string. This function does its best to respect the
107
+ // wishes of the user with regards to single versus double quotes, but if the
108
+ // string contains any escape expressions then it will just keep the original
109
+ // quotes.
110
+ function printStringLiteral(path, { rubySingleQuote }, print) {
111
+ const node = path.getValue();
112
+
113
+ // If the string is empty, it will not have any parts, so just print out the
114
+ // quotes corresponding to the config
115
+ if (node.body.length === 0) {
116
+ return rubySingleQuote ? "''" : '""';
117
+ }
118
+
119
+ // Determine the quote that should enclose the new string
120
+ let quote;
121
+ if (isQuoteLocked(node)) {
122
+ quote = node.quote;
123
+ } else {
124
+ quote = rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
125
+ }
80
126
 
81
- if (body.length !== 2) {
82
- return body;
127
+ const parts = node.body.map((part, index) => {
128
+ if (part.type !== "@tstring_content") {
129
+ // In this case, the part of the string is an embedded expression
130
+ return path.call(print, "body", index);
83
131
  }
84
132
 
85
- const quote = preferSingleQuotes ? "'" : '"';
86
- return body.length === 2 ? concat([quote, body.slice(1), quote]) : body;
87
- },
88
- dyna_symbol: (path, opts, print) => {
89
- const { quote } = path.getValue();
133
+ // In this case, the part of the string is just regular string content
134
+ return join(
135
+ literalline,
136
+ normalizeQuotes(part.body, quote, node.quote).split("\n")
137
+ );
138
+ });
90
139
 
91
- return concat([":", quote, concat(path.call(print, "body", 0)), quote]);
92
- },
93
- heredoc: (path, opts, print) => {
94
- const { beging, body, ending } = path.getValue();
140
+ return concat([quote].concat(parts).concat(getClosingQuote(quote)));
141
+ }
95
142
 
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
- }
143
+ // Prints out a symbol literal. Its child will always be the ident that
144
+ // represents the string content of the symbol.
145
+ function printSymbolLiteral(path, opts, print) {
146
+ return concat([":", path.call(print, "body", 0)]);
147
+ }
101
148
 
102
- // In this case, the part of the string is just regular string content
103
- return join(literalline, part.body.split("\n"));
104
- });
149
+ // Prints out an xstring literal. Its child is an array of string parts,
150
+ // including plain string content and interpolated content.
151
+ function printXStringLiteral(path, opts, print) {
152
+ return concat(["`"].concat(path.map(print, "body")).concat("`"));
153
+ }
105
154
 
106
- return concat([beging, literalline, concat(parts), ending]);
107
- },
108
- string: makeList,
155
+ module.exports = {
156
+ "@CHAR": printChar,
157
+ dyna_symbol: printDynaSymbol,
109
158
  string_concat: (path, opts, print) =>
110
159
  group(
111
160
  concat([
@@ -114,14 +163,15 @@ module.exports = {
114
163
  indent(concat([hardline, path.call(print, "body", 1)]))
115
164
  ])
116
165
  ),
117
- string_dvar: surround("#{", "}"),
166
+ string_dvar: printStringDVar,
118
167
  string_embexpr: (path, opts, print) => {
119
168
  const parts = path.call(print, "body", 0);
120
169
 
121
- // If the interpolated expression is inside of an xstring literal (a string
122
- // that gets sent to the command line) then we don't want to automatically
123
- // indent, as this can lead to some very odd looking expressions
124
- if (path.getParentNode().type === "xstring") {
170
+ // If the interpolated expression is inside of a heredoc or an xstring
171
+ // literal (a string that gets sent to the command line) then we don't want
172
+ // to automatically indent, as this can lead to some very odd looking
173
+ // expressions
174
+ if (["heredoc", "xstring_literal"].includes(path.getParentNode().type)) {
125
175
  return concat(["#{", parts, "}"]);
126
176
  }
127
177
 
@@ -129,53 +179,7 @@ module.exports = {
129
179
  concat(["#{", indent(concat([softline, parts])), concat([softline, "}"])])
130
180
  );
131
181
  },
132
- string_literal: (path, { preferSingleQuotes }, print) => {
133
- const stringLiteral = path.getValue();
134
- const string = stringLiteral.body[0];
135
-
136
- // If this string is actually a heredoc, bail out and return to the print
137
- // function for heredocs
138
- if (string.type === "heredoc") {
139
- return path.call(print, "body", 0);
140
- }
141
-
142
- // If the string is empty, it will not have any parts, so just print out the
143
- // quotes corresponding to the config
144
- if (string.body.length === 0) {
145
- return preferSingleQuotes ? "''" : '""';
146
- }
147
-
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
- }
155
-
156
- const parts = string.body.map((part, index) => {
157
- if (part.type !== "@tstring_content") {
158
- // In this case, the part of the string is an embedded expression
159
- return path.call(print, "body", 0, "body", index);
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
- );
167
- });
168
-
169
- return concat([quote].concat(parts).concat(getClosingQuote(quote)));
170
- },
171
- symbol: prefix(":"),
172
- symbol_literal: concatBody,
173
- word_add: concatBody,
174
- word_new: empty,
175
- xstring: makeList,
176
- xstring_literal: (path, opts, print) => {
177
- const parts = path.call(print, "body", 0);
178
-
179
- return concat(["`"].concat(parts).concat("`"));
180
- }
182
+ string_literal: printStringLiteral,
183
+ symbol_literal: printSymbolLiteral,
184
+ xstring_literal: printXStringLiteral
181
185
  };