syntax_tree 2.5.0 → 2.7.1

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: 3b1505fca44f379d951132dafb8122a2591fe46607c8402d096bab67bd53d0a3
4
- data.tar.gz: 0d58e60b6779ec1acd22ca776f3610f290c4b048cc1bf41b425f9c5a74e80db8
3
+ metadata.gz: 4e90bde4f4aa60ce6784fd2c03234853bc281b7012cdb496d3f2bd97a1d825a9
4
+ data.tar.gz: 24c4a344934acc5fb0b8b7d8931f689f98185f00fd9b71726f5aeab2499dea98
5
5
  SHA512:
6
- metadata.gz: 42d4dea2571b21b3cf877a129f0a840f30125eb918c558b25b5bc303c915bb758b731151cdf2cfb6e70e92cbc4957d915095d1284312529675fec71f3a6af3a7
7
- data.tar.gz: 5178b395ddcfdcf795c18f7c986e86a12f6ca2bdec871792de2c21220dffc7c2a2238a02926cece3021635f42859ab5084ba4dba21932ce7f1791c2a17a5bc49
6
+ metadata.gz: 70d191270dbe2efd07300169410e4e0674b7abd8ec1ed4d95e87f7bb986b790195f8366f27b77f38eeeb94de1a08ce3467d64fbb126910922857366389ce66c0
7
+ data.tar.gz: c3994813de2fa53eb132dce7e4d176e58a83071232bcdefe3ded77a3fdd35ed52639d99989878b4cf5ca3d13c0a5bc14cbdfb95555b9705e2dd08c88d2abf360
@@ -38,7 +38,7 @@ jobs:
38
38
  ruby-version: '3.1'
39
39
  - name: Check
40
40
  run: |
41
- bundle exec rake check
41
+ bundle exec rake stree:check
42
42
  bundle exec rubocop
43
43
 
44
44
  automerge:
data/.rubocop.yml CHANGED
@@ -78,3 +78,6 @@ Style/PerlBackrefs:
78
78
 
79
79
  Style/SpecialGlobalVars:
80
80
  Enabled: false
81
+
82
+ Style/StructInheritance:
83
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -6,6 +6,41 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.7.1] - 2022-05-25
10
+
11
+ ### Added
12
+
13
+ - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - (Internal) Drastically increase test coverage, including many more tests for the language server and the CLI.
14
+
15
+ ### Changed
16
+
17
+ - [#87](https://github.com/ruby-syntax-tree/syntax_tree/pull/87) - Don't convert quotes on strings if it would result in more escapes.
18
+ - [#91](https://github.com/ruby-syntax-tree/syntax_tree/pull/91) - Always use `[]` with array patterns. There are just too many edge cases where you have to use them anyway. This simplifies the look and makes it more consistent.
19
+ - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Remodel the currently shipped plugins such that they're modifying an options hash instead of overriding methods. This should make it easier for other plugins to reference the already loaded plugins, e.g., the RBS plugin referencing the quotes.
20
+ - [#92](https://github.com/ruby-syntax-tree/syntax_tree/pull/92) - Fix up the language server inlay hints to continue walking the tree once a pattern is found. This should increase useability.
21
+
22
+ ## [2.7.0] - 2022-05-19
23
+
24
+ ### Added
25
+
26
+ - [#88](https://github.com/ruby-syntax-tree/syntax_tree/pull/88) - Provide a `SyntaxTree::BasicVisitor` that has no visit methods implemented.
27
+
28
+ ### Changed
29
+
30
+ - [#90](https://github.com/ruby-syntax-tree/syntax_tree/pull/90) - Provide better formatting for `SyntaxTree::AryPtn` when its nested inside a `SyntaxTree::RAssign`.
31
+
32
+ ## [2.6.0] - 2022-05-16
33
+
34
+ ### Added
35
+
36
+ - [#74](https://github.com/ruby-syntax-tree/syntax_tree/pull/74) - Add Rake test to run check and format commands.
37
+ - [#83](https://github.com/ruby-syntax-tree/syntax_tree/pull/83) - Add a trailing commas plugin.
38
+ - [#84](https://github.com/ruby-syntax-tree/syntax_tree/pull/84) - Handle lambda block-local variables.
39
+
40
+ ### Changed
41
+
42
+ - [#85](https://github.com/ruby-syntax-tree/syntax_tree/pull/85) - Better handle trailing operators on command calls.
43
+
9
44
  ## [2.5.0] - 2022-05-13
10
45
 
11
46
  ### Added
@@ -224,7 +259,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
224
259
 
225
260
  - 🎉 Initial release! 🎉
226
261
 
227
- [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...HEAD
262
+ [unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.1...HEAD
263
+ [2.7.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.7.0...v2.7.1
264
+ [2.7.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.6.0...v2.7.0
265
+ [2.6.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.5.0...v2.6.0
228
266
  [2.5.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.1...v2.5.0
229
267
  [2.4.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.4.0...v2.4.1
230
268
  [2.4.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v2.3.1...v2.4.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- syntax_tree (2.5.0)
4
+ syntax_tree (2.7.1)
5
5
  prettier_print
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -32,12 +32,16 @@ It is built with only standard library dependencies. It additionally ships with
32
32
  - [construct_keys](#construct_keys)
33
33
  - [Visitor](#visitor)
34
34
  - [visit_method](#visit_method)
35
+ - [BasicVisitor](#basicvisitor)
35
36
  - [Language server](#language-server)
36
37
  - [textDocument/formatting](#textdocumentformatting)
37
38
  - [textDocument/inlayHints](#textdocumentinlayhints)
38
39
  - [syntaxTree/visualizing](#syntaxtreevisualizing)
39
40
  - [Plugins](#plugins)
41
+ - [Configuration](#configuration)
42
+ - [Languages](#languages)
40
43
  - [Integration](#integration)
44
+ - [Rake](#rake)
41
45
  - [RuboCop](#rubocop)
42
46
  - [VSCode](#vscode)
43
47
  - [Contributing](#contributing)
@@ -223,7 +227,7 @@ This function takes an input string containing Ruby code and returns the syntax
223
227
 
224
228
  ### SyntaxTree.format(source)
225
229
 
226
- This function takes an input string containing Ruby code, parses it into its underlying syntax tree, and formats it back out to a string.
230
+ This function takes an input string containing Ruby code, parses it into its underlying syntax tree, and formats it back out to a string. You can optionally pass a second argument to this method as well that is the maximum width to print. It defaults to `80`.
227
231
 
228
232
  ## Nodes
229
233
 
@@ -370,6 +374,20 @@ Did you mean? visit_binary
370
374
  from bin/console:8:in `<main>'
371
375
  ```
372
376
 
377
+ ### BasicVisitor
378
+
379
+ When you're defining your own visitor, by default it will walk down the tree even if you don't define `visit_*` methods. This is to ensure you can define a subset of the necessary methods in order to only interact with the nodes you're interested in. If you'd like to change this default to instead raise an error if you visit a node you haven't explicitly handled, you can instead inherit from `BasicVisitor`.
380
+
381
+ ```ruby
382
+ class MyVisitor < SyntaxTree::BasicVisitor
383
+ def visit_int(node)
384
+ # ...
385
+ end
386
+ end
387
+ ```
388
+
389
+ The visitor defined above will error out unless it's only visiting a `SyntaxTree::Int` node. This is useful in a couple of ways, e.g., if you're trying to define a visitor to handle the whole tree but it's currently a work-in-progress.
390
+
373
391
  ## Language server
374
392
 
375
393
  Syntax Tree additionally ships with a language server conforming to the [language server protocol](https://microsoft.github.io/language-server-protocol/). It can be invoked through the CLI by running:
@@ -408,9 +426,12 @@ You can register additional configuration and additional languages that can flow
408
426
 
409
427
  ### Configuration
410
428
 
411
- To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin` directory. Then when invoking the CLI, you will pass `--plugins=my_plugin`. That will get required. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are:
429
+ To register additional configuration, define a file somewhere in your load path named `syntax_tree/my_plugin`. Then when invoking the CLI, you will pass `--plugins=my_plugin`. To require multiple, separate them by a comma. In this way, you can modify Syntax Tree however you would like. Some plugins ship with Syntax Tree itself. They are:
412
430
 
413
431
  * `plugin/single_quotes` - This will change all of your string literals to use single quotes instead of the default double quotes.
432
+ * `plugin/trailing_comma` - This will put trailing commas into multiline array literals, hash literals, and method calls that can support trailing commas.
433
+
434
+ If you're using Syntax Tree as a library, you should require those files directly.
414
435
 
415
436
  ### Languages
416
437
 
@@ -436,6 +457,46 @@ Below are listed all of the "official" language plugins hosted under the same Gi
436
457
 
437
458
  Syntax Tree's goal is to seemlessly integrate into your workflow. To this end, it provides a couple of additional tools beyond the CLI and the Ruby library.
438
459
 
460
+ ### Rake
461
+
462
+ Syntax Tree ships with the ability to define [rake](https://github.com/ruby/rake) tasks that will trigger runs of the CLI. To define them in your application, add the following configuration to your `Rakefile`:
463
+
464
+ ```ruby
465
+ require "syntax_tree/rake_tasks"
466
+ SyntaxTree::Rake::CheckTask.new
467
+ SyntaxTree::Rake::WriteTask.new
468
+ ```
469
+
470
+ These calls will define `rake stree:check` and `rake stree:write` (equivalent to calling `stree check` and `stree write` with the CLI respectively). You can configure them by either passing arguments to the `new` method or by using a block.
471
+
472
+ #### `name`
473
+
474
+ If you'd like to change the default name of the rake task, you can pass that as the first argument, as in:
475
+
476
+ ```ruby
477
+ SyntaxTree::Rake::WriteTask.new(:format)
478
+ ```
479
+
480
+ #### `source_files`
481
+
482
+ If you wanted to configure Syntax Tree to check or write different files than the default (`lib/**/*.rb`), you can set the `source_files` field, as in:
483
+
484
+ ```ruby
485
+ SyntaxTree::Rake::WriteTask.new do |t|
486
+ t.source_files = FileList[%w[Gemfile Rakefile lib/**/*.rb test/**/*.rb]]
487
+ end
488
+ ```
489
+
490
+ #### `plugins`
491
+
492
+ If you're running Syntax Tree with plugins (either your own or the pre-built ones), you can pass that to the `plugins` field, as in:
493
+
494
+ ```ruby
495
+ SyntaxTree::Rake::WriteTask.new do |t|
496
+ t.plugins = ["plugin/single_quotes"]
497
+ end
498
+ ```
499
+
439
500
  ### RuboCop
440
501
 
441
502
  RuboCop and Syntax Tree serve different purposes, but there is overlap with some of RuboCop's functionality. Syntax Tree provides a RuboCop configuration file to disable rules that are redundant with Syntax Tree. To use this configuration file, add the following snippet to the top of your project's `.rubocop.yml`:
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/testtask"
5
+ require "syntax_tree/rake_tasks"
5
6
 
6
7
  Rake::TestTask.new(:test) do |t|
7
8
  t.libs << "test"
@@ -11,24 +12,8 @@ end
11
12
 
12
13
  task default: :test
13
14
 
14
- FILEPATHS = %w[
15
- Gemfile
16
- Rakefile
17
- syntax_tree.gemspec
18
- lib/**/*.rb
19
- test/*.rb
20
- ].freeze
15
+ SOURCE_FILES =
16
+ FileList[%w[Gemfile Rakefile syntax_tree.gemspec lib/**/*.rb test/*.rb]]
21
17
 
22
- task :syntax_tree do
23
- $:.unshift File.expand_path("lib", __dir__)
24
- require "syntax_tree"
25
- require "syntax_tree/cli"
26
- end
27
-
28
- task check: :syntax_tree do
29
- exit SyntaxTree::CLI.run(["check"] + FILEPATHS)
30
- end
31
-
32
- task format: :syntax_tree do
33
- exit SyntaxTree::CLI.run(["write"] + FILEPATHS)
34
- end
18
+ SyntaxTree::Rake::CheckTask.new { |t| t.source_files = SOURCE_FILES }
19
+ SyntaxTree::Rake::WriteTask.new { |t| t.source_files = SOURCE_FILES }
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SyntaxTree
4
+ # BasicVisitor is the parent class of the Visitor class that provides the
5
+ # ability to walk down the tree. It does not define any handlers, so you
6
+ # should extend this class if you want your visitor to raise an error if you
7
+ # attempt to visit a node that you don't handle.
8
+ class BasicVisitor
9
+ # This is raised when you use the Visitor.visit_method method and it fails.
10
+ # It is correctable to through DidYouMean.
11
+ class VisitMethodError < StandardError
12
+ attr_reader :visit_method
13
+
14
+ def initialize(visit_method)
15
+ @visit_method = visit_method
16
+ super("Invalid visit method: #{visit_method}")
17
+ end
18
+ end
19
+
20
+ # This class is used by DidYouMean to offer corrections to invalid visit
21
+ # method names.
22
+ class VisitMethodChecker
23
+ attr_reader :visit_method
24
+
25
+ def initialize(error)
26
+ @visit_method = error.visit_method
27
+ end
28
+
29
+ def corrections
30
+ @corrections ||=
31
+ DidYouMean::SpellChecker.new(
32
+ dictionary: Visitor.visit_methods
33
+ ).correct(visit_method)
34
+ end
35
+
36
+ DidYouMean.correct_error(VisitMethodError, self)
37
+ end
38
+
39
+ class << self
40
+ # This method is here to help folks write visitors.
41
+ #
42
+ # It's not always easy to ensure you're writing the correct method name in
43
+ # the visitor since it's perfectly valid to define methods that don't
44
+ # override these parent methods.
45
+ #
46
+ # If you use this method, you can ensure you're writing the correct method
47
+ # name. It will raise an error if the visit method you're defining isn't
48
+ # actually a method on the parent visitor.
49
+ def visit_method(method_name)
50
+ return if visit_methods.include?(method_name)
51
+
52
+ raise VisitMethodError, method_name
53
+ end
54
+
55
+ # This is the list of all of the valid visit methods.
56
+ def visit_methods
57
+ @visit_methods ||=
58
+ Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
59
+ end
60
+ end
61
+
62
+ def visit(node)
63
+ node&.accept(self)
64
+ end
65
+
66
+ def visit_all(nodes)
67
+ nodes.map { |node| visit(node) }
68
+ end
69
+
70
+ def visit_child_nodes(node)
71
+ visit_all(node.child_nodes)
72
+ end
73
+ end
74
+ end
@@ -4,17 +4,42 @@ module SyntaxTree
4
4
  # A slightly enhanced PP that knows how to format recursively including
5
5
  # comments.
6
6
  class Formatter < PrettierPrint
7
+ # We want to minimize as much as possible the number of options that are
8
+ # available in syntax tree. For the most part, if users want non-default
9
+ # formatting, they should override the format methods on the specific nodes
10
+ # themselves. However, because of some history with prettier and the fact
11
+ # that folks have become entrenched in their ways, we decided to provide a
12
+ # small amount of configurability.
13
+ #
14
+ # Note that we're keeping this in a global-ish hash instead of just
15
+ # overriding methods on classes so that other plugins can reference this if
16
+ # necessary. For example, the RBS plugin references the quote style.
17
+ OPTIONS = { quote: "\"", trailing_comma: false }
18
+
7
19
  COMMENT_PRIORITY = 1
8
20
  HEREDOC_PRIORITY = 2
9
21
 
10
- attr_reader :source, :stack, :quote
22
+ attr_reader :source, :stack
23
+
24
+ # These options are overridden in plugins to we need to make sure they are
25
+ # available here.
26
+ attr_reader :quote, :trailing_comma
27
+ alias trailing_comma? trailing_comma
11
28
 
12
- def initialize(source, ...)
13
- super(...)
29
+ def initialize(
30
+ source,
31
+ *args,
32
+ quote: OPTIONS[:quote],
33
+ trailing_comma: OPTIONS[:trailing_comma]
34
+ )
35
+ super(*args)
14
36
 
15
37
  @source = source
16
38
  @stack = []
17
- @quote = "\""
39
+
40
+ # Memoizing these values per formatter to make access faster.
41
+ @quote = quote
42
+ @trailing_comma = trailing_comma
18
43
  end
19
44
 
20
45
  def self.format(source, node)
@@ -38,6 +38,7 @@ module SyntaxTree
38
38
  #
39
39
  def visit_assign(node)
40
40
  parentheses(node.location) if stack[-2].is_a?(Params)
41
+ super
41
42
  end
42
43
 
43
44
  # Adds parentheses around binary expressions to make it clear which
@@ -57,6 +58,8 @@ module SyntaxTree
57
58
  parentheses(node.location)
58
59
  else
59
60
  end
61
+
62
+ super
60
63
  end
61
64
 
62
65
  # Adds parentheses around ternary operators contained within certain
@@ -70,9 +73,13 @@ module SyntaxTree
70
73
  # a ? b : ₍c ? d : e₎
71
74
  #
72
75
  def visit_if_op(node)
73
- if stack[-2] in Assign | Binary | IfOp | OpAssign
76
+ case stack[-2]
77
+ in Assign | Binary | IfOp | OpAssign
74
78
  parentheses(node.location)
79
+ else
75
80
  end
81
+
82
+ super
76
83
  end
77
84
 
78
85
  # Adds the implicitly rescued StandardError into a bare rescue clause. For
@@ -92,6 +99,8 @@ module SyntaxTree
92
99
  if node.exception.nil?
93
100
  after[node.location.start_char + "rescue".length] << " StandardError"
94
101
  end
102
+
103
+ super
95
104
  end
96
105
 
97
106
  # Adds parentheses around unary statements using the - operator that are
@@ -107,6 +116,8 @@ module SyntaxTree
107
116
  if stack[-2].is_a?(Binary) && (node.operator == "-")
108
117
  parentheses(node.location)
109
118
  end
119
+
120
+ super
110
121
  end
111
122
 
112
123
  def self.find(program)
@@ -70,13 +70,11 @@ module SyntaxTree
70
70
  id:,
71
71
  params: { textDocument: { uri: } }
72
72
  }
73
- output = []
74
- PP.pp(SyntaxTree.parse(store[uri]), output)
75
- write(id: id, result: output.join)
73
+ write(id: id, result: PP.pp(SyntaxTree.parse(store[uri]), +""))
76
74
  in method: %r{\$/.+}
77
75
  # ignored
78
76
  else
79
- raise "Unhandled: #{request}"
77
+ raise ArgumentError, "Unhandled: #{request}"
80
78
  end
81
79
  end
82
80
  end
@@ -109,10 +107,6 @@ module SyntaxTree
109
107
  }
110
108
  end
111
109
 
112
- def log(message)
113
- write(method: "window/logMessage", params: { type: 4, message: message })
114
- end
115
-
116
110
  def inlay_hints(source)
117
111
  inlay_hints = InlayHints.find(SyntaxTree.parse(source))
118
112
  serialize = ->(position, text) { { position: position, text: text } }
@@ -597,10 +597,30 @@ module SyntaxTree
597
597
  q.indent do
598
598
  q.breakable("")
599
599
  q.format(arguments)
600
+ q.if_break { q.text(",") } if q.trailing_comma? && trailing_comma?
600
601
  end
601
602
  q.breakable("")
602
603
  end
603
604
  end
605
+
606
+ private
607
+
608
+ def trailing_comma?
609
+ case arguments
610
+ in Args[parts: [*, ArgBlock]]
611
+ # If the last argument is a block, then we can't put a trailing comma
612
+ # after it without resulting in a syntax error.
613
+ false
614
+ in Args[parts: [Command | CommandCall]]
615
+ # If the only argument is a command or command call, then a trailing
616
+ # comma would be parsed as part of that expression instead of on this
617
+ # one, so we don't want to add a trailing comma.
618
+ false
619
+ else
620
+ # Otherwise, we should be okay to add a trailing comma.
621
+ true
622
+ end
623
+ end
604
624
  end
605
625
 
606
626
  # Args represents a list of arguments being passed to a method call or array
@@ -859,6 +879,7 @@ module SyntaxTree
859
879
  end
860
880
 
861
881
  q.seplist(contents.parts, separator) { |part| q.format(part) }
882
+ q.if_break { q.text(",") } if q.trailing_comma?
862
883
  end
863
884
  q.breakable("")
864
885
  end
@@ -954,6 +975,7 @@ module SyntaxTree
954
975
  q.indent do
955
976
  q.breakable("")
956
977
  q.format(contents)
978
+ q.if_break { q.text(",") } if q.trailing_comma?
957
979
  end
958
980
  end
959
981
 
@@ -1101,30 +1123,20 @@ module SyntaxTree
1101
1123
  end
1102
1124
 
1103
1125
  def format(q)
1104
- parts = [*requireds]
1105
- parts << RestFormatter.new(rest) if rest
1106
- parts += posts
1126
+ q.group do
1127
+ q.format(constant) if constant
1128
+ q.text("[")
1129
+ q.indent do
1130
+ q.breakable("")
1131
+
1132
+ parts = [*requireds]
1133
+ parts << RestFormatter.new(rest) if rest
1134
+ parts += posts
1107
1135
 
1108
- if constant
1109
- q.group do
1110
- q.format(constant)
1111
- q.text("[")
1112
1136
  q.seplist(parts) { |part| q.format(part) }
1113
- q.text("]")
1114
1137
  end
1115
-
1116
- return
1117
- end
1118
-
1119
- parent = q.parent
1120
- if parts.length == 1 || PATTERNS.include?(parent.class)
1121
- q.text("[")
1122
- q.seplist(parts) { |part| q.format(part) }
1138
+ q.breakable("")
1123
1139
  q.text("]")
1124
- elsif parts.empty?
1125
- q.text("[]")
1126
- else
1127
- q.group { q.seplist(parts) { |part| q.format(part) } }
1128
1140
  end
1129
1141
  end
1130
1142
  end
@@ -2117,11 +2129,13 @@ module SyntaxTree
2117
2129
  #
2118
2130
  # break
2119
2131
  #
2120
- in [Paren[
2121
- contents: {
2122
- body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
2123
- }
2124
- ]]
2132
+ in [
2133
+ Paren[
2134
+ contents: {
2135
+ body: [ArrayLiteral[contents: { parts: [_, _, *] }] => array]
2136
+ }
2137
+ ]
2138
+ ]
2125
2139
  # Here we have a single argument that is a set of parentheses wrapping
2126
2140
  # an array literal that has at least 2 elements. We're going to print
2127
2141
  # the contents of the array directly. This would be like if we had:
@@ -2755,10 +2769,17 @@ module SyntaxTree
2755
2769
  q.format(value)
2756
2770
  q.text(" ")
2757
2771
  q.format(operator)
2758
- q.group do
2759
- q.indent do
2760
- q.breakable
2761
- q.format(pattern)
2772
+
2773
+ case pattern
2774
+ in AryPtn | FndPtn | HshPtn
2775
+ q.text(" ")
2776
+ q.format(pattern)
2777
+ else
2778
+ q.group do
2779
+ q.indent do
2780
+ q.breakable
2781
+ q.format(pattern)
2782
+ end
2762
2783
  end
2763
2784
  end
2764
2785
  end
@@ -3030,8 +3051,20 @@ module SyntaxTree
3030
3051
  doc =
3031
3052
  q.nest(0) do
3032
3053
  q.format(receiver)
3033
- q.format(CallOperatorFormatter.new(operator), stackable: false)
3034
- q.format(message)
3054
+
3055
+ # If there are leading comments on the message then we know we have
3056
+ # a newline in the source that is forcing these things apart. In
3057
+ # this case we will have to use a trailing operator.
3058
+ if message.comments.any?(&:leading?)
3059
+ q.format(CallOperatorFormatter.new(operator), stackable: false)
3060
+ q.indent do
3061
+ q.breakable("")
3062
+ q.format(message)
3063
+ end
3064
+ else
3065
+ q.format(CallOperatorFormatter.new(operator), stackable: false)
3066
+ q.format(message)
3067
+ end
3035
3068
  end
3036
3069
 
3037
3070
  case arguments
@@ -3830,9 +3863,9 @@ module SyntaxTree
3830
3863
  # whichever quote the user chose. (If they chose single quotes, then double
3831
3864
  # quoting would activate the escape sequence, and if they chose double
3832
3865
  # quotes, then single quotes would deactivate it.)
3833
- def self.locked?(node)
3866
+ def self.locked?(node, quote)
3834
3867
  node.parts.any? do |part|
3835
- !part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]/)
3868
+ !part.is_a?(TStringContent) || part.value.match?(/\\|#[@${]|#{quote}/)
3836
3869
  end
3837
3870
  end
3838
3871
 
@@ -3947,12 +3980,12 @@ module SyntaxTree
3947
3980
 
3948
3981
  if matched
3949
3982
  [quote, matching]
3950
- elsif Quotes.locked?(self)
3983
+ elsif Quotes.locked?(self, q.quote)
3951
3984
  ["#{":" unless hash_key}'", "'"]
3952
3985
  else
3953
3986
  ["#{":" unless hash_key}#{q.quote}", q.quote]
3954
3987
  end
3955
- elsif Quotes.locked?(self)
3988
+ elsif Quotes.locked?(self, q.quote)
3956
3989
  if quote.start_with?(":")
3957
3990
  [hash_key ? quote[1..] : quote, quote[1..]]
3958
3991
  else
@@ -4539,16 +4572,26 @@ module SyntaxTree
4539
4572
 
4540
4573
  def format(q)
4541
4574
  q.format(constant) if constant
4542
- q.group(0, "[", "]") do
4543
- q.text("*")
4544
- q.format(left)
4545
- q.comma_breakable
4546
4575
 
4547
- q.seplist(values) { |value| q.format(value) }
4548
- q.comma_breakable
4576
+ q.group do
4577
+ q.text("[")
4549
4578
 
4550
- q.text("*")
4551
- q.format(right)
4579
+ q.indent do
4580
+ q.breakable("")
4581
+
4582
+ q.text("*")
4583
+ q.format(left)
4584
+ q.comma_breakable
4585
+
4586
+ q.seplist(values) { |value| q.format(value) }
4587
+ q.comma_breakable
4588
+
4589
+ q.text("*")
4590
+ q.format(right)
4591
+ end
4592
+
4593
+ q.breakable("")
4594
+ q.text("]")
4552
4595
  end
4553
4596
  end
4554
4597
  end
@@ -4751,6 +4794,7 @@ module SyntaxTree
4751
4794
  q.indent do
4752
4795
  q.breakable
4753
4796
  q.seplist(assocs) { |assoc| q.format(assoc) }
4797
+ q.if_break { q.text(",") } if q.trailing_comma?
4754
4798
  end
4755
4799
  q.breakable
4756
4800
  end
@@ -5430,12 +5474,14 @@ module SyntaxTree
5430
5474
  q.format(predicate)
5431
5475
  q.text(" ?")
5432
5476
 
5433
- q.breakable
5434
- q.format(truthy)
5435
- q.text(" :")
5477
+ q.indent do
5478
+ q.breakable
5479
+ q.format(truthy)
5480
+ q.text(" :")
5436
5481
 
5437
- q.breakable
5438
- q.format(falsy)
5482
+ q.breakable
5483
+ q.format(falsy)
5484
+ end
5439
5485
  end
5440
5486
  end
5441
5487
 
@@ -5877,7 +5923,7 @@ module SyntaxTree
5877
5923
  # ->(value) { value * 2 }
5878
5924
  #
5879
5925
  class Lambda < Node
5880
- # [Params | Paren] the parameter declaration for this lambda
5926
+ # [LambdaVar | Paren] the parameter declaration for this lambda
5881
5927
  attr_reader :params
5882
5928
 
5883
5929
  # [BodyStmt | Statements] the expressions to be executed in this lambda
@@ -5932,24 +5978,100 @@ module SyntaxTree
5932
5978
  node.is_a?(Command) || node.is_a?(CommandCall)
5933
5979
  end
5934
5980
 
5935
- q.text(force_parens ? "{" : "do")
5936
- q.indent do
5981
+ if force_parens
5982
+ q.text("{")
5983
+
5984
+ unless statements.empty?
5985
+ q.indent do
5986
+ q.breakable
5987
+ q.format(statements)
5988
+ end
5989
+ q.breakable
5990
+ end
5991
+
5992
+ q.text("}")
5993
+ else
5994
+ q.text("do")
5995
+
5996
+ unless statements.empty?
5997
+ q.indent do
5998
+ q.breakable
5999
+ q.format(statements)
6000
+ end
6001
+ end
6002
+
5937
6003
  q.breakable
5938
- q.format(statements)
6004
+ q.text("end")
5939
6005
  end
5940
-
5941
- q.breakable
5942
- q.text(force_parens ? "}" : "end")
5943
6006
  end
5944
6007
  .if_flat do
5945
- q.text("{ ")
5946
- q.format(statements)
5947
- q.text(" }")
6008
+ q.text("{")
6009
+
6010
+ unless statements.empty?
6011
+ q.text(" ")
6012
+ q.format(statements)
6013
+ q.text(" ")
6014
+ end
6015
+
6016
+ q.text("}")
5948
6017
  end
5949
6018
  end
5950
6019
  end
5951
6020
  end
5952
6021
 
6022
+ # LambdaVar represents the parameters being declared for a lambda. Effectively
6023
+ # this node is everything contained within the parentheses. This includes all
6024
+ # of the various parameter types, as well as block-local variable
6025
+ # declarations.
6026
+ #
6027
+ # -> (positional, optional = value, keyword:, &block; local) do
6028
+ # end
6029
+ #
6030
+ class LambdaVar < Node
6031
+ # [Params] the parameters being declared with the block
6032
+ attr_reader :params
6033
+
6034
+ # [Array[ Ident ]] the list of block-local variable declarations
6035
+ attr_reader :locals
6036
+
6037
+ # [Array[ Comment | EmbDoc ]] the comments attached to this node
6038
+ attr_reader :comments
6039
+
6040
+ def initialize(params:, locals:, location:, comments: [])
6041
+ @params = params
6042
+ @locals = locals
6043
+ @location = location
6044
+ @comments = comments
6045
+ end
6046
+
6047
+ def accept(visitor)
6048
+ visitor.visit_lambda_var(self)
6049
+ end
6050
+
6051
+ def child_nodes
6052
+ [params, *locals]
6053
+ end
6054
+
6055
+ alias deconstruct child_nodes
6056
+
6057
+ def deconstruct_keys(_keys)
6058
+ { params: params, locals: locals, location: location, comments: comments }
6059
+ end
6060
+
6061
+ def empty?
6062
+ params.empty? && locals.empty?
6063
+ end
6064
+
6065
+ def format(q)
6066
+ q.format(params)
6067
+
6068
+ if locals.any?
6069
+ q.text("; ")
6070
+ q.seplist(locals, -> { q.text(", ") }) { |local| q.format(local) }
6071
+ end
6072
+ end
6073
+ end
6074
+
5953
6075
  # LBrace represents the use of a left brace, i.e., {.
5954
6076
  class LBrace < Node
5955
6077
  # [String] the left brace
@@ -8293,7 +8415,7 @@ module SyntaxTree
8293
8415
  end
8294
8416
 
8295
8417
  opening_quote, closing_quote =
8296
- if !Quotes.locked?(self)
8418
+ if !Quotes.locked?(self, q.quote)
8297
8419
  [q.quote, q.quote]
8298
8420
  elsif quote.start_with?("%")
8299
8421
  [quote, Quotes.matching(quote[/%[qQ]?(.)/, 1])]
@@ -548,13 +548,6 @@ module SyntaxTree
548
548
  parts[0].location.to(parts[-1].location)
549
549
  end
550
550
 
551
- # If there's the optional then keyword, then we'll delete that and use it
552
- # as the end bounds of the location.
553
- if (token = find_token(Kw, "then", consume: false))
554
- tokens.delete(token)
555
- location = location.to(token.location)
556
- end
557
-
558
551
  # If there is a plain *, then we're going to fix up the location of it
559
552
  # here because it currently doesn't have anything to use for its precise
560
553
  # location. If we hit a comma, then we've gone too far.
@@ -1698,12 +1691,6 @@ module SyntaxTree
1698
1691
  end
1699
1692
  end
1700
1693
 
1701
- # Delete the optional then keyword
1702
- if (token = find_token(Kw, "then", consume: false))
1703
- parts << token
1704
- tokens.delete(token)
1705
- end
1706
-
1707
1694
  HshPtn.new(
1708
1695
  constant: constant,
1709
1696
  keywords: keywords || [],
@@ -1940,6 +1927,41 @@ module SyntaxTree
1940
1927
  token.location.start_char > beginning.location.start_char
1941
1928
  end
1942
1929
 
1930
+ # We need to do some special mapping here. Since ripper doesn't support
1931
+ # capturing lambda var until 3.2, we need to normalize all of that here.
1932
+ params =
1933
+ case params
1934
+ in Paren[contents: Params]
1935
+ # In this case we've gotten to the <3.2 parentheses wrapping a set of
1936
+ # parameters case. Here we need to manually scan for lambda locals.
1937
+ range = (params.location.start_char + 1)...params.location.end_char
1938
+ locals = lambda_locals(source[range])
1939
+
1940
+ location = params.contents.location
1941
+ location = location.to(locals.last.location) if locals.any?
1942
+
1943
+ Paren.new(
1944
+ lparen: params.lparen,
1945
+ contents:
1946
+ LambdaVar.new(
1947
+ params: params.contents,
1948
+ locals: locals,
1949
+ location: location
1950
+ ),
1951
+ location: params.location,
1952
+ comments: params.comments
1953
+ )
1954
+ in Params
1955
+ # In this case we've gotten to the <3.2 plain set of parameters. In
1956
+ # this case there cannot be lambda locals, so we will wrap the
1957
+ # parameters into a lambda var that has no locals.
1958
+ LambdaVar.new(params: params, locals: [], location: params.location)
1959
+ in LambdaVar
1960
+ # In this case we've gotten to 3.2+ lambda var. In this case we don't
1961
+ # need to do anything and can just the value as given.
1962
+ params
1963
+ end
1964
+
1943
1965
  if braces
1944
1966
  opening = find_token(TLamBeg)
1945
1967
  closing = find_token(RBrace)
@@ -1962,6 +1984,83 @@ module SyntaxTree
1962
1984
  )
1963
1985
  end
1964
1986
 
1987
+ # :call-seq:
1988
+ # on_lambda_var: (Params params, Array[ Ident ] locals) -> LambdaVar
1989
+ def on_lambda_var(params, locals)
1990
+ location = params.location
1991
+ location = location.to(locals.last.location) if locals.any?
1992
+
1993
+ LambdaVar.new(params: params, locals: locals || [], location: location)
1994
+ end
1995
+
1996
+ # Ripper doesn't support capturing lambda local variables until 3.2. To
1997
+ # mitigate this, we have to parse that code for ourselves. We use the range
1998
+ # from the parentheses to find where we _should_ be looking. Then we check
1999
+ # if the resulting tokens match a pattern that we determine means that the
2000
+ # declaration has block-local variables. Once it does, we parse those out
2001
+ # and convert them into Ident nodes.
2002
+ def lambda_locals(source)
2003
+ tokens = Ripper.lex(source)
2004
+
2005
+ # First, check that we have a semi-colon. If we do, then we can start to
2006
+ # parse the tokens _after_ the semicolon.
2007
+ index = tokens.rindex { |token| token[1] == :on_semicolon }
2008
+ return [] unless index
2009
+
2010
+ # Next, map over the tokens and convert them into Ident nodes. Bail out
2011
+ # midway through if we encounter a token we didn't expect. Basically we're
2012
+ # making our own mini-parser here. To do that we'll walk through a small
2013
+ # state machine:
2014
+ #
2015
+ # ┌────────┐ ┌────────┐ ┌─────────┐
2016
+ # │ │ │ │ │┌───────┐│
2017
+ # ──> │ item │ ─── ident ──> │ next │ ─── rparen ──> ││ final ││
2018
+ # │ │ <── comma ─── │ │ │└───────┘│
2019
+ # └────────┘ └────────┘ └─────────┘
2020
+ # │ ^ │ ^
2021
+ # └──┘ └──┘
2022
+ # ignored_nl, sp nl, sp
2023
+ #
2024
+ state = :item
2025
+ transitions = {
2026
+ item: {
2027
+ on_ignored_nl: :item,
2028
+ on_sp: :item,
2029
+ on_ident: :next
2030
+ },
2031
+ next: {
2032
+ on_nl: :next,
2033
+ on_sp: :next,
2034
+ on_comma: :item,
2035
+ on_rparen: :final
2036
+ },
2037
+ final: {
2038
+ }
2039
+ }
2040
+
2041
+ tokens[(index + 1)..].each_with_object([]) do |token, locals|
2042
+ (lineno, column), type, value, = token
2043
+
2044
+ # Make the state transition for the parser. If there isn't a transition
2045
+ # from the current state to a new state for this type, then we're in a
2046
+ # pattern that isn't actually locals. In that case we can return [].
2047
+ state = transitions[state].fetch(type) { return [] }
2048
+
2049
+ # If we hit an identifier, then add it to our list.
2050
+ next if type != :on_ident
2051
+
2052
+ location =
2053
+ Location.token(
2054
+ line: lineno,
2055
+ char: line_counts[lineno - 1][column],
2056
+ column: column,
2057
+ size: value.size
2058
+ )
2059
+
2060
+ locals << Ident.new(value: value, location: location)
2061
+ end
2062
+ end
2063
+
1965
2064
  # :call-seq:
1966
2065
  # on_lbrace: (String value) -> LBrace
1967
2066
  def on_lbrace(value)
@@ -2901,6 +3000,11 @@ module SyntaxTree
2901
3000
  # (StringEmbExpr | StringDVar | TStringContent) part
2902
3001
  # ) -> StringContent
2903
3002
  def on_string_add(string, part)
3003
+ # Due to some eccentricities in how ripper works, you need this here in
3004
+ # case you have a syntax error with an embedded expression that doesn't
3005
+ # finish, as in: "#{"
3006
+ return string if part.is_a?(String)
3007
+
2904
3008
  location =
2905
3009
  string.parts.any? ? string.location.to(part.location) : part.location
2906
3010
 
@@ -1,4 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "syntax_tree/formatter/single_quotes"
4
- SyntaxTree::Formatter.prepend(SyntaxTree::Formatter::SingleQuotes)
3
+ SyntaxTree::Formatter::OPTIONS[:quote] = "'"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ SyntaxTree::Formatter::OPTIONS[:trailing_comma] = true
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/tasklib"
5
+
6
+ require "syntax_tree"
7
+ require "syntax_tree/cli"
8
+
9
+ module SyntaxTree
10
+ module Rake
11
+ # A Rake task that runs check on a set of source files.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'syntax_tree/rake/check_task'
16
+ #
17
+ # SyntaxTree::Rake::CheckTask.new do |t|
18
+ # t.source_files = '{app,config,lib}/**/*.rb'
19
+ # end
20
+ #
21
+ # This will create task that can be run with:
22
+ #
23
+ # rake stree_check
24
+ #
25
+ class CheckTask < ::Rake::TaskLib
26
+ # Name of the task.
27
+ # Defaults to :"stree:check".
28
+ attr_accessor :name
29
+
30
+ # Glob pattern to match source files.
31
+ # Defaults to 'lib/**/*.rb'.
32
+ attr_accessor :source_files
33
+
34
+ # The set of plugins to require.
35
+ # Defaults to [].
36
+ attr_accessor :plugins
37
+
38
+ def initialize(
39
+ name = :"stree:check",
40
+ source_files = ::Rake::FileList["lib/**/*.rb"],
41
+ plugins = []
42
+ )
43
+ @name = name
44
+ @source_files = source_files
45
+ @plugins = plugins
46
+
47
+ yield self if block_given?
48
+ define_task
49
+ end
50
+
51
+ private
52
+
53
+ def define_task
54
+ desc "Runs `stree check` over source files"
55
+ task(name) { run_task }
56
+ end
57
+
58
+ def run_task
59
+ arguments = ["check"]
60
+ arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
61
+
62
+ SyntaxTree::CLI.run(arguments + Array(source_files))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake"
4
+ require "rake/tasklib"
5
+
6
+ require "syntax_tree"
7
+ require "syntax_tree/cli"
8
+
9
+ module SyntaxTree
10
+ module Rake
11
+ # A Rake task that runs format on a set of source files.
12
+ #
13
+ # Example:
14
+ #
15
+ # require 'syntax_tree/rake/write_task'
16
+ #
17
+ # SyntaxTree::Rake::WriteTask.new do |t|
18
+ # t.source_files = '{app,config,lib}/**/*.rb'
19
+ # end
20
+ #
21
+ # This will create task that can be run with:
22
+ #
23
+ # rake stree_write
24
+ #
25
+ class WriteTask < ::Rake::TaskLib
26
+ # Name of the task.
27
+ # Defaults to :"stree:write".
28
+ attr_accessor :name
29
+
30
+ # Glob pattern to match source files.
31
+ # Defaults to 'lib/**/*.rb'.
32
+ attr_accessor :source_files
33
+
34
+ # The set of plugins to require.
35
+ # Defaults to [].
36
+ attr_accessor :plugins
37
+
38
+ def initialize(
39
+ name = :"stree:write",
40
+ source_files = ::Rake::FileList["lib/**/*.rb"],
41
+ plugins = []
42
+ )
43
+ @name = name
44
+ @source_files = source_files
45
+ @plugins = plugins
46
+
47
+ yield self if block_given?
48
+ define_task
49
+ end
50
+
51
+ private
52
+
53
+ def define_task
54
+ desc "Runs `stree write` over source files"
55
+ task(name) { run_task }
56
+ end
57
+
58
+ def run_task
59
+ arguments = ["write"]
60
+ arguments << "--plugins=#{plugins.join(",")}" if plugins.any?
61
+
62
+ SyntaxTree::CLI.run(arguments + Array(source_files))
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rake/check_task"
4
+ require_relative "rake/write_task"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SyntaxTree
4
- VERSION = "2.5.0"
4
+ VERSION = "2.7.1"
5
5
  end
@@ -49,9 +49,7 @@ module SyntaxTree
49
49
  # of circumstances, like when visiting the list of optional parameters
50
50
  # defined on a method.
51
51
  #
52
- class FieldVisitor < Visitor
53
- attr_reader :q
54
-
52
+ class FieldVisitor < BasicVisitor
55
53
  def visit_aref(node)
56
54
  node(node, "aref") do
57
55
  field("collection", node.collection)
@@ -586,6 +584,14 @@ module SyntaxTree
586
584
  end
587
585
  end
588
586
 
587
+ def visit_lambda_var(node)
588
+ node(node, "lambda_var") do
589
+ field("params", node.params)
590
+ list("locals", node.locals) if node.locals.any?
591
+ comments(node)
592
+ end
593
+ end
594
+
589
595
  def visit_lbrace(node)
590
596
  visit_token(node, "lbrace")
591
597
  end
@@ -4,72 +4,7 @@ module SyntaxTree
4
4
  # Visitor is a parent class that provides the ability to walk down the tree
5
5
  # and handle a subset of nodes. By defining your own subclass, you can
6
6
  # explicitly handle a node type by defining a visit_* method.
7
- class Visitor
8
- # This is raised when you use the Visitor.visit_method method and it fails.
9
- # It is correctable to through DidYouMean.
10
- class VisitMethodError < StandardError
11
- attr_reader :visit_method
12
-
13
- def initialize(visit_method)
14
- @visit_method = visit_method
15
- super("Invalid visit method: #{visit_method}")
16
- end
17
- end
18
-
19
- # This class is used by DidYouMean to offer corrections to invalid visit
20
- # method names.
21
- class VisitMethodChecker
22
- attr_reader :visit_method
23
-
24
- def initialize(error)
25
- @visit_method = error.visit_method
26
- end
27
-
28
- def corrections
29
- @corrections ||=
30
- DidYouMean::SpellChecker.new(
31
- dictionary: Visitor.visit_methods
32
- ).correct(visit_method)
33
- end
34
-
35
- DidYouMean.correct_error(VisitMethodError, self)
36
- end
37
-
38
- class << self
39
- # This method is here to help folks write visitors.
40
- #
41
- # It's not always easy to ensure you're writing the correct method name in
42
- # the visitor since it's perfectly valid to define methods that don't
43
- # override these parent methods.
44
- #
45
- # If you use this method, you can ensure you're writing the correct method
46
- # name. It will raise an error if the visit method you're defining isn't
47
- # actually a method on the parent visitor.
48
- def visit_method(method_name)
49
- return if visit_methods.include?(method_name)
50
-
51
- raise VisitMethodError, method_name
52
- end
53
-
54
- # This is the list of all of the valid visit methods.
55
- def visit_methods
56
- @visit_methods ||=
57
- Visitor.instance_methods.grep(/^visit_(?!child_nodes)/)
58
- end
59
- end
60
-
61
- def visit(node)
62
- node&.accept(self)
63
- end
64
-
65
- def visit_all(nodes)
66
- nodes.map { |node| visit(node) }
67
- end
68
-
69
- def visit_child_nodes(node)
70
- visit_all(node.child_nodes)
71
- end
72
-
7
+ class Visitor < BasicVisitor
73
8
  # Visit an ARef node.
74
9
  alias visit_aref visit_child_nodes
75
10
 
@@ -301,6 +236,9 @@ module SyntaxTree
301
236
  # Visit a Lambda node.
302
237
  alias visit_lambda visit_child_nodes
303
238
 
239
+ # Visit a LambdaVar node.
240
+ alias visit_lambda_var visit_child_nodes
241
+
304
242
  # Visit a LBrace node.
305
243
  alias visit_lbrace visit_child_nodes
306
244
 
data/lib/syntax_tree.rb CHANGED
@@ -10,6 +10,8 @@ require_relative "syntax_tree/formatter"
10
10
  require_relative "syntax_tree/node"
11
11
  require_relative "syntax_tree/parser"
12
12
  require_relative "syntax_tree/version"
13
+
14
+ require_relative "syntax_tree/basic_visitor"
13
15
  require_relative "syntax_tree/visitor"
14
16
  require_relative "syntax_tree/visitor/field_visitor"
15
17
  require_relative "syntax_tree/visitor/json_visitor"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: syntax_tree
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.5.0
4
+ version: 2.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Newton
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-14 00:00:00.000000000 Z
11
+ date: 2022-05-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prettier_print
@@ -121,14 +121,18 @@ files:
121
121
  - doc/logo.svg
122
122
  - exe/stree
123
123
  - lib/syntax_tree.rb
124
+ - lib/syntax_tree/basic_visitor.rb
124
125
  - lib/syntax_tree/cli.rb
125
126
  - lib/syntax_tree/formatter.rb
126
- - lib/syntax_tree/formatter/single_quotes.rb
127
127
  - lib/syntax_tree/language_server.rb
128
128
  - lib/syntax_tree/language_server/inlay_hints.rb
129
129
  - lib/syntax_tree/node.rb
130
130
  - lib/syntax_tree/parser.rb
131
131
  - lib/syntax_tree/plugin/single_quotes.rb
132
+ - lib/syntax_tree/plugin/trailing_comma.rb
133
+ - lib/syntax_tree/rake/check_task.rb
134
+ - lib/syntax_tree/rake/write_task.rb
135
+ - lib/syntax_tree/rake_tasks.rb
132
136
  - lib/syntax_tree/version.rb
133
137
  - lib/syntax_tree/visitor.rb
134
138
  - lib/syntax_tree/visitor/field_visitor.rb
@@ -156,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
160
  - !ruby/object:Gem::Version
157
161
  version: '0'
158
162
  requirements: []
159
- rubygems_version: 3.3.3
163
+ rubygems_version: 3.4.0.dev
160
164
  signing_key:
161
165
  specification_version: 4
162
166
  summary: A parser based on ripper
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SyntaxTree
4
- class Formatter
5
- # This module overrides the quote method on the formatter to use single
6
- # quotes for everything instead of double quotes.
7
- module SingleQuotes
8
- def quote
9
- "'"
10
- end
11
- end
12
- end
13
- end