prettier 0.20.0 → 1.0.0.pre.rc2

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.
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
  };