src/nodes/arrays.js in prettier-0.21.0 vs src/nodes/arrays.js in prettier-0.22.0
- old
+ new
@@ -1,134 +1,172 @@
const {
concat,
group,
+ hardline,
ifBreak,
indent,
join,
line,
- literalline,
softline
} = require("../prettier");
-const preserveArraySubstrings = [" ", "\\"];
+// Checks that every argument within this args node is a string_literal node
+// that has no spaces or interpolations. This means we're dealing with an array
+// that looks something like:
+//
+// ['a', 'b', 'c']
+//
+function isStringArray(args) {
+ return args.body.every((arg) => {
+ // We want to verify that every node inside of this array is a string
+ // literal. We also want to make sure none of them have comments attached.
+ if (arg.type !== "string_literal" || arg.comments) {
+ return false;
+ }
-const isStringArray = (args) =>
- args.body.every(
- (arg) =>
- arg.type === "string_literal" &&
- arg.body[0].body.length === 1 &&
- arg.body[0].body[0].type === "@tstring_content" &&
- !preserveArraySubstrings.some((str) =>
- arg.body[0].body[0].body.includes(str)
- )
- );
+ // If the string has multiple parts (meaning plain string content but also
+ // interpolated content) then we know it's not a simple string.
+ if (arg.body.length !== 1) {
+ return false;
+ }
-const isSymbolArray = (args) =>
- args.body.every((arg) => arg.type === "symbol_literal");
+ const part = arg.body[0];
-const makeArray = (start) => (path, opts, print) =>
- [start].concat(path.map(print, "body"));
+ // If the only part of this string is not @tstring_content then it's
+ // interpolated, so again we can return false.
+ if (part.type !== "@tstring_content") {
+ return false;
+ }
-const getSpecialArrayParts = (path, print, args) =>
- args.body.map((_arg, index) =>
- path.call(print, "body", 0, "body", index, "body", 0, "body", 0)
+ // Finally, verify that the string doesn't contain a space or an escape
+ // character so that we know it can be put into a string literal array.
+ return !part.body.includes(" ") && !part.body.includes("\\");
+ });
+}
+
+// Checks that every argument within this args node is a symbol_literal node (as
+// opposed to a dyna_symbol) so it has no interpolation. This means we're
+// dealing with an array that looks something like:
+//
+// [:a, :b, :c]
+//
+function isSymbolArray(args) {
+ return args.body.every(
+ (arg) => arg.type === "symbol_literal" && !arg.comments
);
+}
-const printSpecialArray = (parts) =>
- group(
+// Prints out a word that is a part of a special array literal that accepts
+// interpolation. The body is an array of either plain strings or interpolated
+// expressions.
+function printSpecialArrayWord(path, opts, print) {
+ return concat(path.map(print, "body"));
+}
+
+// Prints out a special array literal. Accepts the parts of the array literal as
+// an argument, where the first element of the parts array is a string that
+// contains the special start.
+function printSpecialArrayParts(parts) {
+ return group(
concat([
parts[0],
"[",
indent(concat([softline, join(line, parts.slice(1))])),
concat([softline, "]"])
])
);
+}
-// Extract out the actual elements, taking into account nesting with
-// `args_add_star` nodes. The only nodes that get passed into this function are
-// `args` or `args_add_star`.
-const getElements = (node, elementPath) => {
- if (node.type === "args") {
- return node.body.map((element, index) => ({
- element,
- elementPath: elementPath.concat(["body", index])
- }));
- }
+// Generates a print function with an embedded special start character for the
+// specific type of array literal that we're dealing with. The print function
+// returns an array as it expects to eventually be handed off to
+// printSpecialArrayParts.
+function printSpecialArray(start) {
+ return function printSpecialArrayWithStart(path, opts, print) {
+ return [start].concat(path.map(print, "body"));
+ };
+}
- return getElements(node.body[0], elementPath.concat(["body", 0])).concat(
- node.body.slice(1).map((element, index) => ({
- element,
- elementPath: elementPath.concat(["body", index + 1])
- }))
- );
-};
+function printEmptyArrayWithComments(path, opts) {
+ const arrayNode = path.getValue();
-module.exports = {
- array: (path, { addTrailingCommas }, print) => {
- const args = path.getValue().body[0];
+ const printComment = (commentPath, index) => {
+ arrayNode.comments[index].printed = true;
+ return opts.printer.printComment(commentPath);
+ };
- if (args === null) {
- return "[]";
- }
+ return concat([
+ "[",
+ indent(
+ concat([hardline, join(hardline, path.map(printComment, "comments"))])
+ ),
+ line,
+ "]"
+ ]);
+}
- if (isStringArray(args)) {
- return printSpecialArray(
- ["%w"].concat(getSpecialArrayParts(path, print, args))
- );
- }
+// An array node is any literal array in Ruby. This includes all of the special
+// array literals as well as regular arrays. If it is a special array literal
+// then it will have one child that represents the special array, otherwise it
+// will have one child that contains all of the elements of the array.
+function printArray(path, opts, print) {
+ const array = path.getValue();
+ const args = array.body[0];
- if (isSymbolArray(args)) {
- return printSpecialArray(
- ["%i"].concat(getSpecialArrayParts(path, print, args))
- );
- }
+ // If there is no inner arguments node, then we're dealing with an empty
+ // array, so we can go ahead and return.
+ if (args === null) {
+ return array.comments ? printEmptyArrayWithComments(path, opts) : "[]";
+ }
- if (!["args", "args_add_star"].includes(args.type)) {
- return printSpecialArray(path.call(print, "body", 0));
- }
+ // If we have an array that contains only simple string literals with no
+ // spaces or interpolation, then we're going to print a %w array.
+ if (isStringArray(args)) {
+ const printString = (stringPath) => stringPath.call(print, "body", 0);
+ const parts = path.map(printString, "body", 0, "body");
- const normalDocs = [];
+ return printSpecialArrayParts(["%w"].concat(parts));
+ }
- const elementDocs = path.call(print, "body", 0);
- const elements = getElements(path.getValue().body[0], ["body", 0]);
+ // If we have an array that contains only simple symbol literals with no
+ // interpolation, then we're going to print a %i array.
+ if (isSymbolArray(args)) {
+ const printSymbol = (symbolPath) => symbolPath.call(print, "body", 0);
+ const parts = path.map(printSymbol, "body", 0, "body");
- // We need to manually loop through the elements in the array in order to
- // take care of heredocs printing (their commas go after the opening, as
- // opposed to at the end).
- elements.forEach(({ element, elementPath }, index) => {
- const isInner = index !== elements.length - 1;
+ return printSpecialArrayParts(["%i"].concat(parts));
+ }
- if (element.type === "heredoc") {
- normalDocs.push(
- element.beging,
- isInner || addTrailingCommas ? "," : "",
- literalline,
- concat(
- path.map.apply(path, [print].concat(elementPath).concat("body"))
- ),
- element.ending,
- isInner ? line : ""
- );
- } else {
- normalDocs.push(elementDocs[index]);
+ // If we don't have a regular args node at this point then we have a special
+ // array literal. In that case we're going to print out the body (which will
+ // return to us an array with the first one being the start of the array) and
+ // send that over to the printSpecialArrayParts function.
+ if (!["args", "args_add_star"].includes(args.type)) {
+ return printSpecialArrayParts(path.call(print, "body", 0));
+ }
- if (isInner) {
- normalDocs.push(concat([",", line]));
- } else if (addTrailingCommas) {
- normalDocs.push(ifBreak(",", ""));
- }
- }
- });
+ // Here we have a normal array of any type of object with no special literal
+ // types or anything.
+ return group(
+ concat([
+ "[",
+ indent(
+ concat([
+ softline,
+ join(concat([",", line]), path.call(print, "body", 0)),
+ opts.addTrailingCommas ? ifBreak(",", "") : ""
+ ])
+ ),
+ softline,
+ "]"
+ ])
+ );
+}
- return group(
- concat([
- "[",
- indent(concat([softline].concat(normalDocs))),
- concat([softline, "]"])
- ])
- );
- },
- qsymbols: makeArray("%i"),
- qwords: makeArray("%w"),
- symbols: makeArray("%I"),
- words: makeArray("%W")
+module.exports = {
+ array: printArray,
+ qsymbols: printSpecialArray("%i"),
+ qwords: printSpecialArray("%w"),
+ symbols: printSpecialArray("%I"),
+ word: printSpecialArrayWord,
+ words: printSpecialArray("%W")
};