prettier 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 657db7d1eef6e33d72337eb6b666678675c9eced4cc19716466b4bd74c039cfc
4
- data.tar.gz: d69ffa4e3b2b48146c112a9ceb0d4ddab3351119c1bb2b9d92848f7beb3f40e4
3
+ metadata.gz: b87bc10882cc30779fa7d83d4b52bada919561ed298c0a42331f6df5b8681fb4
4
+ data.tar.gz: fc46f3778f9824d8a9b5f2d15e70a6419e01ed921b3ed3c7f4a0ff4699b7aa2a
5
5
  SHA512:
6
- metadata.gz: d7c4d27daddee1e5d07393c5358e48a419c75a6e1aff62b36779d093f16f3da613c938b98c188752c93835e66ddd9578ebf6595fcde1a1f69f40f2613b492ddd
7
- data.tar.gz: c586e33dcd0057779689df71ab68d8858aac27c9e89cf2f500edd2b55779b26693cf6756815cc616054c364b6bc4e1adff05263036f74025425218c04676969c
6
+ metadata.gz: 76a79ab1910791821427f896140c008a3b5430a99236c65225b24c633bd333462ad77fd5f90c6f9de060568809aa05585198aba8fcfe64f1c30c381c9c6f8ce7
7
+ data.tar.gz: 408d7cabd27ce93415ee1cf5248746c62fbba32ab3cab820fbdc02f45da2ae2555a89eca2d3be81573acfa02d331a06628a332e22021e4771d51dd2c2a51adf4
@@ -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.3.0...HEAD
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` | `"es5"` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
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:
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@prettier/plugin-ruby",
3
- "version": "1.3.0",
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": "PORT=$(bin/port) jest"
9
+ "test": "jest"
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
@@ -1,18 +1,7 @@
1
- const { spawnSync } = require("child_process");
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
- const child = spawnSync("ruby", [parser], { input: text });
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)/;
@@ -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
@@ -1,23 +1,11 @@
1
- const { spawnSync } = require("child_process");
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
- const child = spawnSync("ruby", [path.join(__dirname, "./parser.rb")], {
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)/;
@@ -1,8 +1,23 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'bundler/setup' if ENV['CI']
4
- require 'json'
5
- require 'rbs'
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
@@ -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) {
@@ -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 (exception || variable) {
33
- if (exception) {
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 rescueBody = path.call(print, "body", 2);
40
+ const bodystmt = path.call(print, "body", 1);
59
41
 
60
- if (rescueBody.parts.length > 0) {
61
- parts.push(indent(concat([hardline, rescueBody])));
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 (addition) {
67
- parts.push(concat([hardline, path.call(print, "body", 3)]));
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) =>
@@ -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 interpolated expression is inside of a heredoc or an xstring
109
- // literal (a string that gets sent to the command line) then we don't want
110
- // to automatically indent, as this can lead to some very odd looking
111
- // expressions
112
- if (["heredoc", "xstring_literal"].includes(path.getParentNode().type)) {
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(
@@ -1,59 +1,11 @@
1
- const { spawnSync } = require("child_process");
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
- const child = spawnSync(
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)/;
@@ -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][2]
1772
- consequent = self[:body][3]
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
- last_exception = exceptions.is_a?(Array) ? exceptions[-1] : exceptions
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: [exceptions, variable, stmts, consequent],
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
@@ -54,8 +54,6 @@ function getCommentChildNodes(node) {
54
54
  switch (node.type) {
55
55
  case "heredoc":
56
56
  return [node.beging];
57
- case "rescue":
58
- return [].concat(node.body[0]).concat(node.body.slice(1));
59
57
  case "aryptn":
60
58
  return [node.body[0]]
61
59
  .concat(node.body[1])
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.3.0
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-05 00:00:00.000000000 Z
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.0.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