prettier 1.3.0 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +355 -371
- data/README.md +1 -1
- data/package.json +5 -5
- data/rubocop.yml +3 -0
- data/src/haml/parser.js +2 -13
- data/src/haml/parser.rb +19 -24
- data/src/parser/getLang.js +32 -0
- data/src/parser/getNetcat.js +50 -0
- data/src/parser/netcat.js +15 -0
- data/src/parser/parseSync.js +28 -0
- data/src/parser/requestParse.js +74 -0
- data/src/parser/server.rb +66 -0
- data/src/plugin.js +1 -0
- data/src/rbs/parser.js +2 -14
- data/src/rbs/parser.rb +28 -27
- data/src/ruby/embed.js +61 -13
- data/src/ruby/nodes/args.js +79 -24
- data/src/ruby/nodes/arrays.js +36 -33
- data/src/ruby/nodes/calls.js +2 -31
- data/src/ruby/nodes/class.js +17 -27
- data/src/ruby/nodes/commands.js +1 -1
- data/src/ruby/nodes/conditionals.js +1 -1
- data/src/ruby/nodes/hashes.js +28 -14
- data/src/ruby/nodes/heredocs.js +5 -3
- data/src/ruby/nodes/loops.js +4 -10
- data/src/ruby/nodes/methods.js +4 -11
- data/src/ruby/nodes/rescue.js +32 -25
- data/src/ruby/nodes/statements.js +6 -5
- data/src/ruby/nodes/strings.js +7 -6
- data/src/ruby/parser.js +2 -50
- data/src/ruby/parser.rb +132 -31
- data/src/ruby/printer.js +8 -5
- data/src/utils.js +2 -1
- data/src/utils/inlineEnsureParens.js +8 -1
- data/src/utils/isEmptyBodyStmt.js +7 -0
- data/src/utils/isEmptyStmts.js +9 -5
- data/src/utils/literallineWithoutBreakParent.js +7 -0
- data/src/utils/printEmptyCollection.js +9 -2
- metadata +11 -4
- data/src/utils/literalLineNoBreak.js +0 -7
data/src/ruby/nodes/commands.js
CHANGED
@@ -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/heredocs.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
const { concat, group, lineSuffix, join } = require("../../prettier");
|
2
|
-
const {
|
2
|
+
const { literallineWithoutBreakParent } = require("../../utils");
|
3
3
|
|
4
4
|
function printHeredoc(path, opts, print) {
|
5
5
|
const { body, ending } = path.getValue();
|
@@ -11,7 +11,7 @@ function printHeredoc(path, opts, print) {
|
|
11
11
|
}
|
12
12
|
|
13
13
|
// In this case, the part of the string is just regular string content
|
14
|
-
return join(
|
14
|
+
return join(literallineWithoutBreakParent, part.body.split("\n"));
|
15
15
|
});
|
16
16
|
|
17
17
|
// We use a literalline break because matching indentation is required
|
@@ -23,7 +23,9 @@ function printHeredoc(path, opts, print) {
|
|
23
23
|
concat([
|
24
24
|
path.call(print, "beging"),
|
25
25
|
lineSuffix(
|
26
|
-
group(
|
26
|
+
group(
|
27
|
+
concat([literallineWithoutBreakParent].concat(parts).concat(ending))
|
28
|
+
)
|
27
29
|
)
|
28
30
|
])
|
29
31
|
);
|
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(
|
data/src/ruby/nodes/rescue.js
CHANGED
@@ -26,28 +26,10 @@ function printEnsure(path, opts, print) {
|
|
26
26
|
}
|
27
27
|
|
28
28
|
function printRescue(path, opts, print) {
|
29
|
-
const [exception, variable, _stmts, addition] = path.getValue().body;
|
30
29
|
const parts = ["rescue"];
|
31
30
|
|
32
|
-
if (
|
33
|
-
|
34
|
-
if (Array.isArray(exception)) {
|
35
|
-
// In this case, it's actually only the one exception (it's an array
|
36
|
-
// of length 1).
|
37
|
-
parts.push(" ", path.call(print, "body", 0, 0));
|
38
|
-
} else {
|
39
|
-
// Here we have multiple exceptions from which we're rescuing, so we
|
40
|
-
// need to align them and join them together.
|
41
|
-
const joiner = concat([",", line]);
|
42
|
-
const exceptions = group(join(joiner, path.call(print, "body", 0)));
|
43
|
-
|
44
|
-
parts.push(" ", align("rescue ".length, exceptions));
|
45
|
-
}
|
46
|
-
}
|
47
|
-
|
48
|
-
if (variable) {
|
49
|
-
parts.push(" => ", path.call(print, "body", 1));
|
50
|
-
}
|
31
|
+
if (path.getValue().body[0]) {
|
32
|
+
parts.push(align("rescue ".length, path.call(print, "body", 0)));
|
51
33
|
} else {
|
52
34
|
// If you don't specify an error to rescue in a `begin/rescue` block, then
|
53
35
|
// implicitly you're rescuing from `StandardError`. In this case, we're
|
@@ -55,16 +37,40 @@ function printRescue(path, opts, print) {
|
|
55
37
|
parts.push(" StandardError");
|
56
38
|
}
|
57
39
|
|
58
|
-
const
|
40
|
+
const bodystmt = path.call(print, "body", 1);
|
59
41
|
|
60
|
-
if (
|
61
|
-
parts.push(indent(concat([hardline,
|
42
|
+
if (bodystmt.parts.length > 0) {
|
43
|
+
parts.push(indent(concat([hardline, bodystmt])));
|
62
44
|
}
|
63
45
|
|
64
46
|
// This is the next clause on the `begin` statement, either another
|
65
47
|
// `rescue`, and `ensure`, or an `else` clause.
|
66
|
-
if (
|
67
|
-
parts.push(concat([hardline, path.call(print, "body",
|
48
|
+
if (path.getValue().body[2]) {
|
49
|
+
parts.push(concat([hardline, path.call(print, "body", 2)]));
|
50
|
+
}
|
51
|
+
|
52
|
+
return group(concat(parts));
|
53
|
+
}
|
54
|
+
|
55
|
+
// This is a container node that we're adding into the AST that isn't present in
|
56
|
+
// Ripper solely so that we have a nice place to attach inline comments.
|
57
|
+
function printRescueEx(path, opts, print) {
|
58
|
+
const [exception, variable] = path.getValue().body;
|
59
|
+
const parts = [];
|
60
|
+
|
61
|
+
if (exception) {
|
62
|
+
let exceptionDoc = path.call(print, "body", 0);
|
63
|
+
|
64
|
+
if (Array.isArray(exceptionDoc)) {
|
65
|
+
const joiner = concat([",", line]);
|
66
|
+
exceptionDoc = group(join(joiner, exceptionDoc));
|
67
|
+
}
|
68
|
+
|
69
|
+
parts.push(" ", exceptionDoc);
|
70
|
+
}
|
71
|
+
|
72
|
+
if (variable) {
|
73
|
+
parts.push(" => ", path.call(print, "body", 1));
|
68
74
|
}
|
69
75
|
|
70
76
|
return group(concat(parts));
|
@@ -89,6 +95,7 @@ module.exports = {
|
|
89
95
|
ensure: printEnsure,
|
90
96
|
redo: literal("redo"),
|
91
97
|
rescue: printRescue,
|
98
|
+
rescue_ex: printRescueEx,
|
92
99
|
rescue_mod: printRescueMod,
|
93
100
|
retry: literal("retry")
|
94
101
|
};
|
@@ -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
|
|
@@ -79,6 +77,9 @@ module.exports = {
|
|
79
77
|
const { body } = path.getValue();
|
80
78
|
return concat([trim, "__END__", literalline, body]);
|
81
79
|
},
|
80
|
+
"@comment"(path, opts, _print) {
|
81
|
+
return opts.printer.printComment(path);
|
82
|
+
},
|
82
83
|
bodystmt: printBodyStmt,
|
83
84
|
paren: printParen,
|
84
85
|
program: (path, opts, print) =>
|
data/src/ruby/nodes/strings.js
CHANGED
@@ -4,6 +4,7 @@ const {
|
|
4
4
|
hardline,
|
5
5
|
indent,
|
6
6
|
literalline,
|
7
|
+
removeLines,
|
7
8
|
softline,
|
8
9
|
join
|
9
10
|
} = require("../../prettier");
|
@@ -103,14 +104,14 @@ function printStringDVar(path, opts, print) {
|
|
103
104
|
}
|
104
105
|
|
105
106
|
function printStringEmbExpr(path, opts, print) {
|
107
|
+
const node = path.getValue();
|
106
108
|
const parts = path.call(print, "body", 0);
|
107
109
|
|
108
|
-
// If the
|
109
|
-
//
|
110
|
-
//
|
111
|
-
|
112
|
-
|
113
|
-
return concat(["#{", parts, "}"]);
|
110
|
+
// If the contents of this embedded expression were originally on the same
|
111
|
+
// line in the source, then we're going to leave them in place and assume
|
112
|
+
// that's the way the developer wanted this expression represented.
|
113
|
+
if (node.sl === node.el) {
|
114
|
+
return concat(["#{", removeLines(parts), "}"]);
|
114
115
|
}
|
115
116
|
|
116
117
|
return group(
|
data/src/ruby/parser.js
CHANGED
@@ -1,59 +1,11 @@
|
|
1
|
-
const
|
2
|
-
const path = require("path");
|
3
|
-
|
4
|
-
// In order to properly parse ruby code, we need to tell the ruby process to
|
5
|
-
// parse using UTF-8. Unfortunately, the way that you accomplish this looks
|
6
|
-
// differently depending on your platform.
|
7
|
-
/* istanbul ignore next */
|
8
|
-
const LANG = (() => {
|
9
|
-
const { env, platform } = process;
|
10
|
-
const envValue = env.LC_ALL || env.LC_CTYPE || env.LANG;
|
11
|
-
|
12
|
-
// If an env var is set for the locale that already includes UTF-8 in the
|
13
|
-
// name, then assume we can go with that.
|
14
|
-
if (envValue && envValue.includes("UTF-8")) {
|
15
|
-
return envValue;
|
16
|
-
}
|
17
|
-
|
18
|
-
// Otherwise, we're going to guess which encoding to use based on the system.
|
19
|
-
// This is probably not the best approach in the world, as you could be on
|
20
|
-
// linux and not have C.UTF-8, but in that case you're probably passing an env
|
21
|
-
// var for it. This object below represents all of the possible values of
|
22
|
-
// process.platform per:
|
23
|
-
// https://nodejs.org/api/process.html#process_process_platform
|
24
|
-
return {
|
25
|
-
aix: "C.UTF-8",
|
26
|
-
darwin: "en_US.UTF-8",
|
27
|
-
freebsd: "C.UTF-8",
|
28
|
-
linux: "C.UTF-8",
|
29
|
-
openbsd: "C.UTF-8",
|
30
|
-
sunos: "C.UTF-8",
|
31
|
-
win32: ".UTF-8"
|
32
|
-
}[platform];
|
33
|
-
})();
|
1
|
+
const parseSync = require("../parser/parseSync");
|
34
2
|
|
35
3
|
// This function is responsible for taking an input string of text and returning
|
36
4
|
// to prettier a JavaScript object that is the equivalent AST that represents
|
37
5
|
// the code stored in that string. We accomplish this by spawning a new Ruby
|
38
6
|
// process of parser.rb and reading JSON off STDOUT.
|
39
7
|
function parse(text, _parsers, _opts) {
|
40
|
-
|
41
|
-
"ruby",
|
42
|
-
["--disable-gems", path.join(__dirname, "./parser.rb")],
|
43
|
-
{
|
44
|
-
env: Object.assign({}, process.env, { LANG }),
|
45
|
-
input: text,
|
46
|
-
maxBuffer: 15 * 1024 * 1024 // 15MB
|
47
|
-
}
|
48
|
-
);
|
49
|
-
|
50
|
-
const error = child.stderr.toString();
|
51
|
-
if (error) {
|
52
|
-
throw new Error(error);
|
53
|
-
}
|
54
|
-
|
55
|
-
const response = child.stdout.toString();
|
56
|
-
return JSON.parse(response);
|
8
|
+
return parseSync("ruby", text);
|
57
9
|
}
|
58
10
|
|
59
11
|
const pragmaPattern = /#\s*@(prettier|format)/;
|
data/src/ruby/parser.rb
CHANGED
@@ -17,10 +17,48 @@ require 'delegate'
|
|
17
17
|
require 'json'
|
18
18
|
require 'ripper'
|
19
19
|
|
20
|
-
module Prettier
|
20
|
+
module Prettier
|
21
|
+
end
|
21
22
|
|
22
23
|
class Prettier::Parser < Ripper
|
23
|
-
|
24
|
+
# Represents a line in the source. If this class is being used, it means that
|
25
|
+
# every character in the string is 1 byte in length, so we can just return the
|
26
|
+
# start of the line + the index.
|
27
|
+
class SingleByteString
|
28
|
+
def initialize(start)
|
29
|
+
@start = start
|
30
|
+
end
|
31
|
+
|
32
|
+
def [](byteindex)
|
33
|
+
@start + byteindex
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Represents a line in the source. If this class is being used, it means that
|
38
|
+
# there are characters in the string that are multi-byte, so we will build up
|
39
|
+
# an array of indices, such that array[byteindex] will be equal to the index
|
40
|
+
# of the character within the string.
|
41
|
+
class MultiByteString
|
42
|
+
def initialize(start, line)
|
43
|
+
@indices = []
|
44
|
+
|
45
|
+
line
|
46
|
+
.each_char
|
47
|
+
.with_index(start) do |char, index|
|
48
|
+
char.bytesize.times { @indices << index }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def [](byteindex)
|
53
|
+
@indices[byteindex]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :source, :lines, :scanner_events
|
58
|
+
|
59
|
+
# This is an attr_accessor so Stmts objects can grab comments out of this
|
60
|
+
# array and attach them to themselves.
|
61
|
+
attr_accessor :comments
|
24
62
|
|
25
63
|
def initialize(source, *args)
|
26
64
|
super(source, *args)
|
@@ -35,9 +73,23 @@ class Prettier::Parser < Ripper
|
|
35
73
|
@heredocs = []
|
36
74
|
|
37
75
|
@scanner_events = []
|
38
|
-
@line_counts = [
|
76
|
+
@line_counts = []
|
77
|
+
|
78
|
+
# Here we're going to build up a list of SingleByteString or MultiByteString
|
79
|
+
# objects. They're each going to represent a string in the source. They are
|
80
|
+
# used by the `char_pos` method to determine where we are in the source
|
81
|
+
# string.
|
82
|
+
last_index = 0
|
83
|
+
|
84
|
+
@source.lines.each do |line|
|
85
|
+
if line.size == line.bytesize
|
86
|
+
@line_counts << SingleByteString.new(last_index)
|
87
|
+
else
|
88
|
+
@line_counts << MultiByteString.new(last_index, line)
|
89
|
+
end
|
39
90
|
|
40
|
-
|
91
|
+
last_index += line.size
|
92
|
+
end
|
41
93
|
end
|
42
94
|
|
43
95
|
def self.parse(source)
|
@@ -55,7 +107,7 @@ class Prettier::Parser < Ripper
|
|
55
107
|
# this line, then we add the number of columns into this line that we've gone
|
56
108
|
# through.
|
57
109
|
def char_pos
|
58
|
-
line_counts[lineno - 1]
|
110
|
+
@line_counts[lineno - 1][column]
|
59
111
|
end
|
60
112
|
|
61
113
|
# As we build up a list of scanner events, we'll periodically need to go
|
@@ -125,6 +177,7 @@ class Prettier::Parser < Ripper
|
|
125
177
|
@comments << {
|
126
178
|
type: :@comment,
|
127
179
|
value: value[1..-1].chomp.force_encoding('UTF-8'),
|
180
|
+
inline: value.strip != lines[lineno - 1],
|
128
181
|
sl: lineno,
|
129
182
|
el: lineno,
|
130
183
|
sc: char_pos,
|
@@ -512,6 +565,12 @@ class Prettier::Parser < Ripper
|
|
512
565
|
# binary is a parser event that represents a binary operation between two
|
513
566
|
# values.
|
514
567
|
def on_binary(left, oper, right)
|
568
|
+
# On most Ruby implementations, oper is a Symbol that represents that
|
569
|
+
# operation being performed. For instance in the example `1 < 2`, the `oper`
|
570
|
+
# object would be `:<`. However, on JRuby, it's an `@op` node, so here we're
|
571
|
+
# going to explicitly convert it into the same normalized form.
|
572
|
+
oper = scanner_events.delete(oper)[:body] unless oper.is_a?(Symbol)
|
573
|
+
|
515
574
|
{
|
516
575
|
type: :binary,
|
517
576
|
body: [left, oper, right],
|
@@ -1670,6 +1729,28 @@ class Prettier::Parser < Ripper
|
|
1670
1729
|
)
|
1671
1730
|
end
|
1672
1731
|
|
1732
|
+
# A special parser error so that we can get nice syntax displays on the error
|
1733
|
+
# message when prettier prints out the results.
|
1734
|
+
class ParserError < StandardError
|
1735
|
+
attr_reader :lineno, :column
|
1736
|
+
|
1737
|
+
def initialize(error, lineno, column)
|
1738
|
+
super(error)
|
1739
|
+
@lineno = lineno
|
1740
|
+
@column = column
|
1741
|
+
end
|
1742
|
+
end
|
1743
|
+
|
1744
|
+
# If we encounter a parse error, just immediately bail out so that our runner
|
1745
|
+
# can catch it.
|
1746
|
+
def on_parse_error(error, *)
|
1747
|
+
raise ParserError.new(error, lineno, column)
|
1748
|
+
end
|
1749
|
+
alias on_alias_error on_parse_error
|
1750
|
+
alias on_assign_error on_parse_error
|
1751
|
+
alias on_class_name_error on_parse_error
|
1752
|
+
alias on_param_error on_parse_error
|
1753
|
+
|
1673
1754
|
# The program node is the very top of the AST. Here we'll attach all of
|
1674
1755
|
# the comments that we've gathered up over the course of parsing the
|
1675
1756
|
# source string. We'll also attach on the __END__ content if there was
|
@@ -1768,8 +1849,8 @@ class Prettier::Parser < Ripper
|
|
1768
1849
|
def bind_end(ec)
|
1769
1850
|
merge!(ec: ec)
|
1770
1851
|
|
1771
|
-
stmts = self[:body][
|
1772
|
-
consequent = self[:body][
|
1852
|
+
stmts = self[:body][1]
|
1853
|
+
consequent = self[:body][2]
|
1773
1854
|
|
1774
1855
|
if consequent
|
1775
1856
|
consequent.bind_end(ec)
|
@@ -1784,16 +1865,30 @@ class Prettier::Parser < Ripper
|
|
1784
1865
|
# inside of a bodystmt.
|
1785
1866
|
def on_rescue(exceptions, variable, stmts, consequent)
|
1786
1867
|
beging = find_scanner_event(:@kw, 'rescue')
|
1868
|
+
exceptions = exceptions[0] if exceptions.is_a?(Array)
|
1787
1869
|
|
1788
|
-
|
1789
|
-
last_node = variable || last_exception || beging
|
1790
|
-
|
1870
|
+
last_node = variable || exceptions || beging
|
1791
1871
|
stmts.bind(find_next_statement_start(last_node[:ec]), char_pos)
|
1792
1872
|
|
1873
|
+
# We add an additional inner node here that ripper doesn't provide so that
|
1874
|
+
# we have a nice place to attach inline comment. But we only need it if we
|
1875
|
+
# have an exception or a variable that we're rescuing.
|
1876
|
+
rescue_ex =
|
1877
|
+
if exceptions || variable
|
1878
|
+
{
|
1879
|
+
type: :rescue_ex,
|
1880
|
+
body: [exceptions, variable],
|
1881
|
+
sl: beging[:sl],
|
1882
|
+
sc: beging[:ec] + 1,
|
1883
|
+
el: last_node[:el],
|
1884
|
+
ec: last_node[:ec]
|
1885
|
+
}
|
1886
|
+
end
|
1887
|
+
|
1793
1888
|
Rescue.new(
|
1794
1889
|
beging.merge!(
|
1795
1890
|
type: :rescue,
|
1796
|
-
body: [
|
1891
|
+
body: [rescue_ex, stmts, consequent],
|
1797
1892
|
el: lineno,
|
1798
1893
|
ec: char_pos
|
1799
1894
|
)
|
@@ -1892,12 +1987,21 @@ class Prettier::Parser < Ripper
|
|
1892
1987
|
# propagate that onto void_stmt nodes inside the stmts in order to make sure
|
1893
1988
|
# all comments get printed appropriately.
|
1894
1989
|
class Stmts < SimpleDelegator
|
1990
|
+
attr_reader :parser
|
1991
|
+
|
1992
|
+
def initialize(parser, values)
|
1993
|
+
@parser = parser
|
1994
|
+
__setobj__(values)
|
1995
|
+
end
|
1996
|
+
|
1895
1997
|
def bind(sc, ec)
|
1896
1998
|
merge!(sc: sc, ec: ec)
|
1897
1999
|
|
1898
2000
|
if self[:body][0][:type] == :void_stmt
|
1899
2001
|
self[:body][0].merge!(sc: sc, ec: sc)
|
1900
2002
|
end
|
2003
|
+
|
2004
|
+
attach_comments(sc, ec)
|
1901
2005
|
end
|
1902
2006
|
|
1903
2007
|
def bind_end(ec)
|
@@ -1914,6 +2018,22 @@ class Prettier::Parser < Ripper
|
|
1914
2018
|
self[:body] << statement
|
1915
2019
|
self
|
1916
2020
|
end
|
2021
|
+
|
2022
|
+
private
|
2023
|
+
|
2024
|
+
def attach_comments(sc, ec)
|
2025
|
+
attachable =
|
2026
|
+
parser.comments.select do |comment|
|
2027
|
+
comment[:type] == :@comment && !comment[:inline] &&
|
2028
|
+
sc <= comment[:sc] && ec >= comment[:ec] &&
|
2029
|
+
!comment[:value].include?('prettier-ignore')
|
2030
|
+
end
|
2031
|
+
|
2032
|
+
return if attachable.empty?
|
2033
|
+
|
2034
|
+
parser.comments -= attachable
|
2035
|
+
self[:body] = (self[:body] + attachable).sort_by! { |node| node[:sc] }
|
2036
|
+
end
|
1917
2037
|
end
|
1918
2038
|
|
1919
2039
|
# stmts_new is a parser event that represents the beginning of a list of
|
@@ -1921,6 +2041,7 @@ class Prettier::Parser < Ripper
|
|
1921
2041
|
# stmts_add events, which we'll append onto an array body.
|
1922
2042
|
def on_stmts_new
|
1923
2043
|
Stmts.new(
|
2044
|
+
self,
|
1924
2045
|
type: :stmts,
|
1925
2046
|
body: [],
|
1926
2047
|
sl: lineno,
|
@@ -2549,23 +2670,3 @@ class Prettier::Parser < Ripper
|
|
2549
2670
|
find_scanner_event(:@kw, 'super').merge!(type: :zsuper)
|
2550
2671
|
end
|
2551
2672
|
end
|
2552
|
-
|
2553
|
-
# If this is the main file we're executing, then most likely this is being
|
2554
|
-
# executed from the parser.js spawn. In that case, read the ruby source from
|
2555
|
-
# stdin and report back the AST over stdout.
|
2556
|
-
|
2557
|
-
if $0 == __FILE__
|
2558
|
-
response = Prettier::Parser.parse($stdin.read)
|
2559
|
-
|
2560
|
-
if !response
|
2561
|
-
warn(
|
2562
|
-
'@prettier/plugin-ruby encountered an error when attempting to parse ' \
|
2563
|
-
'the ruby source. This usually means there was a syntax error in the ' \
|
2564
|
-
'file in question. You can verify by running `ruby -i [path/to/file]`.'
|
2565
|
-
)
|
2566
|
-
|
2567
|
-
exit 1
|
2568
|
-
end
|
2569
|
-
|
2570
|
-
puts JSON.fast_generate(response)
|
2571
|
-
end
|