prettier 1.0.0 → 1.2.2

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.
@@ -13,10 +13,13 @@ const { literal } = require("../utils");
13
13
  // because they have low enough operator precedence that you need to explicitly
14
14
  // keep them in there.
15
15
  const canSkipParens = (args) => {
16
- const statement = args.body[0].body[0].body[0];
17
- return (
18
- statement.type !== "binary" || !["and", "or"].includes(statement.body[1])
19
- );
16
+ const stmts = args.body[0].body[0];
17
+ if (stmts.comments) {
18
+ return false;
19
+ }
20
+
21
+ const stmt = stmts.body[0];
22
+ return stmt.type !== "binary" || !["and", "or"].includes(stmt.body[1]);
20
23
  };
21
24
 
22
25
  const printReturn = (path, opts, print) => {
@@ -27,25 +30,27 @@ const printReturn = (path, opts, print) => {
27
30
  return "return";
28
31
  }
29
32
 
30
- // If the body of the return contains parens, then just skip directly to the
31
- // content of the parens so that we can skip printing parens if we don't
32
- // want them.
33
- if (args.body[0] && args.body[0].type === "paren" && canSkipParens(args)) {
34
- args = args.body[0].body[0];
35
- steps = steps.concat("body", 0, "body", 0);
36
- }
33
+ if (args.body.length === 1) {
34
+ // If the body of the return contains parens, then just skip directly to the
35
+ // content of the parens so that we can skip printing parens if we don't
36
+ // want them.
37
+ if (args.body[0] && args.body[0].type === "paren" && canSkipParens(args)) {
38
+ args = args.body[0].body[0];
39
+ steps = steps.concat("body", 0, "body", 0);
40
+ }
37
41
 
38
- // If we're returning an array literal that isn't a special array, single
39
- // element array, or an empty array, then we want to grab the arguments so
40
- // that we can print them out as if they were normal return arguments.
41
- if (
42
- args.body[0] &&
43
- args.body[0].type === "array" &&
44
- args.body[0].body[0] &&
45
- args.body[0].body[0].body.length > 1 &&
46
- ["args", "args_add_star"].includes(args.body[0].body[0].type)
47
- ) {
48
- steps = steps.concat("body", 0, "body", 0);
42
+ // If we're returning an array literal that isn't a special array, single
43
+ // element array, or an empty array, then we want to grab the arguments so
44
+ // that we can print them out as if they were normal return arguments.
45
+ if (
46
+ args.body[0] &&
47
+ args.body[0].type === "array" &&
48
+ args.body[0].body[0] &&
49
+ args.body[0].body[0].body.length > 1 &&
50
+ ["args", "args_add_star"].includes(args.body[0].body[0].type)
51
+ ) {
52
+ steps = steps.concat("body", 0, "body", 0);
53
+ }
49
54
  }
50
55
 
51
56
  // Now that we've established which actual node is the arguments to return,
@@ -56,6 +61,12 @@ const printReturn = (path, opts, print) => {
56
61
  // be a singular doc as opposed to an array.
57
62
  const value = Array.isArray(parts) ? join(concat([",", line]), parts) : parts;
58
63
 
64
+ // We only get here if we have comments somewhere that would prevent us from
65
+ // skipping the parentheses.
66
+ if (args.body.length === 1 && args.body[0].type === "paren") {
67
+ return concat(["return", value]);
68
+ }
69
+
59
70
  return group(
60
71
  concat([
61
72
  "return",
@@ -98,8 +98,8 @@ module.exports = {
98
98
  stmts[0].comments
99
99
  ) {
100
100
  const comments = path.map(
101
- (commentPath, index) => {
102
- stmts[0].comments[index].printed = true;
101
+ (commentPath) => {
102
+ commentPath.getValue().printed = true;
103
103
  return opts.printer.printComment(commentPath);
104
104
  },
105
105
  "body",
@@ -63,14 +63,14 @@ class Prettier::Parser < Ripper
63
63
  # would happen to be the innermost keyword). Then the outer one would only be
64
64
  # able to grab the first one. In this way all of the scanner events act as
65
65
  # their own stack.
66
- def find_scanner_event(type, body = :any)
66
+ def find_scanner_event(type, body = :any, consume: true)
67
67
  index =
68
68
  scanner_events.rindex do |scanner_event|
69
69
  scanner_event[:type] == type &&
70
70
  (body == :any || (scanner_event[:body] == body))
71
71
  end
72
72
 
73
- scanner_events.delete_at(index)
73
+ consume ? scanner_events.delete_at(index) : (index && scanner_events[index])
74
74
  end
75
75
 
76
76
  # Scanner events occur when the lexer hits a new token, like a keyword or an
@@ -675,8 +675,15 @@ class Prettier::Parser < Ripper
675
675
  # It accepts as arguments the switch of the case and the consequent
676
676
  # clause.
677
677
  def on_case(switch, consequent)
678
- find_scanner_event(:@kw, 'case').merge!(
679
- type: :case,
678
+ beging =
679
+ if event = find_scanner_event(:@kw, 'case', consume: false)
680
+ scanner_events.delete(event).merge!(type: :case)
681
+ else
682
+ keyword = find_scanner_event(:@kw, 'in', consume: false)
683
+ switch.merge(type: :rassign, keyword: keyword)
684
+ end
685
+
686
+ beging.merge!(
680
687
  body: [switch, consequent],
681
688
  end: consequent[:end],
682
689
  char_end: consequent[:char_end]
@@ -746,6 +753,11 @@ class Prettier::Parser < Ripper
746
753
  # of the method, the operator being used to send the method, the name of
747
754
  # the method, and the arguments being passed to the method.
748
755
  def on_command_call(receiver, oper, ident, args)
756
+ # Make sure we take the operator out of the scanner events so that it
757
+ # doesn't get confused for a unary operator later.
758
+ scanner_events.delete(oper)
759
+
760
+ # Grab the ending from either the arguments or the method being sent
749
761
  ending = args || ident
750
762
 
751
763
  {
@@ -814,18 +826,43 @@ class Prettier::Parser < Ripper
814
826
  # │ └> params
815
827
  # └> ident
816
828
  #
829
+ # You can also have single-line methods since Ruby 3.0+, which have slightly
830
+ # different syntax but still flow through this method. Those look like:
831
+ #
832
+ # def foo = bar
833
+ # | |
834
+ # | └> stmt
835
+ # └> ident
836
+ #
817
837
  def on_def(ident, params, bodystmt)
818
838
  # Make sure to delete this scanner event in case you're defining something
819
839
  # like def class which would lead to this being a kw and causing all kinds
820
840
  # of trouble
821
841
  scanner_events.delete(ident)
822
842
 
843
+ # Find the beginning of the method definition, which works for single-line
844
+ # and normal method definitions.
845
+ beging = find_scanner_event(:@kw, 'def')
846
+
847
+ # If we don't have a bodystmt node, then we have a single-line method
848
+ if bodystmt[:type] != :bodystmt
849
+ return(
850
+ {
851
+ type: :defsl,
852
+ body: [ident, params, bodystmt],
853
+ start: beging[:start],
854
+ char_start: beging[:char_start],
855
+ end: bodystmt[:end],
856
+ char_end: bodystmt[:char_end]
857
+ }
858
+ )
859
+ end
860
+
823
861
  if params[:type] == :params && !params[:body].any?
824
862
  location = ident[:char_end]
825
863
  params.merge!(char_start: location, char_end: location)
826
864
  end
827
865
 
828
- beging = find_scanner_event(:@kw, 'def')
829
866
  ending = find_scanner_event(:@kw, 'end')
830
867
 
831
868
  bodystmt.bind(
@@ -981,7 +1018,7 @@ class Prettier::Parser < Ripper
981
1018
  #
982
1019
  # which would be the same symbol as above.
983
1020
  def on_dyna_symbol(string)
984
- if scanner_events.any? { |event| event[:type] == :@symbeg }
1021
+ if find_scanner_event(:@symbeg, consume: false)
985
1022
  # A normal dynamic symbol
986
1023
  beging = find_scanner_event(:@symbeg)
987
1024
  ending = find_scanner_event(:@tstring_end)
@@ -1157,6 +1194,24 @@ class Prettier::Parser < Ripper
1157
1194
  }
1158
1195
  end
1159
1196
 
1197
+ # fndptn is a parser event that represents matching against a pattern where
1198
+ # you find a pattern in an array using the Ruby 3.0+ pattern matching syntax.
1199
+ def on_fndptn(const, presplat, args, postsplat)
1200
+ beging = const || find_scanner_event(:@lbracket)
1201
+ ending = find_scanner_event(:@rbracket)
1202
+
1203
+ pieces = [const, presplat, *args, postsplat].compact
1204
+
1205
+ {
1206
+ type: :fndptn,
1207
+ body: [const, presplat, args, postsplat],
1208
+ start: beging[:start],
1209
+ char_start: beging[:char_start],
1210
+ end: ending[:end],
1211
+ char_end: ending[:char_end]
1212
+ }
1213
+ end
1214
+
1160
1215
  # for is a parser event that represents using the somewhat esoteric for
1161
1216
  # loop. It accepts as arguments an ident which is the iterating variable,
1162
1217
  # an enumerable for that which is being enumerated, and a stmts event that
@@ -1302,8 +1357,12 @@ class Prettier::Parser < Ripper
1302
1357
  end
1303
1358
 
1304
1359
  # in is a parser event that represents using the in keyword within the
1305
- # Ruby 2.7+ pattern matching syntax.
1360
+ # Ruby 2.7+ pattern matching syntax. Alternatively in Ruby 3+ it is also used
1361
+ # to handle rightward assignment for pattern matching.
1306
1362
  def on_in(pattern, stmts, consequent)
1363
+ # Here we have a rightward assignment
1364
+ return pattern unless stmts
1365
+
1307
1366
  beging = find_scanner_event(:@kw, 'in')
1308
1367
  ending = consequent || find_scanner_event(:@kw, 'end')
1309
1368
 
@@ -1341,8 +1400,8 @@ class Prettier::Parser < Ripper
1341
1400
  def on_lambda(params, stmts)
1342
1401
  beging = find_scanner_event(:@tlambda)
1343
1402
 
1344
- if scanner_events.any? { |event| event[:type] == :@tlambeg }
1345
- opening = find_scanner_event(:@tlambeg)
1403
+ if event = find_scanner_event(:@tlambeg, consume: false)
1404
+ opening = scanner_events.delete(event)
1346
1405
  closing = find_scanner_event(:@rbrace)
1347
1406
  else
1348
1407
  opening = find_scanner_event(:@kw, 'do')
@@ -1719,7 +1778,8 @@ class Prettier::Parser < Ripper
1719
1778
  # expression literal, like /foo/. It can be followed by any number of
1720
1779
  # regexp_add events, which we'll append onto an array body.
1721
1780
  def on_regexp_new
1722
- find_scanner_event(:@regexp_beg).merge!(type: :regexp, body: [])
1781
+ beging = find_scanner_event(:@regexp_beg)
1782
+ beging.merge!(type: :regexp, body: [], beging: beging[:body])
1723
1783
  end
1724
1784
 
1725
1785
  # regexp_add is a parser event that represents a piece of a regular
@@ -2305,7 +2365,7 @@ class Prettier::Parser < Ripper
2305
2365
  else
2306
2366
  # You can hit this pattern if you're assigning to a splat using pattern
2307
2367
  # matching syntax in Ruby 2.7+
2308
- { type: :var_field, body: [] }
2368
+ { type: :var_field, body: nil }
2309
2369
  end
2310
2370
  end
2311
2371
 
@@ -20,6 +20,18 @@ function printNode(path, opts, print) {
20
20
  throw new Error(`Unsupported node encountered: ${type}\n${ast}`);
21
21
  }
22
22
 
23
+ // This is an escape-hatch to ignore nodes in the tree. If you have a comment
24
+ // that includes this pattern, then the entire node will be ignored and just the
25
+ // original source will be printed out.
26
+ function hasPrettierIgnore(path) {
27
+ const node = path.getValue();
28
+
29
+ return (
30
+ node.comments &&
31
+ node.comments.some((comment) => comment.value.includes("prettier-ignore"))
32
+ );
33
+ }
34
+
23
35
  const noComments = [
24
36
  "args",
25
37
  "args_add_block",
@@ -55,6 +67,38 @@ function getCommentChildNodes(node) {
55
67
 
56
68
  return [node.body[0]].concat(values).concat(node.body[2]);
57
69
  }
70
+ case "params": {
71
+ const [reqs, optls, rest, post, kwargs, kwargRest, block] = node.body;
72
+ let parts = reqs || [];
73
+
74
+ (optls || []).forEach((optl) => {
75
+ parts = parts.concat(optl);
76
+ });
77
+
78
+ if (rest) {
79
+ parts.push(rest);
80
+ }
81
+
82
+ parts = parts.concat(post || []);
83
+
84
+ (kwargs || []).forEach((kwarg) => {
85
+ if (kwarg[1]) {
86
+ parts = parts.concat(kwarg);
87
+ } else {
88
+ parts.push(kwarg[0]);
89
+ }
90
+ });
91
+
92
+ if (kwargRest && kwargRest !== "nil") {
93
+ parts.push(kwargRest);
94
+ }
95
+
96
+ if (block) {
97
+ parts.push(block);
98
+ }
99
+
100
+ return parts;
101
+ }
58
102
  default:
59
103
  return node.body;
60
104
  }
@@ -83,6 +127,7 @@ function isBlockComment(comment) {
83
127
  module.exports = {
84
128
  embed,
85
129
  print: printNode,
130
+ hasPrettierIgnore,
86
131
  canAttachComment,
87
132
  getCommentChildNodes,
88
133
  printComment,
@@ -1,6 +1,7 @@
1
1
  const { concat } = require("./prettier");
2
2
  const isEmptyStmts = require("./utils/isEmptyStmts");
3
3
  const literalLineNoBreak = require("./utils/literalLineNoBreak");
4
+ const printEmptyCollection = require("./utils/printEmptyCollection");
4
5
 
5
6
  // If the node is a type of assignment or if the node is a paren and nested
6
7
  // inside that paren is a node that is a type of assignment.
@@ -89,5 +90,6 @@ module.exports = {
89
90
  makeCall,
90
91
  noIndent,
91
92
  prefix,
93
+ printEmptyCollection,
92
94
  skipAssignIndent
93
95
  };
@@ -0,0 +1,42 @@
1
+ const { concat, group, hardline, indent, join, line } = require("../prettier");
2
+
3
+ // Empty collections are array or hash literals that do not contain any
4
+ // contents. They can, however, have comments inside the body. You can solve
5
+ // this by having a child node inside the array that gets the comments attached
6
+ // to it, but that requires modifying the parser. Instead, we can just manually
7
+ // print out the non-leading comments here.
8
+ function printEmptyCollection(path, opts, startToken, endToken) {
9
+ const node = path.getValue();
10
+
11
+ // If there are no comments or only leading comments, then we can just print
12
+ // out the start and end token and be done, as there are no comments inside
13
+ // the body of this node.
14
+ if (!node.comments || !node.comments.some((comment) => !comment.leading)) {
15
+ return `${startToken}${endToken}`;
16
+ }
17
+
18
+ const comments = [];
19
+
20
+ // For each comment, go through its path and print it out manually.
21
+ const printComment = (commentPath) => {
22
+ const comment = commentPath.getValue();
23
+
24
+ if (!comment.leading) {
25
+ comment.printed = true;
26
+ comments.push(opts.printer.printComment(commentPath));
27
+ }
28
+ };
29
+
30
+ path.each(printComment, "comments");
31
+
32
+ return group(
33
+ concat([
34
+ startToken,
35
+ indent(concat([hardline, join(hardline, comments)])),
36
+ line,
37
+ endToken
38
+ ])
39
+ );
40
+ }
41
+
42
+ module.exports = printEmptyCollection;
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.0.0
4
+ version: 1.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Deisz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-11 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -73,6 +73,7 @@ files:
73
73
  - src/utils/inlineEnsureParens.js
74
74
  - src/utils/isEmptyStmts.js
75
75
  - src/utils/literalLineNoBreak.js
76
+ - src/utils/printEmptyCollection.js
76
77
  homepage: https://github.com/prettier/plugin-ruby#readme
77
78
  licenses:
78
79
  - MIT
@@ -92,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
93
  - !ruby/object:Gem::Version
93
94
  version: '0'
94
95
  requirements: []
95
- rubygems_version: 3.1.4
96
+ rubygems_version: 3.2.3
96
97
  signing_key:
97
98
  specification_version: 4
98
99
  summary: prettier plugin for the Ruby programming language