prettier 1.4.0 → 1.5.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -128,17 +128,19 @@ The `prettier` executable is now installed and ready for use:
128
128
 
129
129
  Below are the options (from [`src/plugin.js`](src/plugin.js)) that `@prettier/plugin-ruby` currently supports:
130
130
 
131
- | API Option | CLI Option | Default | Description |
132
- | ------------------ | ---------------------- | :-----: | ------------------------------------------------------------------------------------------------------------------------------------ |
133
- | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
134
- | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
135
- | `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
136
- | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
137
- | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
138
- | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
139
- | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
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` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#trailing-comma)). `"es5"` is equivalent to `true`. |
131
+ | API Option | CLI Option | Default | Description |
132
+ | ------------------- | ----------------------- | :-----: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
+ | `printWidth` | `--print-width` | `80` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#print-width)). |
134
+ | `requirePragma` | `--require-pragma` | `false` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#require-pragma)). |
135
+ | `rubyArrayLiteral` | `--ruby-array-literal` | `true` | When possible, favor the use of string and symbol array literals. |
136
+ | `rubyHashLabel` | `--ruby-hash-label` | `true` | When possible, uses the shortened hash key syntax, as opposed to hash rockets. |
137
+ | `rubyModifier` | `--ruby-modifier` | `true` | When it fits on one line, allows while and until statements to use the modifier form. |
138
+ | `rubyNetcatCommand` | `--ruby-netcat-command` | | The prefix of the command to execute to communicate between the node.js process and the Ruby process. (For example, `"nc -U"` or `"telnet -u"`) Normally you should not set this option. |
139
+
140
+ | `rubySingleQuote` | `--ruby-single-quote` | `true` | When double quotes are not necessary for interpolation, prefers the use of single quotes for string literals. |
141
+ | `rubyToProc` | `--ruby-to-proc` | `false` | When possible, convert blocks to the more concise `Symbol#to_proc` syntax. |
142
+ | `tabWidth` | `--tab-width` | `2` | Same as in Prettier ([see prettier docs](https://prettier.io/docs/en/options.html#tab-width)). |
143
+ | `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
144
 
143
145
  Any of these can be added to your existing [prettier configuration
144
146
  file](https://prettier.io/docs/en/configuration.html). For example:
@@ -163,11 +165,20 @@ with some of RuboCop's functionality.
163
165
  Prettier provides a RuboCop configuration file to disable the rules which clash.
164
166
  To enable, add the following config at the top of your project's `.rubocop.yml`:
165
167
 
168
+ #### Ruby gem
169
+
166
170
  ```yaml
167
171
  inherit_gem:
168
172
  prettier: rubocop.yml
169
173
  ```
170
174
 
175
+ #### `npm` package
176
+
177
+ ```yaml
178
+ inherit_from:
179
+ - node_modules/@prettier/plugin-ruby/rubocop.yml
180
+ ```
181
+
171
182
  ## Contributing
172
183
 
173
184
  Check out our [contributing guide](CONTRIBUTING.md). Bug reports and pull requests are welcome on GitHub at https://github.com/prettier/plugin-ruby.
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prettier/plugin-ruby",
3
- "version": "1.4.0",
3
+ "version": "1.5.4",
4
4
  "description": "prettier plugin for the Ruby programming language",
5
5
  "main": "src/plugin.js",
6
6
  "scripts": {
@@ -22,9 +22,9 @@
22
22
  "prettier": ">=1.10"
23
23
  },
24
24
  "devDependencies": {
25
- "eslint": "^7.8.1",
26
- "eslint-config-prettier": "^7.0.0",
27
- "husky": "^4.3.5",
25
+ "eslint": "^7.22.0",
26
+ "eslint-config-prettier": "^8.0.0",
27
+ "husky": "^5.0.9",
28
28
  "jest": "^26.0.0",
29
29
  "pretty-quick": "^3.1.0"
30
30
  },
data/rubocop.yml CHANGED
@@ -24,3 +24,6 @@ Style/TrailingCommaInArrayLiteral: # trailingComma
24
24
 
25
25
  Style/TrailingCommaInHashLiteral: # trailingComma
26
26
  Enabled: false
27
+
28
+ Style/Lambda:
29
+ Enabled: false
data/src/haml/parser.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const parseSync = require("../parser/parseSync");
2
2
 
3
- const parse = (text, _parsers, _opts) => {
4
- return parseSync("haml", text);
3
+ const parse = (text, _parsers, opts) => {
4
+ return parseSync("haml", text, opts);
5
5
  };
6
6
 
7
7
  const pragmaPattern = /^\s*-#\s*@(prettier|format)/;
data/src/haml/parser.rb CHANGED
@@ -131,8 +131,6 @@ module Prettier
131
131
  class HAMLParser
132
132
  def self.parse(source)
133
133
  Haml::Parser.new({}).call(source).as_json
134
- rescue StandardError
135
- false
136
134
  end
137
135
  end
138
136
  end
@@ -16,35 +16,42 @@ function hasCommand(name) {
16
16
 
17
17
  // Finds an netcat-like adapter to use for sending data to a socket. We order
18
18
  // these by likelihood of being found so we can avoid some shell-outs.
19
- function getCommandAndArg() {
19
+ function getCommandAndArgs() {
20
20
  if (hasCommand("nc")) {
21
- return ["nc", "-U"];
21
+ return ["nc", ["-U"]];
22
22
  }
23
23
 
24
24
  if (hasCommand("telnet")) {
25
- return ["telnet", "-u"];
25
+ return ["telnet", ["-u"]];
26
26
  }
27
27
 
28
28
  if (hasCommand("ncat")) {
29
- return ["ncat", "-U"];
29
+ return ["ncat", ["-U"]];
30
30
  }
31
31
 
32
32
  if (hasCommand("socat")) {
33
- return ["socat", "-"];
33
+ return ["socat", ["-"]];
34
34
  }
35
35
 
36
- return ["node", require.resolve("./netcat.js")];
36
+ return ["node", [require.resolve("./netcat.js")]];
37
37
  }
38
38
 
39
39
  let command;
40
- let arg;
40
+ let args;
41
41
 
42
- function getNetcat() {
42
+ function getNetcat(opts) {
43
43
  if (!command) {
44
- [command, arg] = getCommandAndArg();
44
+ if (opts.rubyNetcatCommand) {
45
+ const splits = opts.rubyNetcatCommand.split(" ");
46
+
47
+ command = splits[0];
48
+ args = splits.slice(1);
49
+ } else {
50
+ [command, args] = getCommandAndArgs();
51
+ }
45
52
  }
46
53
 
47
- return { command, arg };
54
+ return { command, args };
48
55
  }
49
56
 
50
57
  module.exports = getNetcat;
data/src/parser/netcat.js CHANGED
@@ -5,11 +5,9 @@
5
5
  const { createConnection } = require("net");
6
6
 
7
7
  const sock = process.argv[process.argv.length - 1];
8
-
9
8
  const client = createConnection(sock, () => process.stdin.pipe(client));
10
9
 
11
10
  client.on("data", (data) => process.stdout.write(data));
12
-
13
11
  client.on("error", (error) => {
14
12
  console.error(error);
15
13
  });
@@ -4,27 +4,37 @@ const requestParse = require("./requestParse");
4
4
  // like it) here since Prettier requires the results of `parse` to be
5
5
  // synchronous and Node.js does not offer a mechanism for synchronous socket
6
6
  // requests.
7
- function parseSync(parser, source) {
8
- const response = requestParse(parser, source);
7
+ function parseSync(parser, source, opts) {
8
+ const { stdout, stderr, status } = requestParse(parser, source, opts);
9
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);
10
+ // We need special handling in case the user's version of nc doesn't support
11
+ // using unix sockets.
12
+ if (stderr.includes("invalid option -- U")) {
13
+ throw new Error(`
14
+ @prettier/plugin-ruby uses netcat to communicate over unix sockets between
15
+ the node.js process running prettier and an underlying Ruby process used
16
+ for parsing. Unfortunately the version of netcat that you have installed
17
+ (GNU netcat) does not support unix sockets. To solve this either uninstall
18
+ GNU netcat and use a different implementation, or change the value of the
19
+ rubyNetcatCommand option in your prettier configuration.
20
+ `);
21
+ }
16
22
 
17
- throw new Error(response.stderr || "An unknown error occurred");
23
+ // If we didn't receive anything over stdout or we have a bad exit status,
24
+ // then throw whatever we can.
25
+ if (stdout.length === 0 || (status !== null && status !== 0)) {
26
+ throw new Error(stderr || "An unknown error occurred");
18
27
  }
19
28
 
20
- const parsed = JSON.parse(response.stdout);
29
+ const parsed = JSON.parse(stdout);
21
30
 
22
31
  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
- );
32
+ const error = new Error(parsed.error);
33
+ if (parsed.loc) {
34
+ error.loc = parsed.loc;
35
+ }
36
+
37
+ throw error;
28
38
  }
29
39
 
30
40
  return parsed;
@@ -54,11 +54,11 @@ function ensureParseServer() {
54
54
  }
55
55
 
56
56
  // Sends a request to the parse server to parse the given content.
57
- function requestParse(parser, source) {
57
+ function requestParse(parser, source, opts) {
58
58
  ensureParseServer();
59
59
 
60
- const { command, arg } = getNetcat();
61
- const { stdout, stderr, status } = spawnSync(command, [arg, sockfile], {
60
+ const { command, args } = getNetcat(opts);
61
+ const { stdout, stderr, status } = spawnSync(command, args.concat(sockfile), {
62
62
  input: `${parser}|${source}`,
63
63
  maxBuffer: 15 * 1024 * 1024
64
64
  });
data/src/parser/server.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'bundler/setup' if ENV['CI']
3
+ require 'bundler/setup' if ENV['PLUGIN_RUBY_CI']
4
4
  require 'socket'
5
5
  require 'json'
6
6
 
@@ -14,7 +14,7 @@ $PROGRAM_NAME = 'prettier-ruby-parser'
14
14
  # Make sure we trap these signals to be sure we get the quit command coming from
15
15
  # the parent node process
16
16
  quit = false
17
- trap(:QUIT) { quit = true }
17
+ trap(:QUIT) { quit = true } if RUBY_PLATFORM != 'java'
18
18
  trap(:INT) { quit = true }
19
19
  trap(:TERM) { quit = true }
20
20
 
@@ -48,6 +48,11 @@ loop do
48
48
  else
49
49
  socket.write('{ "error": true }')
50
50
  end
51
+ rescue Prettier::Parser::ParserError => error
52
+ loc = { start: { line: error.lineno, column: error.column } }
53
+ socket.write(JSON.fast_generate(error: error.message, loc: loc))
54
+ rescue StandardError => error
55
+ socket.write(JSON.fast_generate(error: error.message))
51
56
  ensure
52
57
  socket.close
53
58
  end
data/src/plugin.js CHANGED
@@ -36,6 +36,7 @@ module.exports = {
36
36
  ".rabl",
37
37
  ".rake",
38
38
  ".rb",
39
+ ".rbi",
39
40
  ".rbuild",
40
41
  ".rbw",
41
42
  ".rbx",
@@ -118,6 +119,12 @@ module.exports = {
118
119
  description:
119
120
  "When it fits on one line, allows if, unless, while, and until statements to use the modifier form."
120
121
  },
122
+ rubyNetcatCommand: {
123
+ type: "string",
124
+ category: "Ruby",
125
+ description:
126
+ 'The prefix of the command to execute to communicate between the node.js process and the Ruby process. (For example, "nc -U" or "telnet -u") Normally you should not set this option.'
127
+ },
121
128
  rubySingleQuote: {
122
129
  type: "boolean",
123
130
  category: "Ruby",
data/src/rbs/parser.js CHANGED
@@ -4,8 +4,8 @@ const parseSync = require("../parser/parseSync");
4
4
  // to prettier a JavaScript object that is the equivalent AST that represents
5
5
  // the code stored in that string. We accomplish this by spawning a new Ruby
6
6
  // process of parser.rb and reading JSON off STDOUT.
7
- function parse(text, _parsers, _opts) {
8
- return parseSync("rbs", text);
7
+ function parse(text, _parsers, opts) {
8
+ return parseSync("rbs", text, opts);
9
9
  }
10
10
 
11
11
  const pragmaPattern = /#\s*@(prettier|format)/;
data/src/rbs/parser.rb CHANGED
@@ -23,8 +23,14 @@ end
23
23
  class RBS::Location
24
24
  def to_json(*args)
25
25
  {
26
- start: { line: start_line, column: start_column },
27
- end: { line: end_line, column: end_column },
26
+ start: {
27
+ line: start_line,
28
+ column: start_column
29
+ },
30
+ end: {
31
+ line: end_line,
32
+ column: end_column
33
+ },
28
34
  start_pos: start_pos,
29
35
  end_pos: end_pos
30
36
  }.to_json(*args)
@@ -81,8 +87,6 @@ module Prettier
81
87
  class RBSParser
82
88
  def self.parse(text)
83
89
  { declarations: RBS::Parser.parse_signature(text) }
84
- rescue StandardError
85
- false
86
90
  end
87
91
  end
88
92
  end
data/src/ruby/embed.js CHANGED
@@ -8,7 +8,7 @@ const {
8
8
  stripTrailingHardline
9
9
  } = require("../prettier");
10
10
 
11
- const { literalLineNoBreak } = require("../utils");
11
+ const { literallineWithoutBreakParent } = require("../utils");
12
12
 
13
13
  const parsers = {
14
14
  css: "css",
@@ -24,18 +24,54 @@ const parsers = {
24
24
  // have a test that exercises it because I'm not sure for which parser it is
25
25
  // necessary, but since it's in prettier core I'm keeping it here.
26
26
  /* istanbul ignore next */
27
- const replaceNewlines = (doc) =>
28
- mapDoc(doc, (currentDoc) =>
27
+ function replaceNewlines(doc) {
28
+ return mapDoc(doc, (currentDoc) =>
29
29
  typeof currentDoc === "string" && currentDoc.includes("\n")
30
30
  ? concat(
31
31
  currentDoc
32
32
  .split(/(\n)/g)
33
- .map((v, i) => (i % 2 === 0 ? v : literalLineNoBreak))
33
+ .map((v, i) => (i % 2 === 0 ? v : literallineWithoutBreakParent))
34
34
  )
35
35
  : currentDoc
36
36
  );
37
+ }
37
38
 
38
- const embed = (path, print, textToDoc, _opts) => {
39
+ // Returns a number that represents the minimum amount of leading whitespace
40
+ // that is present on every line in the given string. So for example if you have
41
+ // the following heredoc:
42
+ //
43
+ // <<~HERE
44
+ // my
45
+ // content
46
+ // here
47
+ // HERE
48
+ //
49
+ // then the return value of this function would be 2. If you indented every line
50
+ // of the inner content 2 more spaces then this function would return 4.
51
+ function getCommonLeadingWhitespace(content) {
52
+ const pattern = /^\s+/;
53
+
54
+ return content
55
+ .split("\n")
56
+ .slice(0, -1)
57
+ .reduce((minimum, line) => {
58
+ const matched = pattern.exec(line);
59
+ const length = matched ? matched[0].length : 0;
60
+
61
+ return minimum === null ? length : Math.min(minimum, length);
62
+ }, null);
63
+ }
64
+
65
+ // Returns a new string with the common whitespace stripped out. Effectively it
66
+ // emulates what a squiggly heredoc does in Ruby.
67
+ function stripCommonLeadingWhitespace(content) {
68
+ const lines = content.split("\n");
69
+ const minimum = getCommonLeadingWhitespace(content);
70
+
71
+ return lines.map((line) => line.slice(minimum)).join("\n");
72
+ }
73
+
74
+ function embed(path, print, textToDoc, _opts) {
39
75
  const node = path.getValue();
40
76
 
41
77
  // Currently we only support embedded formatting on heredoc nodes
@@ -45,6 +81,8 @@ const embed = (path, print, textToDoc, _opts) => {
45
81
 
46
82
  // First, ensure that we don't have any interpolation
47
83
  const { beging, body, ending } = node;
84
+ const isSquiggly = beging.body[2] === "~";
85
+
48
86
  if (body.some((part) => part.type !== "@tstring_content")) {
49
87
  return null;
50
88
  }
@@ -56,24 +94,32 @@ const embed = (path, print, textToDoc, _opts) => {
56
94
  return null;
57
95
  }
58
96
 
59
- // Get the content as if it were a source string, and then pass that content
60
- // into the embedded parser. Get back the doc node.
61
- const content = body.map((part) => part.body).join("");
97
+ // Get the content as if it were a source string.
98
+ let content = body.map((part) => part.body).join("");
99
+
100
+ // If we're using a squiggly heredoc, then we're going to manually strip off
101
+ // the leading whitespace of each line up to the minimum leading whitespace so
102
+ // that the embedded parser can handle that for us.
103
+ if (isSquiggly) {
104
+ content = stripCommonLeadingWhitespace(content);
105
+ }
106
+
107
+ // Pass that content into the embedded parser. Get back the doc node.
62
108
  const formatted = concat([
63
- literalLineNoBreak,
109
+ literallineWithoutBreakParent,
64
110
  replaceNewlines(stripTrailingHardline(textToDoc(content, { parser })))
65
111
  ]);
66
112
 
67
113
  // If we're using a squiggly heredoc, then we can properly handle indentation
68
114
  // ourselves.
69
- if (beging.body[2] === "~") {
115
+ if (isSquiggly) {
70
116
  return concat([
71
117
  path.call(print, "beging"),
72
118
  lineSuffix(
73
119
  group(
74
120
  concat([
75
121
  indent(markAsRoot(formatted)),
76
- literalLineNoBreak,
122
+ literallineWithoutBreakParent,
77
123
  ending.trim()
78
124
  ])
79
125
  )
@@ -86,9 +132,11 @@ const embed = (path, print, textToDoc, _opts) => {
86
132
  return markAsRoot(
87
133
  concat([
88
134
  path.call(print, "beging"),
89
- lineSuffix(group(concat([formatted, literalLineNoBreak, ending.trim()])))
135
+ lineSuffix(
136
+ group(concat([formatted, literallineWithoutBreakParent, ending.trim()]))
137
+ )
90
138
  ])
91
139
  );
92
- };
140
+ }
93
141
 
94
142
  module.exports = embed;