prettier 0.12.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ const { concat, join } = require("../builders");
2
+
3
+ const usingSymbols = path => {
4
+ const [left, right] = path.getValue().body.map(node => node.body[0].type);
5
+ return left === "symbol" && right === "symbol";
6
+ };
7
+
8
+ const identFromSymbol = (path, print, index) =>
9
+ path.call(print, "body", index, "body", 0, "body", 0);
10
+
11
+ const aliasError = (_path, _opts, _print) => {
12
+ throw new Error("can't make alias for the number variables");
13
+ };
14
+
15
+ const aliasVars = (path, opts, print) => {
16
+ if (usingSymbols(path)) {
17
+ return join(" ", [
18
+ identFromSymbol(path, print, 0),
19
+ identFromSymbol(path, print, 1)
20
+ ]);
21
+ }
22
+ return join(" ", path.map(print, "body"));
23
+ };
24
+
25
+ const alias = (path, opts, print) =>
26
+ concat(["alias ", aliasVars(path, opts, print)]);
27
+
28
+ module.exports = {
29
+ alias,
30
+ alias_error: aliasError,
31
+ var_alias: alias
32
+ };
@@ -0,0 +1,162 @@
1
+ const {
2
+ concat,
3
+ group,
4
+ ifBreak,
5
+ indent,
6
+ join,
7
+ line,
8
+ literalline,
9
+ softline
10
+ } = require("../builders");
11
+
12
+ const isStringArray = args =>
13
+ args.body.every(
14
+ arg =>
15
+ arg.type === "string_literal" &&
16
+ arg.body[0].body.length === 1 &&
17
+ arg.body[0].body[0].type === "@tstring_content" &&
18
+ !arg.body[0].body[0].body.includes(" ")
19
+ );
20
+
21
+ const isSymbolArray = args =>
22
+ args.body.every(arg => arg.type === "symbol_literal");
23
+
24
+ const makeArray = start => (path, opts, print) =>
25
+ [start].concat(path.map(print, "body"));
26
+
27
+ const getSpecialArrayParts = (path, print, args) =>
28
+ args.body.map((_arg, index) =>
29
+ path.call(print, "body", 0, "body", index, "body", 0, "body", 0)
30
+ );
31
+
32
+ const printAref = (path, opts, print) =>
33
+ group(
34
+ concat([
35
+ path.call(print, "body", 0),
36
+ "[",
37
+ indent(
38
+ concat([
39
+ softline,
40
+ join(concat([",", line]), path.call(print, "body", 1))
41
+ ])
42
+ ),
43
+ concat([softline, "]"])
44
+ ])
45
+ );
46
+
47
+ const printSpecialArray = parts =>
48
+ group(
49
+ concat([
50
+ parts[0],
51
+ "[",
52
+ indent(concat([softline, join(line, parts.slice(1))])),
53
+ concat([softline, "]"])
54
+ ])
55
+ );
56
+
57
+ // Extract out the actual elements, taking into account nesting with
58
+ // `args_add_star` nodes. The only nodes that get passed into this function are
59
+ // `args` or `args_add_star`.
60
+ const getElements = (node, elementPath) => {
61
+ if (node.type === "args") {
62
+ return node.body.map((element, index) => ({
63
+ element,
64
+ elementPath: elementPath.concat(["body", index])
65
+ }));
66
+ }
67
+
68
+ return getElements(node.body[0], elementPath.concat(["body", 0])).concat(
69
+ node.body.slice(1).map((element, index) => ({
70
+ element,
71
+ elementPath: elementPath.concat(["body", index + 1])
72
+ }))
73
+ );
74
+ };
75
+
76
+ module.exports = {
77
+ aref: (path, opts, print) => {
78
+ if (!path.getValue().body[1]) {
79
+ return concat([path.call(print, "body", 0), "[]"]);
80
+ }
81
+
82
+ return printAref(path, opts, print);
83
+ },
84
+ aref_field: printAref,
85
+ array: (path, { addTrailingCommas }, print) => {
86
+ const args = path.getValue().body[0];
87
+
88
+ if (args === null) {
89
+ return "[]";
90
+ }
91
+
92
+ if (isStringArray(args)) {
93
+ return printSpecialArray(
94
+ ["%w"].concat(getSpecialArrayParts(path, print, args))
95
+ );
96
+ }
97
+
98
+ if (isSymbolArray(args)) {
99
+ return printSpecialArray(
100
+ ["%i"].concat(getSpecialArrayParts(path, print, args))
101
+ );
102
+ }
103
+
104
+ if (!["args", "args_add_star"].includes(args.type)) {
105
+ return printSpecialArray(path.call(print, "body", 0));
106
+ }
107
+
108
+ const normalDocs = [];
109
+
110
+ const elementDocs = path.call(print, "body", 0);
111
+ const elements = getElements(path.getValue().body[0], ["body", 0]);
112
+
113
+ // We need to manually loop through the elements in the array in order to
114
+ // take care of heredocs printing (their commas go after the opening, as
115
+ // opposed to at the end).
116
+ elements.forEach(({ element, elementPath }, index) => {
117
+ const isInner = index !== elements.length - 1;
118
+
119
+ const isStraightHeredoc = element.type === "heredoc";
120
+ const isSquigglyHeredoc =
121
+ element.type === "string_literal" && element.body[0].type === "heredoc";
122
+
123
+ if (isStraightHeredoc || isSquigglyHeredoc) {
124
+ const heredocNode = isStraightHeredoc ? element : element.body[0];
125
+ const heredocPath = [print].concat(elementPath);
126
+
127
+ if (isSquigglyHeredoc) {
128
+ heredocPath.push("body", 0);
129
+ }
130
+
131
+ normalDocs.push(
132
+ heredocNode.beging,
133
+ isInner || addTrailingCommas ? "," : "",
134
+ literalline,
135
+ concat(path.map.apply(path, heredocPath.concat("body"))),
136
+ heredocNode.ending,
137
+ isInner ? line : ""
138
+ );
139
+ } else {
140
+ normalDocs.push(elementDocs[index]);
141
+
142
+ if (isInner) {
143
+ normalDocs.push(concat([",", line]));
144
+ } else if (addTrailingCommas) {
145
+ normalDocs.push(ifBreak(",", ""));
146
+ }
147
+ }
148
+ });
149
+
150
+ return group(
151
+ concat([
152
+ "[",
153
+ indent(concat([softline].concat(normalDocs))),
154
+ concat([softline, "]"])
155
+ ])
156
+ );
157
+ },
158
+ qsymbols: makeArray("%i"),
159
+ qwords: makeArray("%w"),
160
+ symbols: makeArray("%I"),
161
+ words: makeArray("%W")
162
+ };
@@ -0,0 +1,66 @@
1
+ const {
2
+ breakParent,
3
+ concat,
4
+ group,
5
+ ifBreak,
6
+ indent,
7
+ softline
8
+ } = require("../builders");
9
+ const { hasAncestor } = require("../utils");
10
+
11
+ const printBlock = (path, opts, print) => {
12
+ const [variables, statements] = path.getValue().body;
13
+ const stmts =
14
+ statements.type === "stmts" ? statements.body : statements.body[0].body;
15
+
16
+ let doBlockBody = "";
17
+ if (stmts.length !== 1 || stmts[0].type !== "void_stmt") {
18
+ doBlockBody = indent(concat([softline, path.call(print, "body", 1)]));
19
+ }
20
+
21
+ // If this block is nested underneath a command or command_call node, then we
22
+ // can't use `do...end` because that will get associated with the parent node
23
+ // as opposed to the current node (because of the difference in operator
24
+ // precedence). Instead, we still use a multi-line format but switch to using
25
+ // braces instead.
26
+ const useBraces = hasAncestor(path, ["command", "command_call"]);
27
+
28
+ const doBlock = concat([
29
+ useBraces ? " {" : " do",
30
+ variables ? concat([" ", path.call(print, "body", 0)]) : "",
31
+ doBlockBody,
32
+ concat([softline, useBraces ? "}" : "end"])
33
+ ]);
34
+
35
+ // We can hit this next pattern if within the block the only statement is a
36
+ // comment.
37
+ if (
38
+ stmts.length > 1 &&
39
+ stmts.filter(stmt => stmt.type !== "@comment").length === 1
40
+ ) {
41
+ return concat([breakParent, doBlock]);
42
+ }
43
+
44
+ // If the parent node is a command node, then there are no parentheses around
45
+ // the arguments to that command, so we need to break the block
46
+ if (["command", "command_call"].includes(path.getParentNode().body[0].type)) {
47
+ return concat([breakParent, doBlock]);
48
+ }
49
+
50
+ const hasBody = stmts.some(({ type }) => type !== "void_stmt");
51
+ const braceBlock = concat([
52
+ " {",
53
+ hasBody || variables ? " " : "",
54
+ variables ? path.call(print, "body", 0) : "",
55
+ path.call(print, "body", 1),
56
+ hasBody ? " " : "",
57
+ "}"
58
+ ]);
59
+
60
+ return group(ifBreak(doBlock, braceBlock));
61
+ };
62
+
63
+ module.exports = {
64
+ brace_block: printBlock,
65
+ do_block: printBlock
66
+ };
@@ -0,0 +1,28 @@
1
+ const { concat, group, indent, softline } = require("../builders");
2
+ const { makeCall } = require("../utils");
3
+
4
+ const noIndent = ["array", "hash", "method_add_block", "xstring_literal"];
5
+
6
+ module.exports = {
7
+ call: (path, opts, print) => {
8
+ const receiver = path.call(print, "body", 0);
9
+ const operator = makeCall(path, opts, print);
10
+ let name = path.getValue().body[2];
11
+
12
+ // You can call lambdas with a special syntax that looks like func.(*args).
13
+ // In this case, "call" is returned for the 3rd child node.
14
+ if (name !== "call") {
15
+ name = path.call(print, "body", 2);
16
+ }
17
+
18
+ // For certain left sides of the call nodes, we want to attach directly to
19
+ // the } or end.
20
+ if (noIndent.includes(path.getValue().body[0].type)) {
21
+ return concat([receiver, operator, name]);
22
+ }
23
+
24
+ return group(
25
+ concat([receiver, indent(concat([softline, operator, name]))])
26
+ );
27
+ }
28
+ };
@@ -0,0 +1,57 @@
1
+ const {
2
+ align,
3
+ concat,
4
+ fill,
5
+ group,
6
+ hardline,
7
+ indent,
8
+ line
9
+ } = require("../builders");
10
+
11
+ module.exports = {
12
+ case: (path, opts, print) => {
13
+ const statement = ["case"];
14
+
15
+ // You don't need to explicitly have something to test against in a case
16
+ // statement (without it it effectively becomes an if/elsif chain).
17
+ if (path.getValue().body[0]) {
18
+ statement.push(" ", path.call(print, "body", 0));
19
+ }
20
+
21
+ return concat(
22
+ statement.concat([hardline, path.call(print, "body", 1), hardline, "end"])
23
+ );
24
+ },
25
+ when: (path, opts, print) => {
26
+ const [_preds, _stmts, addition] = path.getValue().body;
27
+
28
+ // The `fill` builder command expects an array of docs alternating with
29
+ // line breaks. This is so it can loop through and determine where to break.
30
+ const preds = fill(
31
+ path
32
+ .call(print, "body", 0)
33
+ .reduce(
34
+ (accum, pred, index) =>
35
+ index === 0 ? [pred] : accum.concat([",", line, pred]),
36
+ null
37
+ )
38
+ );
39
+
40
+ const stmts = path.call(print, "body", 1);
41
+ const parts = [concat(["when ", align("when ".length, preds)])];
42
+
43
+ // It's possible in a when to just have empty void statements, in which case
44
+ // we would skip adding the body.
45
+ if (!stmts.parts.every(part => !part)) {
46
+ parts.push(indent(concat([hardline, stmts])));
47
+ }
48
+
49
+ // This is the next clause on the case statement, either another `when` or
50
+ // an `else` clause.
51
+ if (addition) {
52
+ parts.push(hardline, path.call(print, "body", 2));
53
+ }
54
+
55
+ return group(concat(parts));
56
+ }
57
+ };
@@ -0,0 +1,70 @@
1
+ const { align, concat, group, ifBreak, join, line } = require("../builders");
2
+ const { docLength, makeArgs, makeCall } = require("../utils");
3
+
4
+ const hasDef = node =>
5
+ node.body[1].type === "args_add_block" &&
6
+ node.body[1].body[0].type === "args" &&
7
+ node.body[1].body[0].body[0].type === "def";
8
+
9
+ module.exports = {
10
+ command: (path, opts, print) => {
11
+ const command = path.call(print, "body", 0);
12
+ const { args, heredocs } = makeArgs(path, opts, print, 1);
13
+
14
+ if (heredocs.length > 1) {
15
+ return concat([command, " ", join(", ", args)].concat(heredocs));
16
+ }
17
+
18
+ const joinedArgs = join(concat([",", line]), args);
19
+ const breakArgs = hasDef(path.getValue())
20
+ ? joinedArgs
21
+ : align(command.length + 1, joinedArgs);
22
+
23
+ const commandDoc = group(
24
+ ifBreak(
25
+ concat([command, " ", breakArgs]),
26
+ concat([command, " ", joinedArgs])
27
+ )
28
+ );
29
+
30
+ if (heredocs.length === 1) {
31
+ return group(concat([commandDoc].concat(heredocs)));
32
+ }
33
+
34
+ return commandDoc;
35
+ },
36
+ command_call: (path, opts, print) => {
37
+ const parts = [
38
+ path.call(print, "body", 0),
39
+ makeCall(path, opts, print),
40
+ path.call(print, "body", 2)
41
+ ];
42
+
43
+ if (!path.getValue().body[3]) {
44
+ return concat(parts);
45
+ }
46
+
47
+ parts.push(" ");
48
+ const { args, heredocs } = makeArgs(path, opts, print, 3);
49
+
50
+ if (heredocs.length > 1) {
51
+ return concat(parts.concat([join(", ", args)]).concat(heredocs));
52
+ }
53
+
54
+ const joinedArgs = join(concat([",", line]), args);
55
+ const breakArgs =
56
+ path.getValue().body[2].body === "to"
57
+ ? joinedArgs
58
+ : align(docLength(concat(parts)), joinedArgs);
59
+
60
+ const commandDoc = group(
61
+ ifBreak(concat(parts.concat(breakArgs)), concat(parts.concat(joinedArgs)))
62
+ );
63
+
64
+ if (heredocs.length === 1) {
65
+ return group(concat([commandDoc].concat(heredocs)));
66
+ }
67
+
68
+ return commandDoc;
69
+ }
70
+ };
@@ -0,0 +1,154 @@
1
+ const {
2
+ align,
3
+ breakParent,
4
+ concat,
5
+ hardline,
6
+ group,
7
+ ifBreak,
8
+ indent,
9
+ softline
10
+ } = require("../builders");
11
+
12
+ const noTernary = [
13
+ "@comment",
14
+ "alias",
15
+ "assign",
16
+ "break",
17
+ "command",
18
+ "command_call",
19
+ "if_mod",
20
+ "ifop",
21
+ "lambda",
22
+ "massign",
23
+ "next",
24
+ "opassign",
25
+ "rescue_mod",
26
+ "return",
27
+ "return0",
28
+ "super",
29
+ "undef",
30
+ "unless_mod",
31
+ "until_mod",
32
+ "var_alias",
33
+ "void_stmt",
34
+ "while_mod",
35
+ "yield",
36
+ "yield0",
37
+ "zsuper"
38
+ ];
39
+
40
+ const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
41
+ concat([
42
+ `${keyword} `,
43
+ align(keyword.length - 1, path.call(print, "body", 0)),
44
+ indent(concat([softline, path.call(print, "body", 1)])),
45
+ concat([softline, path.call(print, "body", 2)]),
46
+ concat([softline, "end"]),
47
+ breaking ? breakParent : ""
48
+ ]);
49
+
50
+ const printTernaryConditions = (keyword, truthyValue, falsyValue) => {
51
+ const parts = [truthyValue, " : ", falsyValue];
52
+ return keyword === "if" ? parts : parts.reverse();
53
+ };
54
+
55
+ const canTernary = stmts =>
56
+ stmts.body.length === 1 && !noTernary.includes(stmts.body[0].type);
57
+
58
+ const printConditional = keyword => (path, { inlineConditionals }, print) => {
59
+ const [_predicate, stmts, addition] = path.getValue().body;
60
+
61
+ // If the addition is not an elsif or an else, then it's the second half of a
62
+ // ternary expression
63
+ if (addition && addition.type !== "elsif" && addition.type !== "else") {
64
+ const parts = [path.call(print, "body", 0), " ? "];
65
+ const truthyValue = path.call(print, "body", 1);
66
+ const falsyValue = path.call(print, "body", 2);
67
+
68
+ return group(
69
+ ifBreak(
70
+ concat([
71
+ `${keyword} `,
72
+ path.call(print, "body", 0),
73
+ indent(concat([softline, path.call(print, "body", 1)])),
74
+ concat([softline, "else"]),
75
+ indent(concat([softline, path.call(print, "body", 2)])),
76
+ concat([softline, "end"])
77
+ ]),
78
+ concat(
79
+ parts.concat(printTernaryConditions(keyword, truthyValue, falsyValue))
80
+ )
81
+ )
82
+ );
83
+ }
84
+
85
+ // If there is an else and only an else, attempt to shorten to a ternary
86
+ if (
87
+ addition &&
88
+ addition.type === "else" &&
89
+ canTernary(stmts) &&
90
+ canTernary(addition.body[0])
91
+ ) {
92
+ const ternary = printTernaryConditions(
93
+ keyword,
94
+ path.call(print, "body", 1),
95
+ path.call(print, "body", 2, "body", 0)
96
+ );
97
+
98
+ return group(
99
+ ifBreak(
100
+ printWithAddition(keyword, path, print),
101
+ concat([path.call(print, "body", 0), " ? "].concat(ternary))
102
+ )
103
+ );
104
+ }
105
+
106
+ // If there's an additional clause, we know we can't go for the inline option
107
+ if (addition) {
108
+ return group(printWithAddition(keyword, path, print, { breaking: true }));
109
+ }
110
+
111
+ // If it's short enough, favor the inline conditional
112
+ return group(
113
+ ifBreak(
114
+ concat([
115
+ `${keyword} `,
116
+ align(keyword.length - 1, path.call(print, "body", 0)),
117
+ indent(concat([softline, path.call(print, "body", 1)])),
118
+ concat([softline, "end"])
119
+ ]),
120
+ concat([
121
+ inlineConditionals ? "" : breakParent,
122
+ path.call(print, "body", 1),
123
+ ` ${keyword} `,
124
+ path.call(print, "body", 0)
125
+ ])
126
+ )
127
+ );
128
+ };
129
+
130
+ module.exports = {
131
+ elsif: (path, opts, print) => {
132
+ const [_predicate, _statements, addition] = path.getValue().body;
133
+ const parts = [
134
+ group(
135
+ concat([
136
+ "elsif ",
137
+ align("elsif".length - 1, path.call(print, "body", 0))
138
+ ])
139
+ ),
140
+ indent(concat([hardline, path.call(print, "body", 1)]))
141
+ ];
142
+
143
+ if (addition) {
144
+ parts.push(group(concat([hardline, path.call(print, "body", 2)])));
145
+ }
146
+
147
+ return group(concat(parts));
148
+ },
149
+ if: printConditional("if"),
150
+ ifop: printConditional("if"),
151
+ if_mod: printConditional("if"),
152
+ unless: printConditional("unless"),
153
+ unless_mod: printConditional("unless")
154
+ };