prettier 0.22.0 → 1.1.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 +115 -8
- data/CONTRIBUTING.md +1 -1
- data/README.md +17 -14
- data/node_modules/prettier/index.js +54 -54
- data/package.json +10 -5
- data/src/embed.js +11 -7
- data/src/nodes/args.js +59 -79
- data/src/nodes/arrays.js +38 -50
- data/src/nodes/assign.js +2 -2
- data/src/nodes/calls.js +132 -50
- data/src/nodes/case.js +11 -7
- data/src/nodes/conditionals.js +4 -4
- data/src/nodes/hashes.js +59 -51
- data/src/nodes/heredocs.js +2 -2
- data/src/nodes/hooks.js +5 -3
- data/src/nodes/ints.js +0 -6
- data/src/nodes/lambdas.js +6 -22
- data/src/nodes/loops.js +67 -66
- data/src/nodes/methods.js +2 -0
- data/src/nodes/operators.js +24 -13
- data/src/nodes/params.js +15 -3
- data/src/nodes/regexp.js +38 -9
- data/src/nodes/rescue.js +2 -2
- data/src/nodes/statements.js +65 -45
- data/src/nodes/strings.js +12 -11
- data/src/parser.js +27 -12
- data/src/parser.rb +190 -54
- data/src/printer.js +16 -1
- data/src/ruby.js +15 -21
- data/src/toProc.js +2 -2
- data/src/utils.js +15 -3
- data/src/utils/printEmptyCollection.js +42 -0
- metadata +5 -46
data/src/nodes/methods.js
CHANGED
@@ -28,7 +28,9 @@ function printMethod(offset) {
|
|
28
28
|
|
29
29
|
// If the body is empty, we can replace with a ;
|
30
30
|
const stmts = body.body[0].body;
|
31
|
+
|
31
32
|
if (
|
33
|
+
!body.body.slice(1).some((node) => node) &&
|
32
34
|
stmts.length === 1 &&
|
33
35
|
stmts[0].type === "void_stmt" &&
|
34
36
|
!stmts[0].comments
|
data/src/nodes/operators.js
CHANGED
@@ -1,23 +1,34 @@
|
|
1
1
|
const { concat, group, indent, line, softline } = require("../prettier");
|
2
|
+
const { noIndent } = require("../utils");
|
2
3
|
|
3
4
|
function printBinary(path, opts, print) {
|
4
|
-
const operator = path.getValue().body
|
5
|
-
const
|
5
|
+
const [_leftNode, operator, rightNode] = path.getValue().body;
|
6
|
+
const space = operator === "**" ? "" : " ";
|
7
|
+
|
8
|
+
if (noIndent.includes(rightNode.type)) {
|
9
|
+
return group(
|
10
|
+
concat([
|
11
|
+
group(path.call(print, "body", 0)),
|
12
|
+
space,
|
13
|
+
operator,
|
14
|
+
space,
|
15
|
+
group(path.call(print, "body", 2))
|
16
|
+
])
|
17
|
+
);
|
18
|
+
}
|
6
19
|
|
7
20
|
return group(
|
8
21
|
concat([
|
9
22
|
group(path.call(print, "body", 0)),
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
)
|
20
|
-
])
|
23
|
+
space,
|
24
|
+
group(
|
25
|
+
indent(
|
26
|
+
concat([
|
27
|
+
operator,
|
28
|
+
space === "" ? softline : line,
|
29
|
+
path.call(print, "body", 2)
|
30
|
+
])
|
31
|
+
)
|
21
32
|
)
|
22
33
|
])
|
23
34
|
);
|
data/src/nodes/params.js
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
const { concat, group, join, line } = require("../prettier");
|
1
|
+
const { concat, group, join, indent, line, softline } = require("../prettier");
|
2
2
|
const { literal } = require("../utils");
|
3
3
|
|
4
4
|
function printRestParam(symbol) {
|
@@ -64,6 +64,8 @@ function printParams(path, opts, print) {
|
|
64
64
|
parts.push(path.call(print, "body", 6));
|
65
65
|
}
|
66
66
|
|
67
|
+
const contents = [join(concat([",", line]), parts)];
|
68
|
+
|
67
69
|
// You can put an extra comma at the end of block args between pipes to
|
68
70
|
// change what it does. Below is the difference:
|
69
71
|
//
|
@@ -73,9 +75,19 @@ function printParams(path, opts, print) {
|
|
73
75
|
// In ruby 2.5, the excessed comma is indicated by having a 0 in the rest
|
74
76
|
// param position. In ruby 2.6+ it's indicated by having an "excessed_comma"
|
75
77
|
// node in the rest position. Seems odd, but it's true.
|
76
|
-
|
78
|
+
if (rest === 0 || (rest && rest.type === "excessed_comma")) {
|
79
|
+
contents.push(",");
|
80
|
+
}
|
81
|
+
|
82
|
+
// If the parent node is a paren then we skipped printing the parentheses so
|
83
|
+
// that we could handle them here and get nicer formatting.
|
84
|
+
if (["lambda", "paren"].includes(path.getParentNode().type)) {
|
85
|
+
return group(
|
86
|
+
concat(["(", indent(concat([softline].concat(contents))), softline, ")"])
|
87
|
+
);
|
88
|
+
}
|
77
89
|
|
78
|
-
return group(concat(
|
90
|
+
return group(concat(contents));
|
79
91
|
}
|
80
92
|
|
81
93
|
module.exports = {
|
data/src/nodes/regexp.js
CHANGED
@@ -1,4 +1,27 @@
|
|
1
1
|
const { concat } = require("../prettier");
|
2
|
+
const { hasAncestor } = require("../utils");
|
3
|
+
|
4
|
+
function hasContent(node, pattern) {
|
5
|
+
return node.body.some(
|
6
|
+
(child) => child.type === "@tstring_content" && pattern.test(child.body)
|
7
|
+
);
|
8
|
+
}
|
9
|
+
|
10
|
+
// If the first part of this regex is plain string content, we have a space
|
11
|
+
// or an =, and we're contained within a command or command_call node, then we
|
12
|
+
// want to use braces because otherwise we could end up with an ambiguous
|
13
|
+
// operator, e.g. foo / bar/ or foo /=bar/
|
14
|
+
function forwardSlashIsAmbiguous(path) {
|
15
|
+
const node = path.getValue();
|
16
|
+
const firstChildNode = node.body[0];
|
17
|
+
|
18
|
+
return (
|
19
|
+
firstChildNode &&
|
20
|
+
firstChildNode.type === "@tstring_content" &&
|
21
|
+
[" ", "="].includes(firstChildNode.body[0]) &&
|
22
|
+
hasAncestor(path, ["command", "command_call"])
|
23
|
+
);
|
24
|
+
}
|
2
25
|
|
3
26
|
// This function is responsible for printing out regexp_literal nodes. They can
|
4
27
|
// either use the special %r literal syntax or they can use forward slashes. At
|
@@ -8,18 +31,24 @@ const { concat } = require("../prettier");
|
|
8
31
|
// We favor the use of forward slashes unless the regex contains a forward slash
|
9
32
|
// itself. In that case we switch over to using %r with braces.
|
10
33
|
function printRegexpLiteral(path, opts, print) {
|
11
|
-
const
|
12
|
-
const
|
34
|
+
const node = path.getValue();
|
35
|
+
const docs = path.map(print, "body");
|
13
36
|
|
14
|
-
|
15
|
-
|
16
|
-
);
|
37
|
+
// We should use braces if using a forward slash would be ambiguous in the
|
38
|
+
// current context or if there's a forward slash in the content of the regexp.
|
39
|
+
const useBraces = forwardSlashIsAmbiguous(path) || hasContent(node, /\//);
|
17
40
|
|
18
|
-
|
19
|
-
|
20
|
-
|
41
|
+
// If we should be using braces but we have braces in the body of the regexp,
|
42
|
+
// then we're just going to resort to using whatever the original content was.
|
43
|
+
if (useBraces && hasContent(node, /[{}]/)) {
|
44
|
+
return concat([node.beging].concat(docs).concat(node.ending));
|
45
|
+
}
|
21
46
|
|
22
|
-
return concat(
|
47
|
+
return concat(
|
48
|
+
[useBraces ? "%r{" : "/"]
|
49
|
+
.concat(docs)
|
50
|
+
.concat(useBraces ? "}" : "/", node.ending.slice(1))
|
51
|
+
);
|
23
52
|
}
|
24
53
|
|
25
54
|
module.exports = {
|
data/src/nodes/rescue.js
CHANGED
@@ -20,8 +20,8 @@ function printBegin(path, opts, print) {
|
|
20
20
|
|
21
21
|
function printEnsure(path, opts, print) {
|
22
22
|
return concat([
|
23
|
-
"
|
24
|
-
indent(concat([hardline,
|
23
|
+
path.call(print, "body", 0),
|
24
|
+
indent(concat([hardline, path.call(print, "body", 1)]))
|
25
25
|
]);
|
26
26
|
}
|
27
27
|
|
data/src/nodes/statements.js
CHANGED
@@ -12,58 +12,78 @@ const {
|
|
12
12
|
trim
|
13
13
|
} = require("../prettier");
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
15
|
+
function printBodyStmt(path, opts, print) {
|
16
|
+
const [stmts, rescue, elseClause, ensure] = path.getValue().body;
|
17
|
+
const parts = [];
|
18
|
+
|
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
|
+
}
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
if (rescue) {
|
28
|
+
parts.push(dedent(concat([hardline, path.call(print, "body", 1)])));
|
29
|
+
}
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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);
|
34
37
|
|
35
|
-
|
36
|
-
|
38
|
+
parts.push(concat([dedent(concat([hardline, "else"])), hardline, stmts]));
|
39
|
+
}
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
if (ensure) {
|
42
|
+
parts.push(dedent(concat([hardline, path.call(print, "body", 3)])));
|
43
|
+
}
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
paren: (path, opts, print) => {
|
45
|
-
if (!path.getValue().body[0]) {
|
46
|
-
return "()";
|
47
|
-
}
|
45
|
+
return group(concat(parts));
|
46
|
+
}
|
48
47
|
|
49
|
-
|
48
|
+
const argNodeTypes = ["args", "args_add_star", "args_add_block"];
|
50
49
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
)
|
56
|
-
|
57
|
-
|
50
|
+
function printParen(path, opts, print) {
|
51
|
+
const contentNode = path.getValue().body[0];
|
52
|
+
|
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
|
+
}
|
58
64
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
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) => {
|
@@ -78,8 +98,8 @@ module.exports = {
|
|
78
98
|
stmts[0].comments
|
79
99
|
) {
|
80
100
|
const comments = path.map(
|
81
|
-
(commentPath
|
82
|
-
|
101
|
+
(commentPath) => {
|
102
|
+
commentPath.getValue().printed = true;
|
83
103
|
return opts.printer.printComment(commentPath);
|
84
104
|
},
|
85
105
|
"body",
|
data/src/nodes/strings.js
CHANGED
@@ -32,7 +32,7 @@ function isSingleQuotable(node) {
|
|
32
32
|
const quotePattern = new RegExp("\\\\([\\s\\S])|(['\"])", "g");
|
33
33
|
|
34
34
|
function normalizeQuotes(content, enclosingQuote, originalQuote) {
|
35
|
-
const replaceOther =
|
35
|
+
const replaceOther = originalQuote === '"';
|
36
36
|
const otherQuote = enclosingQuote === '"' ? "'" : '"';
|
37
37
|
|
38
38
|
// Escape and unescape single and double quotes as needed to be able to
|
@@ -66,7 +66,7 @@ function getClosingQuote(quote) {
|
|
66
66
|
return quote;
|
67
67
|
}
|
68
68
|
|
69
|
-
const boundary = /%
|
69
|
+
const boundary = /%[Qq]?(.)/.exec(quote)[1];
|
70
70
|
if (boundary in quotePairs) {
|
71
71
|
return quotePairs[boundary];
|
72
72
|
}
|
@@ -77,14 +77,14 @@ function getClosingQuote(quote) {
|
|
77
77
|
// Prints a @CHAR node. @CHAR nodes are special character strings that usually
|
78
78
|
// are strings of length 1. If they're any longer than we'll try to apply the
|
79
79
|
// correct quotes.
|
80
|
-
function printChar(path, {
|
80
|
+
function printChar(path, { rubySingleQuote }, _print) {
|
81
81
|
const { body } = path.getValue();
|
82
82
|
|
83
83
|
if (body.length !== 2) {
|
84
84
|
return body;
|
85
85
|
}
|
86
86
|
|
87
|
-
const quote =
|
87
|
+
const quote = rubySingleQuote ? "'" : '"';
|
88
88
|
return concat([quote, body.slice(1), quote]);
|
89
89
|
}
|
90
90
|
|
@@ -107,13 +107,13 @@ function printStringDVar(path, opts, print) {
|
|
107
107
|
// wishes of the user with regards to single versus double quotes, but if the
|
108
108
|
// string contains any escape expressions then it will just keep the original
|
109
109
|
// quotes.
|
110
|
-
function printStringLiteral(path, {
|
110
|
+
function printStringLiteral(path, { rubySingleQuote }, print) {
|
111
111
|
const node = path.getValue();
|
112
112
|
|
113
113
|
// If the string is empty, it will not have any parts, so just print out the
|
114
114
|
// quotes corresponding to the config
|
115
115
|
if (node.body.length === 0) {
|
116
|
-
return
|
116
|
+
return rubySingleQuote ? "''" : '""';
|
117
117
|
}
|
118
118
|
|
119
119
|
// Determine the quote that should enclose the new string
|
@@ -121,7 +121,7 @@ function printStringLiteral(path, { preferSingleQuotes }, print) {
|
|
121
121
|
if (isQuoteLocked(node)) {
|
122
122
|
quote = node.quote;
|
123
123
|
} else {
|
124
|
-
quote =
|
124
|
+
quote = rubySingleQuote && isSingleQuotable(node) ? "'" : '"';
|
125
125
|
}
|
126
126
|
|
127
127
|
const parts = node.body.map((part, index) => {
|
@@ -167,10 +167,11 @@ module.exports = {
|
|
167
167
|
string_embexpr: (path, opts, print) => {
|
168
168
|
const parts = path.call(print, "body", 0);
|
169
169
|
|
170
|
-
// If the interpolated expression is inside of
|
171
|
-
// that gets sent to the command line) then we don't want
|
172
|
-
// indent, as this can lead to some very odd looking
|
173
|
-
|
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)) {
|
174
175
|
return concat(["#{", parts, "}"]);
|
175
176
|
}
|
176
177
|
|
data/src/parser.js
CHANGED
@@ -3,18 +3,33 @@ const path = require("path");
|
|
3
3
|
|
4
4
|
// In order to properly parse ruby code, we need to tell the ruby process to
|
5
5
|
// parse using UTF-8. Unfortunately, the way that you accomplish this looks
|
6
|
-
// differently depending on your platform.
|
7
|
-
|
8
|
-
|
9
|
-
const
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
6
|
+
// differently depending on your platform.
|
7
|
+
const LANG = (() => {
|
8
|
+
const { env, platform } = process;
|
9
|
+
const envValue = env.LC_ALL || env.LC_CTYPE || env.LANG;
|
10
|
+
|
11
|
+
// If an env var is set for the locale that already includes UTF-8 in the
|
12
|
+
// name, then assume we can go with that.
|
13
|
+
if (envValue && envValue.includes("UTF-8")) {
|
14
|
+
return envValue;
|
15
|
+
}
|
16
|
+
|
17
|
+
// Otherwise, we're going to guess which encoding to use based on the system.
|
18
|
+
// This is probably not the best approach in the world, as you could be on
|
19
|
+
// linux and not have C.UTF-8, but in that case you're probably passing an env
|
20
|
+
// var for it. This object below represents all of the possible values of
|
21
|
+
// process.platform per:
|
22
|
+
// https://nodejs.org/api/process.html#process_process_platform
|
23
|
+
return {
|
24
|
+
aix: "C.UTF-8",
|
25
|
+
darwin: "en_US.UTF-8",
|
26
|
+
freebsd: "C.UTF-8",
|
27
|
+
linux: "C.UTF-8",
|
28
|
+
openbsd: "C.UTF-8",
|
29
|
+
sunos: "C.UTF-8",
|
30
|
+
win32: ".UTF-8"
|
31
|
+
}[platform];
|
32
|
+
})();
|
18
33
|
|
19
34
|
// This function is responsible for taking an input string of text and returning
|
20
35
|
// to prettier a JavaScript object that is the equivalent AST that represents
|
data/src/parser.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# We implement our own version checking here instead of using Gem::Version so
|
4
4
|
# that we can use the --disable-gems flag.
|
5
|
-
RUBY_MAJOR, RUBY_MINOR, * = RUBY_VERSION.split('.').map(&:to_i)
|
5
|
+
RUBY_MAJOR, RUBY_MINOR, RUBY_PATCH, * = RUBY_VERSION.split('.').map(&:to_i)
|
6
6
|
|
7
7
|
if (RUBY_MAJOR < 2) || ((RUBY_MAJOR == 2) && (RUBY_MINOR < 5))
|
8
8
|
warn(
|
@@ -115,15 +115,14 @@ class Prettier::Parser < Ripper
|
|
115
115
|
# This will break everything, so we need to force the encoding back into
|
116
116
|
# UTF-8 so that the JSON library won't break.
|
117
117
|
def on_comment(value)
|
118
|
-
@comments <<
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
}
|
118
|
+
@comments << {
|
119
|
+
type: :@comment,
|
120
|
+
value: value[1..-1].chomp.force_encoding('UTF-8'),
|
121
|
+
start: lineno,
|
122
|
+
end: lineno,
|
123
|
+
char_start: char_pos,
|
124
|
+
char_end: char_pos + value.length - 1
|
125
|
+
}
|
127
126
|
end
|
128
127
|
|
129
128
|
# ignored_nl is a special kind of scanner event that passes nil as the value,
|
@@ -187,11 +186,14 @@ class Prettier::Parser < Ripper
|
|
187
186
|
beging = find_scanner_event(:@lbrace)
|
188
187
|
ending = find_scanner_event(:@rbrace)
|
189
188
|
|
190
|
-
stmts.bind(
|
189
|
+
stmts.bind(
|
190
|
+
find_next_statement_start(beging[:char_end]),
|
191
|
+
ending[:char_start]
|
192
|
+
)
|
191
193
|
|
192
194
|
find_scanner_event(:@kw, 'BEGIN').merge!(
|
193
195
|
type: :BEGIN,
|
194
|
-
body: [stmts],
|
196
|
+
body: [beging, stmts],
|
195
197
|
end: ending[:end],
|
196
198
|
char_end: ending[:char_end]
|
197
199
|
)
|
@@ -211,10 +213,16 @@ class Prettier::Parser < Ripper
|
|
211
213
|
beging = find_scanner_event(:@lbrace)
|
212
214
|
ending = find_scanner_event(:@rbrace)
|
213
215
|
|
214
|
-
stmts.bind(
|
216
|
+
stmts.bind(
|
217
|
+
find_next_statement_start(beging[:char_end]),
|
218
|
+
ending[:char_start]
|
219
|
+
)
|
215
220
|
|
216
221
|
find_scanner_event(:@kw, 'END').merge!(
|
217
|
-
type: :END,
|
222
|
+
type: :END,
|
223
|
+
body: [beging, stmts],
|
224
|
+
end: ending[:end],
|
225
|
+
char_end: ending[:char_end]
|
218
226
|
)
|
219
227
|
end
|
220
228
|
|
@@ -306,7 +314,9 @@ class Prettier::Parser < Ripper
|
|
306
314
|
arg.merge(type: :args, body: [arg])
|
307
315
|
else
|
308
316
|
args.merge!(
|
309
|
-
body: args[:body] << arg,
|
317
|
+
body: args[:body] << arg,
|
318
|
+
end: arg[:end],
|
319
|
+
char_end: arg[:char_end]
|
310
320
|
)
|
311
321
|
end
|
312
322
|
end
|
@@ -352,7 +362,12 @@ class Prettier::Parser < Ripper
|
|
352
362
|
# method inside a set of parentheses.
|
353
363
|
def on_arg_paren(args)
|
354
364
|
beging = find_scanner_event(:@lparen)
|
355
|
-
|
365
|
+
rparen = find_scanner_event(:@rparen)
|
366
|
+
|
367
|
+
# If the arguments exceed the ending of the parentheses, then we know we
|
368
|
+
# have a heredoc in the arguments, and we need to use the bounds of the
|
369
|
+
# arguments to determine how large the arg_paren is.
|
370
|
+
ending = (args && args[:end] > rparen[:end]) ? args : rparen
|
356
371
|
|
357
372
|
{
|
358
373
|
type: :arg_paren,
|
@@ -553,13 +568,15 @@ class Prettier::Parser < Ripper
|
|
553
568
|
|
554
569
|
# Here we're going to determine the bounds for the stmts
|
555
570
|
consequent = parts[1..-1].compact.first
|
556
|
-
self[:body][0].bind(
|
557
|
-
|
571
|
+
self[:body][0].bind(
|
572
|
+
char_start,
|
573
|
+
consequent ? consequent[:char_start] : char_end
|
574
|
+
)
|
558
575
|
|
559
576
|
# Next we're going to determine the rescue clause if there is one
|
560
577
|
if parts[1]
|
561
578
|
consequent = parts[2..-1].compact.first
|
562
|
-
self[:body][1].
|
579
|
+
self[:body][1].bind_end(consequent ? consequent[:char_start] : char_end)
|
563
580
|
end
|
564
581
|
end
|
565
582
|
end
|
@@ -601,7 +618,16 @@ class Prettier::Parser < Ripper
|
|
601
618
|
# accepts as an argument an args or args_add_block event that contains all
|
602
619
|
# of the arguments being passed to the break.
|
603
620
|
def on_break(args_add_block)
|
604
|
-
find_scanner_event(:@kw, 'break')
|
621
|
+
beging = find_scanner_event(:@kw, 'break')
|
622
|
+
|
623
|
+
# You can hit this if you are passing no arguments to break but it has a
|
624
|
+
# comment right after it. In that case we can just use the location
|
625
|
+
# information straight from the keyword.
|
626
|
+
if args_add_block[:type] == :args
|
627
|
+
return beging.merge!(type: :break, body: [args_add_block])
|
628
|
+
end
|
629
|
+
|
630
|
+
beging.merge!(
|
605
631
|
type: :break,
|
606
632
|
body: [args_add_block],
|
607
633
|
end: args_add_block[:end],
|
@@ -621,6 +647,10 @@ class Prettier::Parser < Ripper
|
|
621
647
|
# foo.(1, 2, 3)
|
622
648
|
#
|
623
649
|
def on_call(receiver, oper, sending)
|
650
|
+
# Make sure we take the operator out of the scanner events so that it
|
651
|
+
# doesn't get confused for a unary operator later.
|
652
|
+
scanner_events.delete(oper)
|
653
|
+
|
624
654
|
ending = sending
|
625
655
|
|
626
656
|
if sending == :call
|
@@ -653,6 +683,27 @@ class Prettier::Parser < Ripper
|
|
653
683
|
)
|
654
684
|
end
|
655
685
|
|
686
|
+
# Finds the next position in the source string that begins a statement. This
|
687
|
+
# is used to bind statements lists and make sure they don't include a
|
688
|
+
# preceding comment. For example, we want the following comment to be attached
|
689
|
+
# to the class node and not the statement node:
|
690
|
+
#
|
691
|
+
# class Foo # :nodoc:
|
692
|
+
# ...
|
693
|
+
# end
|
694
|
+
#
|
695
|
+
# By finding the next non-space character, we can make sure that the bounds of
|
696
|
+
# the statement list are correct.
|
697
|
+
def find_next_statement_start(position)
|
698
|
+
remaining = source[position..-1]
|
699
|
+
|
700
|
+
if remaining.sub(/\A +/, '')[0] == '#'
|
701
|
+
return position + remaining.index("\n")
|
702
|
+
end
|
703
|
+
|
704
|
+
position
|
705
|
+
end
|
706
|
+
|
656
707
|
# class is a parser event that represents defining a class. It accepts as
|
657
708
|
# arguments the name of the class, the optional name of the superclass,
|
658
709
|
# and the bodystmt event that represents the statements evaluated within
|
@@ -661,7 +712,10 @@ class Prettier::Parser < Ripper
|
|
661
712
|
beging = find_scanner_event(:@kw, 'class')
|
662
713
|
ending = find_scanner_event(:@kw, 'end')
|
663
714
|
|
664
|
-
bodystmt.bind(
|
715
|
+
bodystmt.bind(
|
716
|
+
find_next_statement_start((superclass || const)[:char_end]),
|
717
|
+
ending[:char_start]
|
718
|
+
)
|
665
719
|
|
666
720
|
{
|
667
721
|
type: :class,
|
@@ -761,6 +815,11 @@ class Prettier::Parser < Ripper
|
|
761
815
|
# └> ident
|
762
816
|
#
|
763
817
|
def on_def(ident, params, bodystmt)
|
818
|
+
# Make sure to delete this scanner event in case you're defining something
|
819
|
+
# like def class which would lead to this being a kw and causing all kinds
|
820
|
+
# of trouble
|
821
|
+
scanner_events.delete(ident)
|
822
|
+
|
764
823
|
if params[:type] == :params && !params[:body].any?
|
765
824
|
location = ident[:char_end]
|
766
825
|
params.merge!(char_start: location, char_end: location)
|
@@ -769,7 +828,10 @@ class Prettier::Parser < Ripper
|
|
769
828
|
beging = find_scanner_event(:@kw, 'def')
|
770
829
|
ending = find_scanner_event(:@kw, 'end')
|
771
830
|
|
772
|
-
bodystmt.bind(
|
831
|
+
bodystmt.bind(
|
832
|
+
find_next_statement_start(params[:char_end]),
|
833
|
+
ending[:char_start]
|
834
|
+
)
|
773
835
|
|
774
836
|
{
|
775
837
|
type: :def,
|
@@ -796,6 +858,11 @@ class Prettier::Parser < Ripper
|
|
796
858
|
# └> target
|
797
859
|
#
|
798
860
|
def on_defs(target, oper, ident, params, bodystmt)
|
861
|
+
# Make sure to delete this scanner event in case you're defining something
|
862
|
+
# like def class which would lead to this being a kw and causing all kinds
|
863
|
+
# of trouble
|
864
|
+
scanner_events.delete(ident)
|
865
|
+
|
799
866
|
if params[:type] == :params && !params[:body].any?
|
800
867
|
location = ident[:char_end]
|
801
868
|
params.merge!(char_start: location, char_end: location)
|
@@ -804,7 +871,10 @@ class Prettier::Parser < Ripper
|
|
804
871
|
beging = find_scanner_event(:@kw, 'def')
|
805
872
|
ending = find_scanner_event(:@kw, 'end')
|
806
873
|
|
807
|
-
bodystmt.bind(
|
874
|
+
bodystmt.bind(
|
875
|
+
find_next_statement_start(params[:char_end]),
|
876
|
+
ending[:char_start]
|
877
|
+
)
|
808
878
|
|
809
879
|
{
|
810
880
|
type: :defs,
|
@@ -998,7 +1068,10 @@ class Prettier::Parser < Ripper
|
|
998
1068
|
# event, so here we'll initialize the current embdoc.
|
999
1069
|
def on_embdoc_beg(value)
|
1000
1070
|
@embdoc = {
|
1001
|
-
type: :@embdoc,
|
1071
|
+
type: :@embdoc,
|
1072
|
+
value: value,
|
1073
|
+
start: lineno,
|
1074
|
+
char_start: char_pos
|
1002
1075
|
}
|
1003
1076
|
end
|
1004
1077
|
|
@@ -1029,13 +1102,23 @@ class Prettier::Parser < Ripper
|
|
1029
1102
|
# and its subsequent statements.
|
1030
1103
|
def on_ensure(stmts)
|
1031
1104
|
beging = find_scanner_event(:@kw, 'ensure')
|
1032
|
-
ending = find_scanner_event(:@kw, 'end')
|
1033
1105
|
|
1034
|
-
|
1106
|
+
# Specifically not using find_scanner_event here because we don't want to
|
1107
|
+
# consume the :@end event, because that would break def..ensure..end chains.
|
1108
|
+
index =
|
1109
|
+
scanner_events.rindex do |scanner_event|
|
1110
|
+
scanner_event[:type] == :@kw && scanner_event[:body] == 'end'
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
ending = scanner_events[index]
|
1114
|
+
stmts.bind(
|
1115
|
+
find_next_statement_start(beging[:char_end]),
|
1116
|
+
ending[:char_start]
|
1117
|
+
)
|
1035
1118
|
|
1036
1119
|
{
|
1037
1120
|
type: :ensure,
|
1038
|
-
body: [stmts],
|
1121
|
+
body: [beging, stmts],
|
1039
1122
|
start: beging[:start],
|
1040
1123
|
char_start: beging[:char_start],
|
1041
1124
|
end: ending[:end],
|
@@ -1105,7 +1188,8 @@ class Prettier::Parser < Ripper
|
|
1105
1188
|
# Here we're going to expand out the location information for the assocs
|
1106
1189
|
# node so that it can grab up any remaining comments inside the hash.
|
1107
1190
|
assoclist_from_args.merge!(
|
1108
|
-
char_start: beging[:char_end],
|
1191
|
+
char_start: beging[:char_end],
|
1192
|
+
char_end: ending[:char_start]
|
1109
1193
|
)
|
1110
1194
|
end
|
1111
1195
|
|
@@ -1126,21 +1210,28 @@ class Prettier::Parser < Ripper
|
|
1126
1210
|
# prettier parser, we'll later attempt to print it using that parser and
|
1127
1211
|
# printer through our embed function.
|
1128
1212
|
def on_heredoc_beg(beging)
|
1129
|
-
{
|
1130
|
-
type: :heredoc,
|
1131
|
-
beging: beging,
|
1213
|
+
location = {
|
1132
1214
|
start: lineno,
|
1133
1215
|
end: lineno,
|
1134
|
-
char_start: char_pos
|
1135
|
-
char_end: char_pos
|
1136
|
-
}
|
1216
|
+
char_start: char_pos,
|
1217
|
+
char_end: char_pos + beging.length + 1
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
# Here we're going to artificially create an extra node type so that if
|
1221
|
+
# there are comments after the declaration of a heredoc, they get printed.
|
1222
|
+
location
|
1223
|
+
.merge(
|
1224
|
+
type: :heredoc,
|
1225
|
+
beging: location.merge(type: :@heredoc_beg, body: beging)
|
1226
|
+
)
|
1227
|
+
.tap { |node| @heredocs << node }
|
1137
1228
|
end
|
1138
1229
|
|
1139
1230
|
# This is a parser event that occurs when you're using a heredoc with a
|
1140
1231
|
# tilde. These are considered `heredoc_dedent` nodes, whereas the hyphen
|
1141
1232
|
# heredocs show up as string literals.
|
1142
1233
|
def on_heredoc_dedent(string, _width)
|
1143
|
-
@heredocs[-1].merge!(string
|
1234
|
+
@heredocs[-1].merge!(body: string[:body])
|
1144
1235
|
end
|
1145
1236
|
|
1146
1237
|
# This is a scanner event that represents the end of the heredoc.
|
@@ -1306,6 +1397,14 @@ class Prettier::Parser < Ripper
|
|
1306
1397
|
# arguments and parentheses. It accepts as arguments the method being called
|
1307
1398
|
# and the arg_paren event that contains the arguments to the method.
|
1308
1399
|
def on_method_add_arg(fcall, arg_paren)
|
1400
|
+
# You can hit this if you are passing no arguments to a method that ends in
|
1401
|
+
# a question mark. Because it knows it has to be a method and not a local
|
1402
|
+
# variable. In that case we can just use the location information straight
|
1403
|
+
# from the fcall.
|
1404
|
+
if arg_paren[:type] == :args
|
1405
|
+
return fcall.merge(type: :method_add_arg, body: [fcall, arg_paren])
|
1406
|
+
end
|
1407
|
+
|
1309
1408
|
{
|
1310
1409
|
type: :method_add_arg,
|
1311
1410
|
body: [fcall, arg_paren],
|
@@ -1352,7 +1451,9 @@ class Prettier::Parser < Ripper
|
|
1352
1451
|
part.merge(type: :mlhs, body: [part])
|
1353
1452
|
else
|
1354
1453
|
mlhs.merge!(
|
1355
|
-
body: mlhs[:body] << part,
|
1454
|
+
body: mlhs[:body] << part,
|
1455
|
+
end: part[:end],
|
1456
|
+
char_end: part[:char_end]
|
1356
1457
|
)
|
1357
1458
|
end
|
1358
1459
|
end
|
@@ -1418,7 +1519,10 @@ class Prettier::Parser < Ripper
|
|
1418
1519
|
beging = find_scanner_event(:@kw, 'module')
|
1419
1520
|
ending = find_scanner_event(:@kw, 'end')
|
1420
1521
|
|
1421
|
-
bodystmt.bind(
|
1522
|
+
bodystmt.bind(
|
1523
|
+
find_next_statement_start(const[:char_end]),
|
1524
|
+
ending[:char_start]
|
1525
|
+
)
|
1422
1526
|
|
1423
1527
|
{
|
1424
1528
|
type: :module,
|
@@ -1452,7 +1556,9 @@ class Prettier::Parser < Ripper
|
|
1452
1556
|
part.merge(type: :mrhs, body: [part])
|
1453
1557
|
else
|
1454
1558
|
mrhs.merge!(
|
1455
|
-
body: mrhs[:body] << part,
|
1559
|
+
body: mrhs[:body] << part,
|
1560
|
+
end: part[:end],
|
1561
|
+
char_end: part[:char_end]
|
1456
1562
|
)
|
1457
1563
|
end
|
1458
1564
|
end
|
@@ -1553,7 +1659,10 @@ class Prettier::Parser < Ripper
|
|
1553
1659
|
# some found at the end of the source string.
|
1554
1660
|
def on_program(stmts)
|
1555
1661
|
range = {
|
1556
|
-
start: 1,
|
1662
|
+
start: 1,
|
1663
|
+
end: lines.length,
|
1664
|
+
char_start: 0,
|
1665
|
+
char_end: source.length
|
1557
1666
|
}
|
1558
1667
|
|
1559
1668
|
stmts[:body] << @__end__ if @__end__
|
@@ -1610,7 +1719,8 @@ class Prettier::Parser < Ripper
|
|
1610
1719
|
# expression literal, like /foo/. It can be followed by any number of
|
1611
1720
|
# regexp_add events, which we'll append onto an array body.
|
1612
1721
|
def on_regexp_new
|
1613
|
-
find_scanner_event(:@regexp_beg)
|
1722
|
+
beging = find_scanner_event(:@regexp_beg)
|
1723
|
+
beging.merge!(type: :regexp, body: [], beging: beging[:body])
|
1614
1724
|
end
|
1615
1725
|
|
1616
1726
|
# regexp_add is a parser event that represents a piece of a regular
|
@@ -1643,17 +1753,17 @@ class Prettier::Parser < Ripper
|
|
1643
1753
|
# determine its ending. Therefore it relies on its parent bodystmt node to
|
1644
1754
|
# report its ending to it.
|
1645
1755
|
class Rescue < SimpleDelegator
|
1646
|
-
def
|
1756
|
+
def bind_end(char_end)
|
1647
1757
|
merge!(char_end: char_end)
|
1648
1758
|
|
1649
1759
|
stmts = self[:body][2]
|
1650
1760
|
consequent = self[:body][3]
|
1651
1761
|
|
1652
1762
|
if consequent
|
1653
|
-
consequent.
|
1654
|
-
stmts.
|
1763
|
+
consequent.bind_end(char_end)
|
1764
|
+
stmts.bind_end(consequent[:char_start])
|
1655
1765
|
else
|
1656
|
-
stmts.
|
1766
|
+
stmts.bind_end(char_end)
|
1657
1767
|
end
|
1658
1768
|
end
|
1659
1769
|
end
|
@@ -1663,10 +1773,10 @@ class Prettier::Parser < Ripper
|
|
1663
1773
|
def on_rescue(exceptions, variable, stmts, consequent)
|
1664
1774
|
beging = find_scanner_event(:@kw, 'rescue')
|
1665
1775
|
|
1666
|
-
|
1667
|
-
|
1668
|
-
|
1669
|
-
)
|
1776
|
+
last_exception = exceptions.is_a?(Array) ? exceptions[-1] : exceptions
|
1777
|
+
last_node = variable || last_exception || beging
|
1778
|
+
|
1779
|
+
stmts.bind(find_next_statement_start(last_node[:char_end]), char_pos)
|
1670
1780
|
|
1671
1781
|
Rescue.new(
|
1672
1782
|
beging.merge!(
|
@@ -1750,7 +1860,10 @@ class Prettier::Parser < Ripper
|
|
1750
1860
|
beging = find_scanner_event(:@kw, 'class')
|
1751
1861
|
ending = find_scanner_event(:@kw, 'end')
|
1752
1862
|
|
1753
|
-
bodystmt.bind(
|
1863
|
+
bodystmt.bind(
|
1864
|
+
find_next_statement_start(target[:char_end]),
|
1865
|
+
ending[:char_start]
|
1866
|
+
)
|
1754
1867
|
|
1755
1868
|
{
|
1756
1869
|
type: :sclass,
|
@@ -1778,6 +1891,10 @@ class Prettier::Parser < Ripper
|
|
1778
1891
|
end
|
1779
1892
|
end
|
1780
1893
|
|
1894
|
+
def bind_end(char_end)
|
1895
|
+
merge!(char_end: char_end)
|
1896
|
+
end
|
1897
|
+
|
1781
1898
|
def <<(statement)
|
1782
1899
|
if self[:body].any?
|
1783
1900
|
merge!(statement.slice(:end, :char_end))
|
@@ -1851,7 +1968,9 @@ class Prettier::Parser < Ripper
|
|
1851
1968
|
# piece of the string.
|
1852
1969
|
def on_string_add(string, piece)
|
1853
1970
|
string.merge!(
|
1854
|
-
body: string[:body] << piece,
|
1971
|
+
body: string[:body] << piece,
|
1972
|
+
end: piece[:end],
|
1973
|
+
char_end: piece[:char_end]
|
1855
1974
|
)
|
1856
1975
|
end
|
1857
1976
|
|
@@ -1948,7 +2067,7 @@ class Prettier::Parser < Ripper
|
|
1948
2067
|
# symbol node (for most cases) or an ident node (in the case that we're
|
1949
2068
|
# using bare words, as in an alias node like alias foo bar).
|
1950
2069
|
def on_symbol_literal(contents)
|
1951
|
-
if
|
2070
|
+
if scanner_events[-1] == contents
|
1952
2071
|
contents.merge(type: :symbol_literal, body: [contents])
|
1953
2072
|
else
|
1954
2073
|
beging = find_scanner_event(:@symbeg)
|
@@ -2042,7 +2161,18 @@ class Prettier::Parser < Ripper
|
|
2042
2161
|
paren: paren
|
2043
2162
|
)
|
2044
2163
|
else
|
2045
|
-
find_scanner_event
|
2164
|
+
# Special case instead of using find_scanner_event here. It turns out that
|
2165
|
+
# if you have a range that goes from a negative number to a negative
|
2166
|
+
# number then you can end up with a .. or a ... that's higher in the
|
2167
|
+
# stack. So we need to explicitly disallow those operators.
|
2168
|
+
index =
|
2169
|
+
scanner_events.rindex do |scanner_event|
|
2170
|
+
scanner_event[:type] == :@op &&
|
2171
|
+
!%w[.. ...].include?(scanner_event[:body])
|
2172
|
+
end
|
2173
|
+
|
2174
|
+
beging = scanner_events.delete_at(index)
|
2175
|
+
beging.merge!(
|
2046
2176
|
type: :unary,
|
2047
2177
|
oper: oper[0],
|
2048
2178
|
body: [value],
|
@@ -2297,7 +2427,9 @@ class Prettier::Parser < Ripper
|
|
2297
2427
|
piece.merge(type: :word, body: [piece])
|
2298
2428
|
else
|
2299
2429
|
word.merge!(
|
2300
|
-
body: word[:body] << piece,
|
2430
|
+
body: word[:body] << piece,
|
2431
|
+
end: piece[:end],
|
2432
|
+
char_end: piece[:char_end]
|
2301
2433
|
)
|
2302
2434
|
end
|
2303
2435
|
end
|
@@ -2335,6 +2467,8 @@ class Prettier::Parser < Ripper
|
|
2335
2467
|
|
2336
2468
|
if heredoc && heredoc[:beging][3] = '`'
|
2337
2469
|
heredoc.merge(type: :xstring, body: [])
|
2470
|
+
elsif RUBY_MAJOR <= 2 && RUBY_MINOR <= 5 && RUBY_PATCH < 7
|
2471
|
+
{ type: :xstring, body: [] }
|
2338
2472
|
else
|
2339
2473
|
find_scanner_event(:@backtick).merge!(type: :xstring, body: [])
|
2340
2474
|
end
|
@@ -2376,7 +2510,9 @@ class Prettier::Parser < Ripper
|
|
2376
2510
|
else
|
2377
2511
|
ending = find_scanner_event(:@tstring_end)
|
2378
2512
|
xstring.merge!(
|
2379
|
-
type: :xstring_literal,
|
2513
|
+
type: :xstring_literal,
|
2514
|
+
end: ending[:end],
|
2515
|
+
char_end: ending[:char_end]
|
2380
2516
|
)
|
2381
2517
|
end
|
2382
2518
|
end
|