prettier 0.15.1 → 0.16.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 +47 -1
- data/CONTRIBUTING.md +127 -96
- data/README.md +31 -7
- data/node_modules/prettier/bin-prettier.js +28420 -26950
- data/node_modules/prettier/index.js +28307 -26856
- data/node_modules/prettier/third-party.js +813 -580
- data/package.json +2 -2
- data/src/haml.js +21 -0
- data/src/haml/embed.js +58 -0
- data/src/haml/nodes/comment.js +27 -0
- data/src/haml/nodes/doctype.js +32 -0
- data/src/haml/nodes/filter.js +16 -0
- data/src/haml/nodes/hamlComment.js +21 -0
- data/src/haml/nodes/script.js +29 -0
- data/src/haml/nodes/silentScript.js +30 -0
- data/src/haml/nodes/tag.js +151 -0
- data/src/haml/parse.js +18 -0
- data/src/haml/parse.rb +65 -0
- data/src/haml/print.js +38 -0
- data/src/nodes.js +1 -0
- data/src/nodes/conditionals.js +45 -30
- data/src/nodes/flow.js +1 -34
- data/src/nodes/massign.js +16 -17
- data/src/nodes/return.js +60 -0
- data/src/nodes/statements.js +1 -4
- data/src/nodes/strings.js +57 -5
- data/src/ripper.rb +27 -1
- data/src/ruby.js +18 -0
- metadata +19 -6
data/src/haml/print.js
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
const { concat, hardline, join, markAsRoot } = require("../prettier");
|
2
|
+
|
3
|
+
const comment = require("./nodes/comment");
|
4
|
+
const doctype = require("./nodes/doctype");
|
5
|
+
const filter = require("./nodes/filter");
|
6
|
+
const hamlComment = require("./nodes/hamlComment");
|
7
|
+
const script = require("./nodes/script");
|
8
|
+
const silentScript = require("./nodes/silentScript");
|
9
|
+
const tag = require("./nodes/tag");
|
10
|
+
|
11
|
+
const nodes = {
|
12
|
+
comment,
|
13
|
+
doctype,
|
14
|
+
filter,
|
15
|
+
haml_comment: hamlComment,
|
16
|
+
plain: (path, _opts, _print) => {
|
17
|
+
const { value } = path.getValue();
|
18
|
+
|
19
|
+
return value.text.startsWith("=") ? `\\${value.text}` : value.text;
|
20
|
+
},
|
21
|
+
root: (path, opts, print) =>
|
22
|
+
markAsRoot(concat([join(hardline, path.map(print, "children")), hardline])),
|
23
|
+
script,
|
24
|
+
silent_script: silentScript,
|
25
|
+
tag
|
26
|
+
};
|
27
|
+
|
28
|
+
const genericPrint = (path, opts, print) => {
|
29
|
+
const { type } = path.getValue();
|
30
|
+
|
31
|
+
if (!(type in nodes)) {
|
32
|
+
throw new Error(`Unsupported node encountered: ${type}`);
|
33
|
+
}
|
34
|
+
|
35
|
+
return nodes[type](path, opts, print);
|
36
|
+
};
|
37
|
+
|
38
|
+
module.exports = genericPrint;
|
data/src/nodes.js
CHANGED
data/src/nodes/conditionals.js
CHANGED
@@ -10,34 +10,6 @@ const {
|
|
10
10
|
} = require("../prettier");
|
11
11
|
const { containsAssignment } = require("../utils");
|
12
12
|
|
13
|
-
const noTernary = [
|
14
|
-
"@comment",
|
15
|
-
"alias",
|
16
|
-
"assign",
|
17
|
-
"break",
|
18
|
-
"command",
|
19
|
-
"command_call",
|
20
|
-
"if_mod",
|
21
|
-
"ifop",
|
22
|
-
"lambda",
|
23
|
-
"massign",
|
24
|
-
"next",
|
25
|
-
"opassign",
|
26
|
-
"rescue_mod",
|
27
|
-
"return",
|
28
|
-
"return0",
|
29
|
-
"super",
|
30
|
-
"undef",
|
31
|
-
"unless_mod",
|
32
|
-
"until_mod",
|
33
|
-
"var_alias",
|
34
|
-
"void_stmt",
|
35
|
-
"while_mod",
|
36
|
-
"yield",
|
37
|
-
"yield0",
|
38
|
-
"zsuper"
|
39
|
-
];
|
40
|
-
|
41
13
|
const printWithAddition = (keyword, path, print, { breaking = false } = {}) =>
|
42
14
|
concat([
|
43
15
|
`${keyword} `,
|
@@ -152,11 +124,54 @@ const printSingle = keyword => (path, { inlineConditionals }, print) => {
|
|
152
124
|
return group(ifBreak(multiline, inline));
|
153
125
|
};
|
154
126
|
|
127
|
+
const noTernary = [
|
128
|
+
"@comment",
|
129
|
+
"alias",
|
130
|
+
"assign",
|
131
|
+
"break",
|
132
|
+
"command",
|
133
|
+
"command_call",
|
134
|
+
"if_mod",
|
135
|
+
"ifop",
|
136
|
+
"lambda",
|
137
|
+
"massign",
|
138
|
+
"next",
|
139
|
+
"opassign",
|
140
|
+
"rescue_mod",
|
141
|
+
"return",
|
142
|
+
"return0",
|
143
|
+
"super",
|
144
|
+
"undef",
|
145
|
+
"unless_mod",
|
146
|
+
"until_mod",
|
147
|
+
"var_alias",
|
148
|
+
"void_stmt",
|
149
|
+
"while_mod",
|
150
|
+
"yield",
|
151
|
+
"yield0",
|
152
|
+
"zsuper"
|
153
|
+
];
|
154
|
+
|
155
155
|
// Certain expressions cannot be reduced to a ternary without adding parens
|
156
156
|
// around them. In this case we say they cannot be ternaried and default instead
|
157
157
|
// to breaking them into multiple lines.
|
158
|
-
const canTernaryStmts = stmts =>
|
159
|
-
stmts.body.length
|
158
|
+
const canTernaryStmts = stmts => {
|
159
|
+
if (stmts.body.length !== 1) {
|
160
|
+
return false;
|
161
|
+
}
|
162
|
+
|
163
|
+
const stmt = stmts.body[0];
|
164
|
+
|
165
|
+
// If the user is using one of the lower precedence "and" or "or" operators,
|
166
|
+
// then we can't use a ternary expression as it would break the flow control.
|
167
|
+
if (stmt.type === "binary" && ["and", "or"].includes(stmt.body[1])) {
|
168
|
+
return false;
|
169
|
+
}
|
170
|
+
|
171
|
+
// Check against the blocklist of statement types that are not allowed to be
|
172
|
+
// a part of a ternary expression.
|
173
|
+
return !noTernary.includes(stmt.type);
|
174
|
+
};
|
160
175
|
|
161
176
|
// In order for an `if` or `unless` expression to be shortened to a ternary,
|
162
177
|
// there has to be one and only one "addition" (another clause attached) which
|
data/src/nodes/flow.js
CHANGED
@@ -1,11 +1,4 @@
|
|
1
|
-
const {
|
2
|
-
concat,
|
3
|
-
group,
|
4
|
-
ifBreak,
|
5
|
-
indent,
|
6
|
-
join,
|
7
|
-
softline
|
8
|
-
} = require("../prettier");
|
1
|
+
const { concat, join } = require("../prettier");
|
9
2
|
const { literal } = require("../utils");
|
10
3
|
|
11
4
|
module.exports = {
|
@@ -42,32 +35,6 @@ module.exports = {
|
|
42
35
|
|
43
36
|
return concat(["next ", join(", ", path.call(print, "body", 0))]);
|
44
37
|
},
|
45
|
-
return: (path, opts, print) => {
|
46
|
-
const args = path.getValue().body[0].body[0];
|
47
|
-
|
48
|
-
if (!args) {
|
49
|
-
return "return";
|
50
|
-
}
|
51
|
-
|
52
|
-
let value = join(", ", path.call(print, "body", 0));
|
53
|
-
|
54
|
-
// If the body of the return contains parens, then just skip directly to the
|
55
|
-
// content of the parens so that we can skip printing parens if we don't
|
56
|
-
// want them.
|
57
|
-
if (args.body[0] && args.body[0].type === "paren") {
|
58
|
-
value = path.call(print, "body", 0, "body", 0, "body", 0, "body", 0);
|
59
|
-
}
|
60
|
-
|
61
|
-
return group(
|
62
|
-
concat([
|
63
|
-
"return",
|
64
|
-
ifBreak("(", " "),
|
65
|
-
indent(concat([softline, value])),
|
66
|
-
concat([softline, ifBreak(")", "")])
|
67
|
-
])
|
68
|
-
);
|
69
|
-
},
|
70
|
-
return0: literal("return"),
|
71
38
|
yield: (path, opts, print) => {
|
72
39
|
if (path.getValue().body[0].type === "paren") {
|
73
40
|
return concat(["yield", path.call(print, "body", 0)]);
|
data/src/nodes/massign.js
CHANGED
@@ -13,12 +13,13 @@ module.exports = {
|
|
13
13
|
right = group(join(concat([",", line]), right));
|
14
14
|
}
|
15
15
|
|
16
|
+
const parts = [join(concat([",", line]), path.call(print, "body", 0))];
|
17
|
+
if (path.getValue().body[0].comma) {
|
18
|
+
parts.push(",");
|
19
|
+
}
|
20
|
+
|
16
21
|
return group(
|
17
|
-
concat([
|
18
|
-
group(join(concat([",", line]), path.call(print, "body", 0))),
|
19
|
-
" =",
|
20
|
-
indent(concat([line, right]))
|
21
|
-
])
|
22
|
+
concat([group(concat(parts)), " =", indent(concat([line, right]))])
|
22
23
|
);
|
23
24
|
},
|
24
25
|
mlhs: makeList,
|
@@ -40,18 +41,16 @@ module.exports = {
|
|
40
41
|
return path.call(print, "body", 0);
|
41
42
|
}
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
])
|
54
|
-
);
|
44
|
+
const parts = [
|
45
|
+
softline,
|
46
|
+
join(concat([",", line]), path.call(print, "body", 0))
|
47
|
+
];
|
48
|
+
|
49
|
+
if (path.getValue().body[0].comma) {
|
50
|
+
parts.push(",");
|
51
|
+
}
|
52
|
+
|
53
|
+
return group(concat(["(", indent(concat(parts)), concat([softline, ")"])]));
|
55
54
|
},
|
56
55
|
mrhs: makeList,
|
57
56
|
mrhs_add_star: (path, opts, print) =>
|
data/src/nodes/return.js
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
const {
|
2
|
+
concat,
|
3
|
+
group,
|
4
|
+
ifBreak,
|
5
|
+
indent,
|
6
|
+
line,
|
7
|
+
join,
|
8
|
+
softline
|
9
|
+
} = require("../prettier");
|
10
|
+
const { literal } = require("../utils");
|
11
|
+
|
12
|
+
const printReturn = (path, opts, print) => {
|
13
|
+
let args = path.getValue().body[0].body[0];
|
14
|
+
let steps = ["body", 0, "body", 0];
|
15
|
+
|
16
|
+
if (!args) {
|
17
|
+
return "return";
|
18
|
+
}
|
19
|
+
|
20
|
+
// If the body of the return contains parens, then just skip directly to the
|
21
|
+
// content of the parens so that we can skip printing parens if we don't
|
22
|
+
// want them.
|
23
|
+
if (args.body[0] && args.body[0].type === "paren") {
|
24
|
+
args = args.body[0].body[0];
|
25
|
+
steps = steps.concat("body", 0, "body", 0);
|
26
|
+
}
|
27
|
+
|
28
|
+
// If we're returning an array literal that isn't a special array, then we
|
29
|
+
// want to grab the arguments so that we can print them out as if they were
|
30
|
+
// normal return arguments.
|
31
|
+
if (
|
32
|
+
args.body[0] &&
|
33
|
+
args.body[0].type === "array" &&
|
34
|
+
["args", "args_add_star"].includes(args.body[0].body[0].type)
|
35
|
+
) {
|
36
|
+
steps = steps.concat("body", 0, "body", 0);
|
37
|
+
}
|
38
|
+
|
39
|
+
// Now that we've established which actual node is the arguments to return,
|
40
|
+
// we grab it out of the path by diving down the steps that we've set up.
|
41
|
+
const parts = path.call.apply(path, [print].concat(steps));
|
42
|
+
|
43
|
+
// If we got the value straight out of the parens, then `parts` would only
|
44
|
+
// be a singular doc as opposed to an array.
|
45
|
+
const value = Array.isArray(parts) ? join(concat([",", line]), parts) : parts;
|
46
|
+
|
47
|
+
return group(
|
48
|
+
concat([
|
49
|
+
"return",
|
50
|
+
ifBreak(parts.length > 1 ? " [" : "(", " "),
|
51
|
+
indent(concat([softline, value])),
|
52
|
+
concat([softline, ifBreak(parts.length > 1 ? "]" : ")", "")])
|
53
|
+
])
|
54
|
+
);
|
55
|
+
};
|
56
|
+
|
57
|
+
module.exports = {
|
58
|
+
return: printReturn,
|
59
|
+
return0: literal("return")
|
60
|
+
};
|
data/src/nodes/statements.js
CHANGED
@@ -7,7 +7,6 @@ const {
|
|
7
7
|
join,
|
8
8
|
line,
|
9
9
|
literalline,
|
10
|
-
markAsRoot,
|
11
10
|
softline,
|
12
11
|
trim
|
13
12
|
} = require("../prettier");
|
@@ -66,9 +65,7 @@ module.exports = {
|
|
66
65
|
);
|
67
66
|
},
|
68
67
|
program: (path, opts, print) =>
|
69
|
-
|
70
|
-
concat([join(literalline, path.map(print, "body")), literalline])
|
71
|
-
),
|
68
|
+
concat([join(hardline, path.map(print, "body")), hardline]),
|
72
69
|
stmts: (path, opts, print) => {
|
73
70
|
const stmts = path.getValue().body;
|
74
71
|
const parts = [];
|
data/src/nodes/strings.js
CHANGED
@@ -4,7 +4,8 @@ const {
|
|
4
4
|
hardline,
|
5
5
|
indent,
|
6
6
|
literalline,
|
7
|
-
softline
|
7
|
+
softline,
|
8
|
+
join
|
8
9
|
} = require("../prettier");
|
9
10
|
const { concatBody, empty, makeList, prefix, surround } = require("../utils");
|
10
11
|
const escapePattern = require("../escapePattern");
|
@@ -38,13 +39,14 @@ const getStringQuote = (string, preferSingleQuotes) => {
|
|
38
39
|
|
39
40
|
const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
|
40
41
|
|
41
|
-
const makeString = (content, enclosingQuote) => {
|
42
|
+
const makeString = (content, enclosingQuote, originalQuote) => {
|
43
|
+
const replaceOther = ["'", '"'].includes(originalQuote);
|
42
44
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
43
45
|
|
44
46
|
// Escape and unescape single and double quotes as needed to be able to
|
45
47
|
// enclose `content` with `enclosingQuote`.
|
46
48
|
return content.replace(quotePattern, (match, escaped, quote) => {
|
47
|
-
if (escaped === otherQuote) {
|
49
|
+
if (replaceOther && escaped === otherQuote) {
|
48
50
|
return escaped;
|
49
51
|
}
|
50
52
|
|
@@ -60,6 +62,53 @@ const makeString = (content, enclosingQuote) => {
|
|
60
62
|
});
|
61
63
|
};
|
62
64
|
|
65
|
+
// The `parts` argument that comes into this function is an array of either
|
66
|
+
// printed embedded expressions or plain strings (that themselves can contain
|
67
|
+
// newlines). What we want to return is an array where each element represents a
|
68
|
+
// line in the original text. So we end up tracking every line as we go and
|
69
|
+
// pushing onto a `currentLine` variable, then when we hit a new line we push
|
70
|
+
// the current line onto the `lines` variable until we've used up every part.
|
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
|
+
}
|
98
|
+
|
99
|
+
if (splits.length > 1) {
|
100
|
+
lines = lines.concat(splits.slice(1, -1));
|
101
|
+
currentLine.push(splits[splits.length - 1]);
|
102
|
+
}
|
103
|
+
});
|
104
|
+
|
105
|
+
if (currentLine.length > 0) {
|
106
|
+
lines = lines.concat(currentLine);
|
107
|
+
}
|
108
|
+
|
109
|
+
return lines;
|
110
|
+
};
|
111
|
+
|
63
112
|
module.exports = {
|
64
113
|
"@CHAR": (path, { preferSingleQuotes }, _print) => {
|
65
114
|
const { body } = path.getValue();
|
@@ -78,10 +127,13 @@ module.exports = {
|
|
78
127
|
},
|
79
128
|
heredoc: (path, opts, print) => {
|
80
129
|
const { beging, ending } = path.getValue();
|
130
|
+
const lines = makeHeredocLines(path.map(print, "body"));
|
81
131
|
|
82
132
|
return concat([
|
83
133
|
beging,
|
84
|
-
|
134
|
+
literalline,
|
135
|
+
join(literalline, lines),
|
136
|
+
literalline,
|
85
137
|
ending
|
86
138
|
]);
|
87
139
|
},
|
@@ -130,7 +182,7 @@ module.exports = {
|
|
130
182
|
string.body.forEach((part, index) => {
|
131
183
|
if (part.type === "@tstring_content") {
|
132
184
|
// In this case, the part of the string is just regular string content
|
133
|
-
parts.push(makeString(part.body, quote));
|
185
|
+
parts.push(makeString(part.body, quote, string.quote));
|
134
186
|
} else {
|
135
187
|
// In this case, the part of the string is an embedded expression
|
136
188
|
parts.push(path.call(print, "body", 0, "body", index));
|
data/src/ripper.rb
CHANGED
@@ -14,11 +14,12 @@ require 'json' unless defined?(JSON)
|
|
14
14
|
require 'ripper'
|
15
15
|
|
16
16
|
class RipperJS < Ripper
|
17
|
-
attr_reader :lines, :__end__
|
17
|
+
attr_reader :source, :lines, :__end__
|
18
18
|
|
19
19
|
def initialize(source, *args)
|
20
20
|
super(source, *args)
|
21
21
|
|
22
|
+
@source = source
|
22
23
|
@lines = source.split("\n")
|
23
24
|
@__end__ = nil
|
24
25
|
end
|
@@ -750,6 +751,31 @@ class RipperJS < Ripper
|
|
750
751
|
sexp.merge!(type: :lambda, body: [params, stmts])
|
751
752
|
end
|
752
753
|
end
|
754
|
+
|
755
|
+
# We need to track for `mlhs_paren` and `massign` nodes whether or not
|
756
|
+
# there was an extra comma at the end of the expression. For some reason
|
757
|
+
# it's not showing up in the AST in an obvious way. In this case we're
|
758
|
+
# just simplifying everything by adding an additional field to `mlhs`
|
759
|
+
# nodes called `comma` that indicates whether or not there was an extra.
|
760
|
+
def on_mlhs_paren(body)
|
761
|
+
super.tap do |node|
|
762
|
+
next unless body[:type] == :mlhs
|
763
|
+
|
764
|
+
ending = source.rindex(')', char_pos)
|
765
|
+
buffer = source[(node[:char_start] + 1)...ending]
|
766
|
+
|
767
|
+
body[:comma] = buffer.strip.end_with?(',')
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
def on_massign(left, right)
|
772
|
+
super.tap do
|
773
|
+
next unless left[:type] == :mlhs
|
774
|
+
|
775
|
+
range = left[:char_start]..left[:char_end]
|
776
|
+
left[:comma] = source[range].strip.end_with?(',')
|
777
|
+
end
|
778
|
+
end
|
753
779
|
end
|
754
780
|
)
|
755
781
|
end
|