prettier 1.3.0 → 1.4.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 +19 -1
- data/README.md +1 -1
- data/package.json +2 -2
- data/src/haml/parser.js +2 -13
- data/src/haml/parser.rb +19 -22
- 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 +33 -0
- data/src/parser/requestParse.js +74 -0
- data/src/parser/server.rb +61 -0
- data/src/rbs/parser.js +2 -14
- data/src/rbs/parser.rb +20 -23
- data/src/ruby/nodes/calls.js +2 -31
- data/src/ruby/nodes/rescue.js +32 -25
- data/src/ruby/nodes/statements.js +3 -0
- data/src/ruby/nodes/strings.js +7 -6
- data/src/ruby/parser.js +2 -50
- data/src/ruby/parser.rb +51 -26
- data/src/ruby/printer.js +0 -2
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b87bc10882cc30779fa7d83d4b52bada919561ed298c0a42331f6df5b8681fb4
|
4
|
+
data.tar.gz: fc46f3778f9824d8a9b5f2d15e70a6419e01ed921b3ed3c7f4a0ff4699b7aa2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76a79ab1910791821427f896140c008a3b5430a99236c65225b24c633bd333462ad77fd5f90c6f9de060568809aa05585198aba8fcfe64f1c30c381c9c6f8ce7
|
7
|
+
data.tar.gz: 408d7cabd27ce93415ee1cf5248746c62fbba32ab3cab820fbdc02f45da2ae2555a89eca2d3be81573acfa02d331a06628a332e22021e4771d51dd2c2a51adf4
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.4.0] - 2021-01-15
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- [@ianks], [@kddeisz] - Use `netcat` to communicate to a parser server for better performance when formatting multiple files.
|
14
|
+
|
15
|
+
### Changed
|
16
|
+
|
17
|
+
- [@jeffcarbs], [@kddeisz] - Long strings with interpolated expressions should only break if the contents in the original source is broken.
|
18
|
+
- [@johannesluedke], [@kddeisz] - Fix for rescues with inline comments.
|
19
|
+
- [@johncsnyder], [@kddeisz] - Comments should not attach to nodes beyond a double newline.
|
20
|
+
- [@blampe], [@kddeisz] - Comments inside of a broken method chain get dropped.
|
21
|
+
- [@clarkdave], [@kddeisz] - Don't ignore Sorbet `sig` transformations.
|
22
|
+
|
9
23
|
## [1.3.0] - 2021-01-05
|
10
24
|
|
11
25
|
### Added
|
@@ -1035,7 +1049,8 @@ would previously result in `array[]`, but now prints properly.
|
|
1035
1049
|
|
1036
1050
|
- Initial release 🎉
|
1037
1051
|
|
1038
|
-
[unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.
|
1052
|
+
[unreleased]: https://github.com/prettier/plugin-ruby/compare/v1.4.0...HEAD
|
1053
|
+
[1.4.0]: https://github.com/prettier/plugin-ruby/compare/v1.3.0...v1.4.0
|
1039
1054
|
[1.3.0]: https://github.com/prettier/plugin-ruby/compare/v1.2.5...v1.3.0
|
1040
1055
|
[1.2.5]: https://github.com/prettier/plugin-ruby/compare/v1.2.4...v1.2.5
|
1041
1056
|
[1.2.4]: https://github.com/prettier/plugin-ruby/compare/v1.2.3...v1.2.4
|
@@ -1107,6 +1122,7 @@ would previously result in `array[]`, but now prints properly.
|
|
1107
1122
|
[@bugthing]: https://github.com/bugthing
|
1108
1123
|
[@cbothner]: https://github.com/cbothner
|
1109
1124
|
[@christoomey]: https://github.com/christoomey
|
1125
|
+
[@clarkdave]: https://github.com/clarkdave
|
1110
1126
|
[@cldevs]: https://github.com/cldevs
|
1111
1127
|
[@codingitwrong]: https://github.com/CodingItWrong
|
1112
1128
|
[@coiti]: https://github.com/coiti
|
@@ -1126,7 +1142,9 @@ would previously result in `array[]`, but now prints properly.
|
|
1126
1142
|
[@jamescostian]: https://github.com/jamescostian
|
1127
1143
|
[@janklimo]: https://github.com/janklimo
|
1128
1144
|
[@jbielick]: https://github.com/jbielick
|
1145
|
+
[@jeffcarbs]: https://github.com/jeffcarbs
|
1129
1146
|
[@joeyjoejoejr]: https://github.com/joeyjoejoejr
|
1147
|
+
[@johannesluedke]: https://github.com/johannesluedke
|
1130
1148
|
[@johncsnyder]: https://github.com/johncsnyder
|
1131
1149
|
[@johnschoeman]: https://github.com/johnschoeman
|
1132
1150
|
[@joshuakgoldberg]: https://github.com/JoshuaKGoldberg
|
data/README.md
CHANGED
@@ -138,7 +138,7 @@ Below are the options (from [`src/plugin.js`](src/plugin.js)) that `@prettier/pl
|
|
138
138
|
| `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
|
139
139
|
| `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
|
140
140
|
| `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
|
141
|
-
| `trailingComma` | `--trailing-comma` | `
|
141
|
+
| `trailingComma` | `--trailing-comma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
|
142
142
|
|
143
143
|
Any of these can be added to your existing [prettier configuration
|
144
144
|
file](https://prettier.io/docs/en/configuration.html). For example:
|
data/package.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "@prettier/plugin-ruby",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.4.0",
|
4
4
|
"description": "prettier plugin for the Ruby programming language",
|
5
5
|
"main": "src/plugin.js",
|
6
6
|
"scripts": {
|
7
7
|
"check-format": "prettier --check '**/*'",
|
8
8
|
"lint": "eslint --cache .",
|
9
|
-
"test": "
|
9
|
+
"test": "jest"
|
10
10
|
},
|
11
11
|
"repository": {
|
12
12
|
"type": "git",
|
data/src/haml/parser.js
CHANGED
@@ -1,18 +1,7 @@
|
|
1
|
-
const
|
2
|
-
const path = require("path");
|
3
|
-
|
4
|
-
const parser = path.join(__dirname, "./parser.rb");
|
1
|
+
const parseSync = require("../parser/parseSync");
|
5
2
|
|
6
3
|
const parse = (text, _parsers, _opts) => {
|
7
|
-
|
8
|
-
|
9
|
-
const error = child.stderr.toString();
|
10
|
-
if (error) {
|
11
|
-
throw new Error(error);
|
12
|
-
}
|
13
|
-
|
14
|
-
const response = child.stdout.toString();
|
15
|
-
return JSON.parse(response);
|
4
|
+
return parseSync("haml", text);
|
16
5
|
};
|
17
6
|
|
18
7
|
const pragmaPattern = /^\s*-#\s*@(prettier|format)/;
|
data/src/haml/parser.rb
CHANGED
@@ -1,10 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'bundler/setup' if ENV['CI']
|
4
|
-
require 'haml'
|
5
|
-
require 'json'
|
6
3
|
require 'ripper'
|
7
4
|
|
5
|
+
begin
|
6
|
+
require 'haml'
|
7
|
+
rescue LoadError
|
8
|
+
# If we can't load the haml gem, then we're going to provide a shim parser
|
9
|
+
# that will warn and bail out.
|
10
|
+
class Prettier::HAMLParser
|
11
|
+
def self.parse(text)
|
12
|
+
warn(
|
13
|
+
'The `haml` gem could not be loaded. Please ensure you have it ' \
|
14
|
+
'installed and that it is available in the gem path.'
|
15
|
+
)
|
16
|
+
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
return
|
22
|
+
end
|
23
|
+
|
8
24
|
class Haml::Parser::ParseNode
|
9
25
|
class DeepAttributeParser
|
10
26
|
def parse(string)
|
@@ -120,22 +136,3 @@ module Prettier
|
|
120
136
|
end
|
121
137
|
end
|
122
138
|
end
|
123
|
-
|
124
|
-
# If this is the main file we're executing, then most likely this is being
|
125
|
-
# executed from the haml.js spawn. In that case, read the ruby source from
|
126
|
-
# stdin and report back the AST over stdout.
|
127
|
-
if $0 == __FILE__
|
128
|
-
response = Prettier::HAMLParser.parse($stdin.read)
|
129
|
-
|
130
|
-
if !response
|
131
|
-
warn(
|
132
|
-
'@prettier/plugin-ruby encountered an error when attempting to parse ' \
|
133
|
-
'the HAML source. This usually means there was a syntax error in the ' \
|
134
|
-
'file in question.'
|
135
|
-
)
|
136
|
-
|
137
|
-
exit 1
|
138
|
-
end
|
139
|
-
|
140
|
-
puts JSON.fast_generate(response)
|
141
|
-
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
// In order to properly parse ruby code, we need to tell the ruby process to
|
2
|
+
// parse using UTF-8. Unfortunately, the way that you accomplish this looks
|
3
|
+
// differently depending on your platform.
|
4
|
+
/* istanbul ignore next */
|
5
|
+
function getLang() {
|
6
|
+
const { env, platform } = process;
|
7
|
+
const envValue = env.LC_ALL || env.LC_CTYPE || env.LANG;
|
8
|
+
|
9
|
+
// If an env var is set for the locale that already includes UTF-8 in the
|
10
|
+
// name, then assume we can go with that.
|
11
|
+
if (envValue && envValue.includes("UTF-8")) {
|
12
|
+
return envValue;
|
13
|
+
}
|
14
|
+
|
15
|
+
// Otherwise, we're going to guess which encoding to use based on the system.
|
16
|
+
// This is probably not the best approach in the world, as you could be on
|
17
|
+
// linux and not have C.UTF-8, but in that case you're probably passing an env
|
18
|
+
// var for it. This object below represents all of the possible values of
|
19
|
+
// process.platform per:
|
20
|
+
// https://nodejs.org/api/process.html#process_process_platform
|
21
|
+
return {
|
22
|
+
aix: "C.UTF-8",
|
23
|
+
darwin: "en_US.UTF-8",
|
24
|
+
freebsd: "C.UTF-8",
|
25
|
+
linux: "C.UTF-8",
|
26
|
+
openbsd: "C.UTF-8",
|
27
|
+
sunos: "C.UTF-8",
|
28
|
+
win32: ".UTF-8"
|
29
|
+
}[platform];
|
30
|
+
}
|
31
|
+
|
32
|
+
module.exports = getLang;
|
@@ -0,0 +1,50 @@
|
|
1
|
+
const { spawnSync } = require("child_process");
|
2
|
+
const os = require("os");
|
3
|
+
|
4
|
+
// Checks to see if an executable is available.
|
5
|
+
function hasCommand(name) {
|
6
|
+
let result;
|
7
|
+
|
8
|
+
if (os.type() === "Windows_NT") {
|
9
|
+
result = spawnSync("where", [name]);
|
10
|
+
} else {
|
11
|
+
result = spawnSync("command", ["-v", name]);
|
12
|
+
}
|
13
|
+
|
14
|
+
return result.status === 0;
|
15
|
+
}
|
16
|
+
|
17
|
+
// Finds an netcat-like adapter to use for sending data to a socket. We order
|
18
|
+
// these by likelihood of being found so we can avoid some shell-outs.
|
19
|
+
function getCommandAndArg() {
|
20
|
+
if (hasCommand("nc")) {
|
21
|
+
return ["nc", "-U"];
|
22
|
+
}
|
23
|
+
|
24
|
+
if (hasCommand("telnet")) {
|
25
|
+
return ["telnet", "-u"];
|
26
|
+
}
|
27
|
+
|
28
|
+
if (hasCommand("ncat")) {
|
29
|
+
return ["ncat", "-U"];
|
30
|
+
}
|
31
|
+
|
32
|
+
if (hasCommand("socat")) {
|
33
|
+
return ["socat", "-"];
|
34
|
+
}
|
35
|
+
|
36
|
+
return ["node", require.resolve("./netcat.js")];
|
37
|
+
}
|
38
|
+
|
39
|
+
let command;
|
40
|
+
let arg;
|
41
|
+
|
42
|
+
function getNetcat() {
|
43
|
+
if (!command) {
|
44
|
+
[command, arg] = getCommandAndArg();
|
45
|
+
}
|
46
|
+
|
47
|
+
return { command, arg };
|
48
|
+
}
|
49
|
+
|
50
|
+
module.exports = getNetcat;
|
@@ -0,0 +1,15 @@
|
|
1
|
+
// A simple fallback when no netcat-compatible adapter is found on the system.
|
2
|
+
// On average, this is 2-3x slower than netcat, but still much faster than
|
3
|
+
// spawning a new Ruby process.
|
4
|
+
|
5
|
+
const { createConnection } = require("net");
|
6
|
+
|
7
|
+
const sock = process.argv[process.argv.length - 1];
|
8
|
+
|
9
|
+
const client = createConnection(sock, () => process.stdin.pipe(client));
|
10
|
+
|
11
|
+
client.on("data", (data) => process.stdout.write(data));
|
12
|
+
|
13
|
+
client.on("error", (error) => {
|
14
|
+
console.error(error);
|
15
|
+
});
|
@@ -0,0 +1,33 @@
|
|
1
|
+
const requestParse = require("./requestParse");
|
2
|
+
|
3
|
+
// Formats and sends a request to the parser server. We use netcat (or something
|
4
|
+
// like it) here since Prettier requires the results of `parse` to be
|
5
|
+
// synchronous and Node.js does not offer a mechanism for synchronous socket
|
6
|
+
// requests.
|
7
|
+
function parseSync(parser, source) {
|
8
|
+
const response = requestParse(parser, source);
|
9
|
+
|
10
|
+
if (
|
11
|
+
response.stdout.length === 0 ||
|
12
|
+
(response.status !== null && response.status !== 0)
|
13
|
+
) {
|
14
|
+
console.error("Could not parse response from server");
|
15
|
+
console.error(response);
|
16
|
+
|
17
|
+
throw new Error(response.stderr || "An unknown error occurred");
|
18
|
+
}
|
19
|
+
|
20
|
+
const parsed = JSON.parse(response.stdout);
|
21
|
+
|
22
|
+
if (parsed.error) {
|
23
|
+
throw new Error(
|
24
|
+
`@prettier/plugin-ruby encountered an error when attempting to parse \
|
25
|
+
the ruby source. This usually means there was a syntax error in the \
|
26
|
+
file in question. You can verify by running \`ruby -i [path/to/file]\`.`
|
27
|
+
);
|
28
|
+
}
|
29
|
+
|
30
|
+
return parsed;
|
31
|
+
}
|
32
|
+
|
33
|
+
module.exports = parseSync;
|
@@ -0,0 +1,74 @@
|
|
1
|
+
const { spawn, spawnSync, execSync } = require("child_process");
|
2
|
+
const path = require("path");
|
3
|
+
const { existsSync, mkdtempSync } = require("fs");
|
4
|
+
const process = require("process");
|
5
|
+
const os = require("os");
|
6
|
+
|
7
|
+
const getNetcat = require("./getNetcat");
|
8
|
+
const getLang = require("./getLang");
|
9
|
+
|
10
|
+
let sockfile = process.env.PRETTIER_RUBY_HOST;
|
11
|
+
|
12
|
+
// Spawn the parser.rb subprocess. We do this since booting Ruby is slow, and we
|
13
|
+
// can re-use the parser process multiple times since it is statelesss.
|
14
|
+
function spawnParseServer() {
|
15
|
+
const server = spawn(
|
16
|
+
"ruby",
|
17
|
+
[path.join(__dirname, "./server.rb"), sockfile],
|
18
|
+
{
|
19
|
+
env: Object.assign({}, process.env, { LANG: getLang() }),
|
20
|
+
detached: true,
|
21
|
+
stdio: "inherit"
|
22
|
+
}
|
23
|
+
);
|
24
|
+
|
25
|
+
process.on("exit", () => {
|
26
|
+
try {
|
27
|
+
process.kill(-server.pid);
|
28
|
+
} catch (e) {
|
29
|
+
// ignore
|
30
|
+
}
|
31
|
+
});
|
32
|
+
|
33
|
+
server.unref();
|
34
|
+
const now = new Date();
|
35
|
+
|
36
|
+
// Wait for server to go live.
|
37
|
+
while (!existsSync(sockfile) && new Date() - now < 3000) {
|
38
|
+
execSync("sleep 0.1");
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
// Ensures that a parser server is currently running by checking against the
|
43
|
+
// sockfile variable. If it is not, create a temporary directory to house the
|
44
|
+
// sockfile and spawn the ruby process.
|
45
|
+
function ensureParseServer() {
|
46
|
+
if (sockfile) {
|
47
|
+
return;
|
48
|
+
}
|
49
|
+
|
50
|
+
const tmpDir = mkdtempSync(path.join(os.tmpdir(), "prettier-ruby"));
|
51
|
+
sockfile = path.join(tmpDir, `${process.pid}.sock`);
|
52
|
+
|
53
|
+
spawnParseServer();
|
54
|
+
}
|
55
|
+
|
56
|
+
// Sends a request to the parse server to parse the given content.
|
57
|
+
function requestParse(parser, source) {
|
58
|
+
ensureParseServer();
|
59
|
+
|
60
|
+
const { command, arg } = getNetcat();
|
61
|
+
const { stdout, stderr, status } = spawnSync(command, [arg, sockfile], {
|
62
|
+
input: `${parser}|${source}`,
|
63
|
+
maxBuffer: 15 * 1024 * 1024
|
64
|
+
});
|
65
|
+
|
66
|
+
return {
|
67
|
+
command,
|
68
|
+
stdout: stdout.toString(),
|
69
|
+
stderr: stderr.toString(),
|
70
|
+
status
|
71
|
+
};
|
72
|
+
}
|
73
|
+
|
74
|
+
module.exports = requestParse;
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup' if ENV['CI']
|
4
|
+
require 'socket'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
require_relative '../ruby/parser'
|
8
|
+
require_relative '../rbs/parser'
|
9
|
+
require_relative '../haml/parser'
|
10
|
+
|
11
|
+
# Set the program name so that it's easy to find if we need it
|
12
|
+
$PROGRAM_NAME = 'prettier-ruby-parser'
|
13
|
+
|
14
|
+
# Make sure we trap these signals to be sure we get the quit command coming from
|
15
|
+
# the parent node process
|
16
|
+
quit = false
|
17
|
+
trap(:QUIT) { quit = true }
|
18
|
+
trap(:INT) { quit = true }
|
19
|
+
trap(:TERM) { quit = true }
|
20
|
+
|
21
|
+
sockfile = ARGV.first || "/tmp/#{$PROGRAM_NAME}.sock"
|
22
|
+
server = UNIXServer.new(sockfile)
|
23
|
+
|
24
|
+
at_exit do
|
25
|
+
server.close
|
26
|
+
File.unlink(sockfile)
|
27
|
+
end
|
28
|
+
|
29
|
+
loop do
|
30
|
+
break if quit
|
31
|
+
|
32
|
+
# Start up a new thread that will handle each successive connection.
|
33
|
+
Thread.new(server.accept_nonblock) do |socket|
|
34
|
+
parser, source = socket.read.force_encoding('UTF-8').split('|', 2)
|
35
|
+
|
36
|
+
response =
|
37
|
+
case parser
|
38
|
+
when 'ruby'
|
39
|
+
Prettier::Parser.parse(source)
|
40
|
+
when 'rbs'
|
41
|
+
Prettier::RBSParser.parse(source)
|
42
|
+
when 'haml'
|
43
|
+
Prettier::HAMLParser.parse(source)
|
44
|
+
end
|
45
|
+
|
46
|
+
if response
|
47
|
+
socket.write(JSON.fast_generate(response))
|
48
|
+
else
|
49
|
+
socket.write('{ "error": true }')
|
50
|
+
end
|
51
|
+
ensure
|
52
|
+
socket.close
|
53
|
+
end
|
54
|
+
rescue IO::WaitReadable, Errno::EINTR
|
55
|
+
# Wait for select(2) to give us a connection that has content for 1 second.
|
56
|
+
# Otherwise timeout and continue on (so that we hit our "break if quit"
|
57
|
+
# pretty often).
|
58
|
+
IO.select([server], nil, nil, 1)
|
59
|
+
|
60
|
+
retry unless quit
|
61
|
+
end
|
data/src/rbs/parser.js
CHANGED
@@ -1,23 +1,11 @@
|
|
1
|
-
const
|
2
|
-
const path = require("path");
|
1
|
+
const parseSync = require("../parser/parseSync");
|
3
2
|
|
4
3
|
// This function is responsible for taking an input string of text and returning
|
5
4
|
// to prettier a JavaScript object that is the equivalent AST that represents
|
6
5
|
// the code stored in that string. We accomplish this by spawning a new Ruby
|
7
6
|
// process of parser.rb and reading JSON off STDOUT.
|
8
7
|
function parse(text, _parsers, _opts) {
|
9
|
-
|
10
|
-
input: text,
|
11
|
-
maxBuffer: 15 * 1024 * 1024 // 15MB
|
12
|
-
});
|
13
|
-
|
14
|
-
const error = child.stderr.toString();
|
15
|
-
if (error) {
|
16
|
-
throw new Error(error);
|
17
|
-
}
|
18
|
-
|
19
|
-
const response = child.stdout.toString();
|
20
|
-
return JSON.parse(response);
|
8
|
+
return parseSync("rbs", text);
|
21
9
|
}
|
22
10
|
|
23
11
|
const pragmaPattern = /#\s*@(prettier|format)/;
|
data/src/rbs/parser.rb
CHANGED
@@ -1,8 +1,23 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
require '
|
5
|
-
|
3
|
+
begin
|
4
|
+
require 'rbs'
|
5
|
+
rescue LoadError
|
6
|
+
# If we can't load the rbs gem, then we're going to provide a shim parser that
|
7
|
+
# will warn and bail out.
|
8
|
+
class Prettier::RBSParser
|
9
|
+
def self.parse(text)
|
10
|
+
warn(
|
11
|
+
'The `rbs` gem could not be loaded. Please ensure you have it ' \
|
12
|
+
'installed and that it is available in the gem path.'
|
13
|
+
)
|
14
|
+
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
return
|
20
|
+
end
|
6
21
|
|
7
22
|
# Monkey-patch this so that we can get the character positions.
|
8
23
|
class RBS::Location
|
@@ -16,6 +31,7 @@ class RBS::Location
|
|
16
31
|
end
|
17
32
|
end
|
18
33
|
|
34
|
+
# Monkey-patch this so that we get whether or not it needs to be escaped.
|
19
35
|
class RBS::Types::Function::Param
|
20
36
|
def to_json(*a)
|
21
37
|
escaped = name && /\A#{RBS::Parser::KEYWORDS_RE}\z/.match?(name)
|
@@ -60,6 +76,7 @@ class RBS::Types::Record
|
|
60
76
|
end
|
61
77
|
end
|
62
78
|
|
79
|
+
# The main parser interface.
|
63
80
|
module Prettier
|
64
81
|
class RBSParser
|
65
82
|
def self.parse(text)
|
@@ -69,23 +86,3 @@ module Prettier
|
|
69
86
|
end
|
70
87
|
end
|
71
88
|
end
|
72
|
-
|
73
|
-
# If this is the main file we're executing, then most likely this is being
|
74
|
-
# executed from the parser.js spawn. In that case, read the rbs source from
|
75
|
-
# stdin and report back the AST over stdout.
|
76
|
-
|
77
|
-
if $0 == __FILE__
|
78
|
-
response = Prettier::RBSParser.parse($stdin.read)
|
79
|
-
|
80
|
-
if !response
|
81
|
-
warn(
|
82
|
-
'@prettier/plugin-ruby encountered an error when attempting to parse ' \
|
83
|
-
'the RBS source. This usually means there was a syntax error in the ' \
|
84
|
-
'file in question.'
|
85
|
-
)
|
86
|
-
|
87
|
-
exit 1
|
88
|
-
end
|
89
|
-
|
90
|
-
puts JSON.fast_generate(response)
|
91
|
-
end
|
data/src/ruby/nodes/calls.js
CHANGED
@@ -43,7 +43,7 @@ function printCall(path, opts, print) {
|
|
43
43
|
|
44
44
|
// If our parent node is a chained node then we're not going to group the
|
45
45
|
// right side of the expression, as we want to have a nice multi-line layout.
|
46
|
-
if (chained.includes(parentNode.type)) {
|
46
|
+
if (chained.includes(parentNode.type) && !node.comments) {
|
47
47
|
parentNode.chain = (node.chain || 0) + 1;
|
48
48
|
parentNode.callChain = (node.callChain || 0) + 1;
|
49
49
|
parentNode.breakDoc = (node.breakDoc || [receiverDoc]).concat(rightSideDoc);
|
@@ -103,7 +103,7 @@ function printMethodAddArg(path, opts, print) {
|
|
103
103
|
|
104
104
|
// If our parent node is a chained node then we're not going to group the
|
105
105
|
// right side of the expression, as we want to have a nice multi-line layout.
|
106
|
-
if (chained.includes(parentNode.type)) {
|
106
|
+
if (chained.includes(parentNode.type) && !node.comments) {
|
107
107
|
parentNode.chain = (node.chain || 0) + 1;
|
108
108
|
parentNode.breakDoc = (node.breakDoc || [methodDoc]).concat(argsDoc);
|
109
109
|
parentNode.firstReceiverType = node.firstReceiverType;
|
@@ -136,41 +136,12 @@ function printMethodAddArg(path, opts, print) {
|
|
136
136
|
return concat([methodDoc, argsDoc]);
|
137
137
|
}
|
138
138
|
|
139
|
-
// Sorbet type annotations look like the following:
|
140
|
-
//
|
141
|
-
// {method_add_block
|
142
|
-
// [{method_add_arg
|
143
|
-
// [{fcall
|
144
|
-
// [{@ident "sig"}]},
|
145
|
-
// {args []}]},
|
146
|
-
// {brace_block [nil, {stmts}]}}]}
|
147
|
-
//
|
148
|
-
function isSorbetTypeAnnotation(node) {
|
149
|
-
const [callNode, blockNode] = node.body;
|
150
|
-
|
151
|
-
return (
|
152
|
-
callNode.type === "method_add_arg" &&
|
153
|
-
callNode.body[0].type === "fcall" &&
|
154
|
-
callNode.body[0].body[0].body === "sig" &&
|
155
|
-
callNode.body[1].type === "args" &&
|
156
|
-
callNode.body[1].body.length === 0 &&
|
157
|
-
blockNode
|
158
|
-
);
|
159
|
-
}
|
160
|
-
|
161
139
|
function printMethodAddBlock(path, opts, print) {
|
162
140
|
const node = path.getValue();
|
163
141
|
|
164
142
|
const [callNode, blockNode] = node.body;
|
165
143
|
const [callDoc, blockDoc] = path.map(print, "body");
|
166
144
|
|
167
|
-
// Very special handling here for sorbet type annotations. They look like Ruby
|
168
|
-
// code, but they're not actually Ruby code, so we're not going to mess with
|
169
|
-
// them at all.
|
170
|
-
if (isSorbetTypeAnnotation(node)) {
|
171
|
-
return opts.originalText.slice(opts.locStart(node), opts.locEnd(node));
|
172
|
-
}
|
173
|
-
|
174
145
|
// Don't bother trying to do any kind of fancy toProc transform if the option
|
175
146
|
// is disabled.
|
176
147
|
if (opts.rubyToProc) {
|
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
|
};
|
@@ -79,6 +79,9 @@ module.exports = {
|
|
79
79
|
const { body } = path.getValue();
|
80
80
|
return concat([trim, "__END__", literalline, body]);
|
81
81
|
},
|
82
|
+
"@comment"(path, opts, _print) {
|
83
|
+
return opts.printer.printComment(path);
|
84
|
+
},
|
82
85
|
bodystmt: printBodyStmt,
|
83
86
|
paren: printParen,
|
84
87
|
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
@@ -22,6 +22,10 @@ module Prettier; end
|
|
22
22
|
class Prettier::Parser < Ripper
|
23
23
|
attr_reader :source, :lines, :scanner_events, :line_counts
|
24
24
|
|
25
|
+
# This is an attr_accessor so Stmts objects can grab comments out of this
|
26
|
+
# array and attach them to themselves.
|
27
|
+
attr_accessor :comments
|
28
|
+
|
25
29
|
def initialize(source, *args)
|
26
30
|
super(source, *args)
|
27
31
|
|
@@ -125,6 +129,7 @@ class Prettier::Parser < Ripper
|
|
125
129
|
@comments << {
|
126
130
|
type: :@comment,
|
127
131
|
value: value[1..-1].chomp.force_encoding('UTF-8'),
|
132
|
+
inline: value.strip != lines[lineno - 1],
|
128
133
|
sl: lineno,
|
129
134
|
el: lineno,
|
130
135
|
sc: char_pos,
|
@@ -1768,8 +1773,8 @@ class Prettier::Parser < Ripper
|
|
1768
1773
|
def bind_end(ec)
|
1769
1774
|
merge!(ec: ec)
|
1770
1775
|
|
1771
|
-
stmts = self[:body][
|
1772
|
-
consequent = self[:body][
|
1776
|
+
stmts = self[:body][1]
|
1777
|
+
consequent = self[:body][2]
|
1773
1778
|
|
1774
1779
|
if consequent
|
1775
1780
|
consequent.bind_end(ec)
|
@@ -1784,16 +1789,30 @@ class Prettier::Parser < Ripper
|
|
1784
1789
|
# inside of a bodystmt.
|
1785
1790
|
def on_rescue(exceptions, variable, stmts, consequent)
|
1786
1791
|
beging = find_scanner_event(:@kw, 'rescue')
|
1792
|
+
exceptions = exceptions[0] if exceptions.is_a?(Array)
|
1787
1793
|
|
1788
|
-
|
1789
|
-
last_node = variable || last_exception || beging
|
1790
|
-
|
1794
|
+
last_node = variable || exceptions || beging
|
1791
1795
|
stmts.bind(find_next_statement_start(last_node[:ec]), char_pos)
|
1792
1796
|
|
1797
|
+
# We add an additional inner node here that ripper doesn't provide so that
|
1798
|
+
# we have a nice place to attach inline comment. But we only need it if we
|
1799
|
+
# have an exception or a variable that we're rescuing.
|
1800
|
+
rescue_ex =
|
1801
|
+
if exceptions || variable
|
1802
|
+
{
|
1803
|
+
type: :rescue_ex,
|
1804
|
+
body: [exceptions, variable],
|
1805
|
+
sl: beging[:sl],
|
1806
|
+
sc: beging[:ec] + 1,
|
1807
|
+
el: last_node[:el],
|
1808
|
+
ec: last_node[:ec]
|
1809
|
+
}
|
1810
|
+
end
|
1811
|
+
|
1793
1812
|
Rescue.new(
|
1794
1813
|
beging.merge!(
|
1795
1814
|
type: :rescue,
|
1796
|
-
body: [
|
1815
|
+
body: [rescue_ex, stmts, consequent],
|
1797
1816
|
el: lineno,
|
1798
1817
|
ec: char_pos
|
1799
1818
|
)
|
@@ -1892,12 +1911,21 @@ class Prettier::Parser < Ripper
|
|
1892
1911
|
# propagate that onto void_stmt nodes inside the stmts in order to make sure
|
1893
1912
|
# all comments get printed appropriately.
|
1894
1913
|
class Stmts < SimpleDelegator
|
1914
|
+
attr_reader :parser
|
1915
|
+
|
1916
|
+
def initialize(parser, values)
|
1917
|
+
@parser = parser
|
1918
|
+
__setobj__(values)
|
1919
|
+
end
|
1920
|
+
|
1895
1921
|
def bind(sc, ec)
|
1896
1922
|
merge!(sc: sc, ec: ec)
|
1897
1923
|
|
1898
1924
|
if self[:body][0][:type] == :void_stmt
|
1899
1925
|
self[:body][0].merge!(sc: sc, ec: sc)
|
1900
1926
|
end
|
1927
|
+
|
1928
|
+
attach_comments(sc, ec)
|
1901
1929
|
end
|
1902
1930
|
|
1903
1931
|
def bind_end(ec)
|
@@ -1914,6 +1942,22 @@ class Prettier::Parser < Ripper
|
|
1914
1942
|
self[:body] << statement
|
1915
1943
|
self
|
1916
1944
|
end
|
1945
|
+
|
1946
|
+
private
|
1947
|
+
|
1948
|
+
def attach_comments(sc, ec)
|
1949
|
+
attachable =
|
1950
|
+
parser.comments.select do |comment|
|
1951
|
+
comment[:type] == :@comment && !comment[:inline] &&
|
1952
|
+
sc <= comment[:sc] && ec >= comment[:ec] &&
|
1953
|
+
!comment[:value].include?('prettier-ignore')
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
return if attachable.empty?
|
1957
|
+
|
1958
|
+
parser.comments -= attachable
|
1959
|
+
self[:body] = (self[:body] + attachable).sort_by! { |node| node[:sc] }
|
1960
|
+
end
|
1917
1961
|
end
|
1918
1962
|
|
1919
1963
|
# stmts_new is a parser event that represents the beginning of a list of
|
@@ -1921,6 +1965,7 @@ class Prettier::Parser < Ripper
|
|
1921
1965
|
# stmts_add events, which we'll append onto an array body.
|
1922
1966
|
def on_stmts_new
|
1923
1967
|
Stmts.new(
|
1968
|
+
self,
|
1924
1969
|
type: :stmts,
|
1925
1970
|
body: [],
|
1926
1971
|
sl: lineno,
|
@@ -2549,23 +2594,3 @@ class Prettier::Parser < Ripper
|
|
2549
2594
|
find_scanner_event(:@kw, 'super').merge!(type: :zsuper)
|
2550
2595
|
end
|
2551
2596
|
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
|
data/src/ruby/printer.js
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prettier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Deisz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-01-
|
11
|
+
date: 2021-01-15 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -44,6 +44,12 @@ files:
|
|
44
44
|
- src/haml/parser.js
|
45
45
|
- src/haml/parser.rb
|
46
46
|
- src/haml/printer.js
|
47
|
+
- src/parser/getLang.js
|
48
|
+
- src/parser/getNetcat.js
|
49
|
+
- src/parser/netcat.js
|
50
|
+
- src/parser/parseSync.js
|
51
|
+
- src/parser/requestParse.js
|
52
|
+
- src/parser/server.rb
|
47
53
|
- src/plugin.js
|
48
54
|
- src/prettier.js
|
49
55
|
- src/rbs/parser.js
|
@@ -117,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
123
|
- !ruby/object:Gem::Version
|
118
124
|
version: '0'
|
119
125
|
requirements: []
|
120
|
-
rubygems_version: 3.
|
126
|
+
rubygems_version: 3.1.4
|
121
127
|
signing_key:
|
122
128
|
specification_version: 4
|
123
129
|
summary: prettier plugin for the Ruby programming language
|