prettier 1.4.0 → 1.5.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +318 -377
- data/package.json +1 -1
- data/src/plugin.js +1 -0
- data/src/rbs/parser.rb +8 -2
- data/src/ruby/embed.js +54 -8
- data/src/ruby/nodes/args.js +20 -6
- data/src/ruby/nodes/class.js +17 -27
- data/src/ruby/nodes/conditionals.js +1 -1
- data/src/ruby/nodes/hashes.js +28 -14
- data/src/ruby/nodes/loops.js +4 -10
- data/src/ruby/nodes/methods.js +4 -11
- data/src/ruby/nodes/statements.js +3 -5
- data/src/ruby/parser.rb +2 -1
- data/src/utils.js +1 -0
- data/src/utils/isEmptyBodyStmt.js +7 -0
- data/src/utils/isEmptyStmts.js +9 -5
- data/src/utils/printEmptyCollection.js +9 -2
- metadata +4 -3
data/package.json
CHANGED
data/src/plugin.js
CHANGED
data/src/rbs/parser.rb
CHANGED
@@ -23,8 +23,14 @@ end
|
|
23
23
|
class RBS::Location
|
24
24
|
def to_json(*args)
|
25
25
|
{
|
26
|
-
start: {
|
27
|
-
|
26
|
+
start: {
|
27
|
+
line: start_line,
|
28
|
+
column: start_column
|
29
|
+
},
|
30
|
+
end: {
|
31
|
+
line: end_line,
|
32
|
+
column: end_column
|
33
|
+
},
|
28
34
|
start_pos: start_pos,
|
29
35
|
end_pos: end_pos
|
30
36
|
}.to_json(*args)
|
data/src/ruby/embed.js
CHANGED
@@ -24,8 +24,8 @@ const parsers = {
|
|
24
24
|
// have a test that exercises it because I'm not sure for which parser it is
|
25
25
|
// necessary, but since it's in prettier core I'm keeping it here.
|
26
26
|
/* istanbul ignore next */
|
27
|
-
|
28
|
-
mapDoc(doc, (currentDoc) =>
|
27
|
+
function replaceNewlines(doc) {
|
28
|
+
return mapDoc(doc, (currentDoc) =>
|
29
29
|
typeof currentDoc === "string" && currentDoc.includes("\n")
|
30
30
|
? concat(
|
31
31
|
currentDoc
|
@@ -34,8 +34,44 @@ const replaceNewlines = (doc) =>
|
|
34
34
|
)
|
35
35
|
: currentDoc
|
36
36
|
);
|
37
|
+
}
|
37
38
|
|
38
|
-
|
39
|
+
// Returns a number that represents the minimum amount of leading whitespace
|
40
|
+
// that is present on every line in the given string. So for example if you have
|
41
|
+
// the following heredoc:
|
42
|
+
//
|
43
|
+
// <<~HERE
|
44
|
+
// my
|
45
|
+
// content
|
46
|
+
// here
|
47
|
+
// HERE
|
48
|
+
//
|
49
|
+
// then the return value of this function would be 2. If you indented every line
|
50
|
+
// of the inner content 2 more spaces then this function would return 4.
|
51
|
+
function getCommonLeadingWhitespace(content) {
|
52
|
+
const pattern = /^\s+/;
|
53
|
+
|
54
|
+
return content
|
55
|
+
.split("\n")
|
56
|
+
.slice(0, -1)
|
57
|
+
.reduce((minimum, line) => {
|
58
|
+
const matched = pattern.exec(line);
|
59
|
+
const length = matched ? matched[0].length : 0;
|
60
|
+
|
61
|
+
return minimum === null ? length : Math.min(minimum, length);
|
62
|
+
}, null);
|
63
|
+
}
|
64
|
+
|
65
|
+
// Returns a new string with the common whitespace stripped out. Effectively it
|
66
|
+
// emulates what a squiggly heredoc does in Ruby.
|
67
|
+
function stripCommonLeadingWhitespace(content) {
|
68
|
+
const lines = content.split("\n");
|
69
|
+
const minimum = getCommonLeadingWhitespace(content);
|
70
|
+
|
71
|
+
return lines.map((line) => line.slice(minimum)).join("\n");
|
72
|
+
}
|
73
|
+
|
74
|
+
function embed(path, print, textToDoc, _opts) {
|
39
75
|
const node = path.getValue();
|
40
76
|
|
41
77
|
// Currently we only support embedded formatting on heredoc nodes
|
@@ -45,6 +81,8 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
45
81
|
|
46
82
|
// First, ensure that we don't have any interpolation
|
47
83
|
const { beging, body, ending } = node;
|
84
|
+
const isSquiggly = beging.body[2] === "~";
|
85
|
+
|
48
86
|
if (body.some((part) => part.type !== "@tstring_content")) {
|
49
87
|
return null;
|
50
88
|
}
|
@@ -56,9 +94,17 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
56
94
|
return null;
|
57
95
|
}
|
58
96
|
|
59
|
-
// Get the content as if it were a source string
|
60
|
-
|
61
|
-
|
97
|
+
// Get the content as if it were a source string.
|
98
|
+
let content = body.map((part) => part.body).join("");
|
99
|
+
|
100
|
+
// If we're using a squiggly heredoc, then we're going to manually strip off
|
101
|
+
// the leading whitespace of each line up to the minimum leading whitespace so
|
102
|
+
// that the embedded parser can handle that for us.
|
103
|
+
if (isSquiggly) {
|
104
|
+
content = stripCommonLeadingWhitespace(content);
|
105
|
+
}
|
106
|
+
|
107
|
+
// Pass that content into the embedded parser. Get back the doc node.
|
62
108
|
const formatted = concat([
|
63
109
|
literalLineNoBreak,
|
64
110
|
replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
|
@@ -66,7 +112,7 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
66
112
|
|
67
113
|
// If we're using a squiggly heredoc, then we can properly handle indentation
|
68
114
|
// ourselves.
|
69
|
-
if (
|
115
|
+
if (isSquiggly) {
|
70
116
|
return concat([
|
71
117
|
path.call(print, "beging"),
|
72
118
|
lineSuffix(
|
@@ -89,6 +135,6 @@ const embed = (path, print, textToDoc, _opts) => {
|
|
89
135
|
lineSuffix(group(concat([formatted, literalLineNoBreak, ending.trim()])))
|
90
136
|
])
|
91
137
|
);
|
92
|
-
}
|
138
|
+
}
|
93
139
|
|
94
140
|
module.exports = embed;
|
data/src/ruby/nodes/args.js
CHANGED
@@ -8,9 +8,26 @@ const {
|
|
8
8
|
softline
|
9
9
|
} = require("../../prettier");
|
10
10
|
const { getTrailingComma } = require("../../utils");
|
11
|
-
|
12
11
|
const toProc = require("../toProc");
|
13
12
|
|
13
|
+
const noTrailingComma = ["command", "command_call"];
|
14
|
+
|
15
|
+
function getArgParenTrailingComma(node) {
|
16
|
+
// If we have a block, then we don't want to add a trailing comma.
|
17
|
+
if (node.type === "args_add_block" && node.body[1]) {
|
18
|
+
return "";
|
19
|
+
}
|
20
|
+
|
21
|
+
// If we only have one argument and that first argument necessitates that we
|
22
|
+
// skip putting a comma (because it would interfere with parsing the argument)
|
23
|
+
// then we don't want to add a trailing comma.
|
24
|
+
if (node.body.length === 1 && noTrailingComma.includes(node.body[0].type)) {
|
25
|
+
return "";
|
26
|
+
}
|
27
|
+
|
28
|
+
return ifBreak(",", "");
|
29
|
+
}
|
30
|
+
|
14
31
|
function printArgParen(path, opts, print) {
|
15
32
|
const argsNode = path.getValue().body[0];
|
16
33
|
|
@@ -32,9 +49,6 @@ function printArgParen(path, opts, print) {
|
|
32
49
|
);
|
33
50
|
}
|
34
51
|
|
35
|
-
const args = path.call(print, "body", 0);
|
36
|
-
const hasBlock = argsNode.type === "args_add_block" && argsNode.body[1];
|
37
|
-
|
38
52
|
// Now here we return a doc that represents the whole grouped expression,
|
39
53
|
// including the surrouding parentheses.
|
40
54
|
return group(
|
@@ -43,8 +57,8 @@ function printArgParen(path, opts, print) {
|
|
43
57
|
indent(
|
44
58
|
concat([
|
45
59
|
softline,
|
46
|
-
join(concat([",", line]),
|
47
|
-
getTrailingComma(opts) &&
|
60
|
+
join(concat([",", line]), path.call(print, "body", 0)),
|
61
|
+
getTrailingComma(opts) && getArgParenTrailingComma(argsNode)
|
48
62
|
])
|
49
63
|
),
|
50
64
|
softline,
|
data/src/ruby/nodes/class.js
CHANGED
@@ -1,34 +1,22 @@
|
|
1
|
-
const {
|
2
|
-
|
3
|
-
group,
|
4
|
-
hardline,
|
5
|
-
ifBreak,
|
6
|
-
indent,
|
7
|
-
line
|
8
|
-
} = require("../../prettier");
|
1
|
+
const { concat, group, hardline, indent } = require("../../prettier");
|
2
|
+
const { isEmptyBodyStmt } = require("../../utils");
|
9
3
|
|
10
4
|
function printClass(path, opts, print) {
|
11
5
|
const [_constant, superclass, bodystmt] = path.getValue().body;
|
12
|
-
const stmts = bodystmt.body[0];
|
13
6
|
|
14
7
|
const parts = ["class ", path.call(print, "body", 0)];
|
15
8
|
if (superclass) {
|
16
9
|
parts.push(" < ", path.call(print, "body", 1));
|
17
10
|
}
|
18
11
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
stmts.body.length === 1 &&
|
23
|
-
stmts.body[0].type === "void_stmt" &&
|
24
|
-
!stmts.body[0].comments
|
25
|
-
) {
|
26
|
-
return group(concat([concat(parts), ifBreak(line, "; "), "end"]));
|
12
|
+
const declaration = group(concat(parts));
|
13
|
+
if (isEmptyBodyStmt(bodystmt)) {
|
14
|
+
return group(concat([declaration, hardline, "end"]));
|
27
15
|
}
|
28
16
|
|
29
17
|
return group(
|
30
18
|
concat([
|
31
|
-
|
19
|
+
declaration,
|
32
20
|
indent(concat([hardline, path.call(print, "body", 2)])),
|
33
21
|
concat([hardline, "end"])
|
34
22
|
])
|
@@ -36,16 +24,11 @@ function printClass(path, opts, print) {
|
|
36
24
|
}
|
37
25
|
|
38
26
|
function printModule(path, opts, print) {
|
27
|
+
const node = path.getValue();
|
39
28
|
const declaration = group(concat(["module ", path.call(print, "body", 0)]));
|
40
29
|
|
41
|
-
|
42
|
-
|
43
|
-
if (
|
44
|
-
stmts.body.length === 1 &&
|
45
|
-
stmts.body[0].type === "void_stmt" &&
|
46
|
-
!stmts.body[0].comments
|
47
|
-
) {
|
48
|
-
return group(concat([declaration, ifBreak(line, "; "), "end"]));
|
30
|
+
if (isEmptyBodyStmt(node.body[1])) {
|
31
|
+
return group(concat([declaration, hardline, "end"]));
|
49
32
|
}
|
50
33
|
|
51
34
|
return group(
|
@@ -58,9 +41,16 @@ function printModule(path, opts, print) {
|
|
58
41
|
}
|
59
42
|
|
60
43
|
function printSClass(path, opts, print) {
|
44
|
+
const bodystmt = path.getValue().body[1];
|
45
|
+
const declaration = concat(["class << ", path.call(print, "body", 0)]);
|
46
|
+
|
47
|
+
if (isEmptyBodyStmt(bodystmt)) {
|
48
|
+
return group(concat([declaration, hardline, "end"]));
|
49
|
+
}
|
50
|
+
|
61
51
|
return group(
|
62
52
|
concat([
|
63
|
-
|
53
|
+
declaration,
|
64
54
|
indent(concat([hardline, path.call(print, "body", 1)])),
|
65
55
|
concat([hardline, "end"])
|
66
56
|
])
|
@@ -219,7 +219,7 @@ const printConditional = (keyword) => (path, { rubyModifier }, print) => {
|
|
219
219
|
|
220
220
|
// If the body of the conditional is empty, then we explicitly have to use the
|
221
221
|
// block form.
|
222
|
-
if (isEmptyStmts(statements)
|
222
|
+
if (isEmptyStmts(statements)) {
|
223
223
|
return concat([
|
224
224
|
`${keyword} `,
|
225
225
|
align(keyword.length + 1, path.call(print, "body", 0)),
|
data/src/ruby/nodes/hashes.js
CHANGED
@@ -6,6 +6,7 @@ const {
|
|
6
6
|
join,
|
7
7
|
line
|
8
8
|
} = require("../../prettier");
|
9
|
+
|
9
10
|
const {
|
10
11
|
getTrailingComma,
|
11
12
|
printEmptyCollection,
|
@@ -89,6 +90,13 @@ function printAssocNew(path, opts, print) {
|
|
89
90
|
const parts = [path.call((keyPath) => keyPrinter(keyPath, print), "body", 0)];
|
90
91
|
const valueDoc = path.call(print, "body", 1);
|
91
92
|
|
93
|
+
// If we're printing a child hash then we want it to break along with its
|
94
|
+
// parent hash, so we don't group the parts.
|
95
|
+
if (valueNode.type === "hash") {
|
96
|
+
parts.push(" ", valueDoc);
|
97
|
+
return concat(parts);
|
98
|
+
}
|
99
|
+
|
92
100
|
if (!skipAssignIndent(valueNode) || keyNode.comments) {
|
93
101
|
parts.push(indent(concat([line, valueDoc])));
|
94
102
|
} else {
|
@@ -125,20 +133,26 @@ function printHash(path, opts, print) {
|
|
125
133
|
return printEmptyCollection(path, opts, "{", "}");
|
126
134
|
}
|
127
135
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
136
|
+
let hashDoc = concat([
|
137
|
+
"{",
|
138
|
+
indent(
|
139
|
+
concat([
|
140
|
+
line,
|
141
|
+
path.call(print, "body", 0),
|
142
|
+
getTrailingComma(opts) ? ifBreak(",", "") : ""
|
143
|
+
])
|
144
|
+
),
|
145
|
+
line,
|
146
|
+
"}"
|
147
|
+
]);
|
148
|
+
|
149
|
+
// If we're inside another hash, then we don't want to group our contents
|
150
|
+
// because we want this hash to break along with its parent hash.
|
151
|
+
if (path.getParentNode().type === "assoc_new") {
|
152
|
+
return hashDoc;
|
153
|
+
}
|
154
|
+
|
155
|
+
return group(hashDoc);
|
142
156
|
}
|
143
157
|
|
144
158
|
module.exports = {
|
data/src/ruby/nodes/loops.js
CHANGED
@@ -10,7 +10,7 @@ const {
|
|
10
10
|
softline
|
11
11
|
} = require("../../prettier");
|
12
12
|
|
13
|
-
const { containsAssignment } = require("../../utils");
|
13
|
+
const { containsAssignment, isEmptyStmts } = require("../../utils");
|
14
14
|
const inlineEnsureParens = require("../../utils/inlineEnsureParens");
|
15
15
|
|
16
16
|
function printLoop(keyword, modifier) {
|
@@ -19,17 +19,11 @@ function printLoop(keyword, modifier) {
|
|
19
19
|
|
20
20
|
// If the only statement inside this while loop is a void statement, then we
|
21
21
|
// can shorten to just displaying the predicate and then a semicolon.
|
22
|
-
if (
|
23
|
-
stmts.body.length === 1 &&
|
24
|
-
stmts.body[0].type === "void_stmt" &&
|
25
|
-
!stmts.body[0].comments
|
26
|
-
) {
|
22
|
+
if (isEmptyStmts(stmts)) {
|
27
23
|
return group(
|
28
24
|
concat([
|
29
|
-
keyword,
|
30
|
-
|
31
|
-
path.call(print, "body", 0),
|
32
|
-
ifBreak(softline, "; "),
|
25
|
+
group(concat([keyword, " ", path.call(print, "body", 0)])),
|
26
|
+
hardline,
|
33
27
|
"end"
|
34
28
|
])
|
35
29
|
);
|
data/src/ruby/nodes/methods.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
const { concat, group, hardline, indent, line } = require("../../prettier");
|
2
|
+
const { isEmptyBodyStmt } = require("../../utils");
|
2
3
|
|
3
4
|
function printMethod(offset) {
|
4
5
|
return function printMethodWithOffset(path, opts, print) {
|
5
|
-
const [_name, params,
|
6
|
+
const [_name, params, bodystmt] = path.getValue().body.slice(offset);
|
6
7
|
const declaration = ["def "];
|
7
8
|
|
8
9
|
// In this case, we're printing a method that's defined as a singleton, so
|
@@ -24,16 +25,8 @@ function printMethod(offset) {
|
|
24
25
|
parens ? ")" : ""
|
25
26
|
);
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if (
|
31
|
-
!body.body.slice(1).some((node) => node) &&
|
32
|
-
stmts.length === 1 &&
|
33
|
-
stmts[0].type === "void_stmt" &&
|
34
|
-
!stmts[0].comments
|
35
|
-
) {
|
36
|
-
return group(concat(declaration.concat(["; end"])));
|
28
|
+
if (isEmptyBodyStmt(bodystmt)) {
|
29
|
+
return group(concat(declaration.concat("; end")));
|
37
30
|
}
|
38
31
|
|
39
32
|
return group(
|
@@ -12,15 +12,13 @@ const {
|
|
12
12
|
trim
|
13
13
|
} = require("../../prettier");
|
14
14
|
|
15
|
+
const { isEmptyStmts } = require("../../utils");
|
16
|
+
|
15
17
|
function printBodyStmt(path, opts, print) {
|
16
18
|
const [stmts, rescue, elseClause, ensure] = path.getValue().body;
|
17
19
|
const parts = [];
|
18
20
|
|
19
|
-
if (
|
20
|
-
stmts.body.length > 1 ||
|
21
|
-
stmts.body[0].type != "void_stmt" ||
|
22
|
-
stmts.body[0].comments
|
23
|
-
) {
|
21
|
+
if (!isEmptyStmts(stmts)) {
|
24
22
|
parts.push(path.call(print, "body", 0));
|
25
23
|
}
|
26
24
|
|
data/src/ruby/parser.rb
CHANGED
data/src/utils.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module.exports = {
|
2
2
|
containsAssignment: require("./utils/containsAssignment"),
|
3
3
|
getTrailingComma: require("./utils/getTrailingComma"),
|
4
|
+
isEmptyBodyStmt: require("./utils/isEmptyBodyStmt"),
|
4
5
|
isEmptyStmts: require("./utils/isEmptyStmts"),
|
5
6
|
hasAncestor: require("./utils/hasAncestor"),
|
6
7
|
literal: require("./utils/literal"),
|
data/src/utils/isEmptyStmts.js
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
function isEmptyStmts(node) {
|
2
|
+
return (
|
3
|
+
node &&
|
4
|
+
node.type === "stmts" &&
|
5
|
+
node.body.length === 1 &&
|
6
|
+
node.body[0].type === "void_stmt" &&
|
7
|
+
!node.body[0].comments
|
8
|
+
);
|
9
|
+
}
|
6
10
|
|
7
11
|
module.exports = isEmptyStmts;
|