ruby-lsp 0.4.5 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -86
- data/VERSION +1 -1
- data/lib/ruby_lsp/check_docs.rb +112 -0
- data/lib/ruby_lsp/document.rb +13 -2
- data/lib/ruby_lsp/event_emitter.rb +84 -18
- data/lib/ruby_lsp/executor.rb +126 -48
- data/lib/ruby_lsp/internal.rb +2 -0
- data/lib/ruby_lsp/listener.rb +6 -13
- data/lib/ruby_lsp/requests/base_request.rb +0 -5
- data/lib/ruby_lsp/requests/code_action_resolve.rb +1 -1
- data/lib/ruby_lsp/requests/code_actions.rb +1 -1
- data/lib/ruby_lsp/requests/code_lens.rb +82 -66
- data/lib/ruby_lsp/requests/diagnostics.rb +2 -2
- data/lib/ruby_lsp/requests/document_highlight.rb +1 -1
- data/lib/ruby_lsp/requests/document_link.rb +17 -15
- data/lib/ruby_lsp/requests/document_symbol.rb +51 -31
- data/lib/ruby_lsp/requests/folding_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/formatting.rb +10 -11
- data/lib/ruby_lsp/requests/hover.rb +20 -20
- data/lib/ruby_lsp/requests/inlay_hints.rb +1 -1
- data/lib/ruby_lsp/requests/on_type_formatting.rb +1 -1
- data/lib/ruby_lsp/requests/path_completion.rb +21 -57
- data/lib/ruby_lsp/requests/selection_ranges.rb +1 -1
- data/lib/ruby_lsp/requests/semantic_highlighting.rb +1 -1
- data/lib/ruby_lsp/requests/support/common.rb +36 -0
- data/lib/ruby_lsp/requests/support/rubocop_diagnostics_runner.rb +0 -1
- data/lib/ruby_lsp/requests/support/rubocop_formatting_runner.rb +0 -1
- data/lib/ruby_lsp/requests/support/syntax_tree_formatting_runner.rb +5 -2
- data/lib/ruby_lsp/requests.rb +15 -15
- data/lib/ruby_lsp/server.rb +44 -20
- data/lib/ruby_lsp/store.rb +1 -1
- data/lib/ruby_lsp/utils.rb +2 -7
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eadf22b9ba9191df338c0da93b3ce6b98ddf61a4831cf204f79ad7c636695fcd
|
4
|
+
data.tar.gz: c0f7fc6ec73638ffc2075e86e40925a14a2e629d9ee79e10a0ed5039f9fee3f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b3ceb8e427dabbb4819d105b29a82fb719c61c4d2bd69b48c215b7010b6ad79f06bf71f57dc936f8ef74440fd0678b5e3640880008e8d76fe3be50091b8e0da1
|
7
|
+
data.tar.gz: 855d9cff0ee7a3db7fe2699b08fe8d3cb7fc7edb39448fb9821ab58b4c234763f2489388a995de216e90f05ecbc4e4b22b7ef23b266bdff9654efbc0a1dc6bcd
|
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
[](https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp)
|
3
3
|
[](https://join.slack.com/t/ruby-dx/shared_invite/zt-1s6f4y15t-v9jedZ9YUPQLM91TEJ4Gew)
|
4
4
|
|
5
|
-
|
6
5
|
# Ruby LSP
|
7
6
|
|
8
7
|
The Ruby LSP is an implementation of the [language server protocol](https://microsoft.github.io/language-server-protocol/)
|
@@ -21,8 +20,7 @@ get the extra features in the editor. Do not install this gem manually.
|
|
21
20
|
|
22
21
|
### With other editors
|
23
22
|
|
24
|
-
See [editors](
|
25
|
-
Ruby LSP.
|
23
|
+
See [editors](EDITORS.md) for community instructions on setting up the Ruby LSP.
|
26
24
|
|
27
25
|
The gem can be installed by doing
|
28
26
|
```shell
|
@@ -51,92 +49,13 @@ See the [documentation](https://shopify.github.io/ruby-lsp) for more in-depth de
|
|
51
49
|
Bug reports and pull requests are welcome on GitHub at https://github.com/Shopify/ruby-lsp.
|
52
50
|
This project is intended to be a safe, welcoming space for collaboration, and contributors
|
53
51
|
are expected to adhere to the
|
54
|
-
[Contributor Covenant](
|
52
|
+
[Contributor Covenant](CODE_OF_CONDUCT.md)
|
55
53
|
code of conduct.
|
56
54
|
|
57
|
-
|
58
|
-
|
59
|
-
Run the test suite with `bin/test`.
|
60
|
-
|
61
|
-
For more visibility into which tests are running, use the `SpecReporter`:
|
62
|
-
|
63
|
-
`SPEC_REPORTER=1 bin/test`
|
64
|
-
|
65
|
-
By default the tests run with warnings disabled to reduce noise. To enable warnings, pass `VERBOSE=1`.
|
66
|
-
Warnings are always shown when running in CI.
|
67
|
-
|
68
|
-
### Expectation testing
|
69
|
-
|
70
|
-
To simplify the way we run tests over different pieces of Ruby code, we use a custom expectations test framework against a set of Ruby fixtures.
|
71
|
-
|
72
|
-
We define expectations as `.exp` files, of which there are two variants:
|
73
|
-
* `.exp.rb`, to indicate the resulting code after an operation.
|
74
|
-
* `.exp.json`, consisting of a `result`, and an optional set of input `params`.
|
75
|
-
|
76
|
-
To add a new fixture to the expectations test suite:
|
77
|
-
|
78
|
-
1. Add a new fixture `my_fixture.rb` file under `test/fixtures`
|
79
|
-
2. (optional) Add new expectations under `test/expectations/$HANDLER` for the request handlers you're concerned by
|
80
|
-
3. Profit by running `bin/test test/requests/$HANDLER_expectations_test my_fixture`
|
81
|
-
* Handlers for which you added expectations will be checked with `assert_expectations`
|
82
|
-
* Handlers without expectations will be ran against your new test to check that nothing breaks
|
83
|
-
|
84
|
-
To add a new expectations test runner for a new request handler:
|
85
|
-
|
86
|
-
1. Add a new file under `test/requests/$HANDLER_expectations_test.rb` that subclasses `ExpectationsTestRunner` and calls `expectations_tests $HANDLER, "$EXPECTATIONS_DIR"` where: `$HANDLER` is the fully qualified name or your handler class and `$EXPECTATIONS_DIR` is the directory name where you want to store the expectation files.
|
87
|
-
|
88
|
-
```rb
|
89
|
-
# frozen_string_literal: true
|
90
|
-
|
91
|
-
require "test_helper"
|
92
|
-
require "expectations/expectations_test_runner"
|
93
|
-
|
94
|
-
class $HANDLERExpectationsTest < ExpectationsTestRunner
|
95
|
-
expectations_tests RubyLsp::Requests::$HANDLER, "$EXPECTATIONS_DIR"
|
96
|
-
end
|
97
|
-
```
|
98
|
-
|
99
|
-
2. (optional) Override the `run_expectations` and `assert_expectations` methods if needed. See the different request handler expectations runners under `test/requests/*_expectations_test.rb` for examples.
|
100
|
-
|
101
|
-
4. (optional) Add new fixtures for your handler under `test/fixtures`
|
102
|
-
|
103
|
-
5. (optional) Add new expectations under `test/expectations/$HANDLER`
|
104
|
-
* No need to write the expectations by hand, just run the test with an empty expectation file and copy from the output.
|
105
|
-
|
106
|
-
7. Profit by running, `bin/test test/expectations_test $HANDLER`
|
107
|
-
* Tests with expectations will be checked with `assert_expectations`
|
108
|
-
* Tests without expectations will be ran against your new $HANDLER to check that nothing breaks
|
109
|
-
|
110
|
-
### Debugging
|
111
|
-
|
112
|
-
### Debugging Tests
|
113
|
-
|
114
|
-
1. Open the test file.
|
115
|
-
2. Set a breakpoint(s) on lines by clicking next to their numbers.
|
116
|
-
3. Open VS Code's `Run and Debug` panel.
|
117
|
-
4. At the top of the panel, select `Minitset - current file` and click the green triangle (or press F5).
|
118
|
-
|
119
|
-
### Debugging Running Ruby LSP Process
|
120
|
-
|
121
|
-
1. Open the `vscode-ruby-lsp` project in VS Code.
|
122
|
-
2. [`vscode-ruby-lsp`] Open VS Code's `Run and Debug` panel.
|
123
|
-
3. [`vscode-ruby-lsp`] Select `Run Extension` and click the green triangle (or press F5).
|
124
|
-
4. [`vscode-ruby-lsp`] Now VS Code will:
|
125
|
-
- Open another workspace as the `Extension Development Host`.
|
126
|
-
- Run `vscode-ruby-lsp` extension in debug mode, which will start a new `ruby-lsp` process with the `--debug` flag. Note that debugging is not available on Windows.
|
127
|
-
5. Open `ruby-lsp` in VS Code.
|
128
|
-
6. [`ruby-lsp`] Run `bin/rdbg -A` to connect to the running `ruby-lsp` process.
|
129
|
-
7. [`ruby-lsp`] Use commands like `b <file>:<line>` or `b Class#method` to set breakpoints and type `c` to continue the process.
|
130
|
-
8. In your `Extension Development Host` project (e.g. [`Tapioca`](https://github.com/Shopify/tapioca)), trigger the request that will hit the breakpoint.
|
131
|
-
|
132
|
-
### Spell Checking
|
133
|
-
|
134
|
-
VS Code users will be prompted to enable the [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) extension.
|
135
|
-
By default this will be enabled for all workspaces, but you can choose to selectively enable or disable it per workspace.
|
136
|
-
|
137
|
-
If you introduce a word which the spell checker does not recognize, you can add it to the `cspell.json` configuration alongside your PR.
|
55
|
+
If you wish to contribute, see [CONTRIBUTING](CONTRIBUTING.md) for development instructions and check out our pinned
|
56
|
+
[roadmap issue](https://github.com/Shopify/ruby-lsp/issues) for a list of tasks to get started.
|
138
57
|
|
139
58
|
## License
|
140
59
|
|
141
60
|
The gem is available as open source under the terms of the
|
142
|
-
[MIT License](
|
61
|
+
[MIT License](LICENSE.txt).
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.1
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "ruby_lsp/internal"
|
5
|
+
require "objspace"
|
6
|
+
|
7
|
+
module RubyLsp
|
8
|
+
# This rake task checks that all requests or extensions are fully documented. Add the rake task to your Rakefile and
|
9
|
+
# specify the absolute path for all files that must be required in order to discover all listeners
|
10
|
+
#
|
11
|
+
# # Rakefile
|
12
|
+
# request_files = FileList.new("#{__dir__}/lib/ruby_lsp/requests/*.rb") do |fl|
|
13
|
+
# fl.exclude(/base_request\.rb/)
|
14
|
+
# end
|
15
|
+
# RubyLsp::CheckDocs.new(request_files)
|
16
|
+
# # Run with bundle exec rake ruby_lsp:check_docs
|
17
|
+
class CheckDocs < Rake::TaskLib
|
18
|
+
extend T::Sig
|
19
|
+
|
20
|
+
sig { params(require_files: Rake::FileList).void }
|
21
|
+
def initialize(require_files)
|
22
|
+
super()
|
23
|
+
|
24
|
+
@name = T.let("ruby_lsp:check_docs", String)
|
25
|
+
@file_list = require_files
|
26
|
+
define_task
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
sig { void }
|
32
|
+
def define_task
|
33
|
+
desc("Checks if all Ruby LSP listeners are documented")
|
34
|
+
task(@name) { run_task }
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { void }
|
38
|
+
def run_task
|
39
|
+
# Require all files configured to make sure all listeners are loaded
|
40
|
+
@file_list.each { |f| require(f.delete_suffix(".rb")) }
|
41
|
+
|
42
|
+
# Find all classes that inherit from BaseRequest or Listener, which are the ones we want to make sure are
|
43
|
+
# documented
|
44
|
+
features = ObjectSpace.each_object(Class).filter_map do |k|
|
45
|
+
klass = T.cast(k, Class)
|
46
|
+
klass if klass < RubyLsp::Requests::BaseRequest || klass < RubyLsp::Listener
|
47
|
+
end
|
48
|
+
|
49
|
+
missing_docs = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[String, T::Array[String]])
|
50
|
+
|
51
|
+
features.each do |klass|
|
52
|
+
class_name = T.must(klass.name)
|
53
|
+
file_path, line_number = Module.const_source_location(class_name)
|
54
|
+
next unless file_path && line_number
|
55
|
+
|
56
|
+
# Adjust the line number to start searching right above the class definition
|
57
|
+
line_number -= 2
|
58
|
+
|
59
|
+
lines = File.readlines(file_path)
|
60
|
+
docs = []
|
61
|
+
|
62
|
+
# Extract the documentation on top of the listener constant
|
63
|
+
while (line = lines[line_number]&.strip) && line.start_with?("#")
|
64
|
+
docs.unshift(line)
|
65
|
+
line_number -= 1
|
66
|
+
end
|
67
|
+
|
68
|
+
documentation = docs.join("\n")
|
69
|
+
|
70
|
+
if docs.empty?
|
71
|
+
T.must(missing_docs[class_name]) << "No documentation found"
|
72
|
+
elsif !%r{\(https://microsoft.github.io/language-server-protocol/specification#.*\)}.match?(documentation)
|
73
|
+
T.must(missing_docs[class_name]) << <<~DOCS
|
74
|
+
Missing specification link. Requests and extensions should include a link to the LSP specification for the
|
75
|
+
related feature. For example:
|
76
|
+
|
77
|
+
[Inlay hint](https://microsoft.github.io/language-server-protocol/specification#textDocument_inlayHint)
|
78
|
+
DOCS
|
79
|
+
elsif !documentation.include?("# Example")
|
80
|
+
T.must(missing_docs[class_name]) << <<~DOCS
|
81
|
+
Missing example. Requests and extensions should include a code example that explains what the feature does.
|
82
|
+
|
83
|
+
# # Example
|
84
|
+
# ```ruby
|
85
|
+
# class Foo # <- information is shown here
|
86
|
+
# end
|
87
|
+
# ```
|
88
|
+
DOCS
|
89
|
+
elsif !/\[.* demo\]\(.*\.gif\)/.match?(documentation)
|
90
|
+
T.must(missing_docs[class_name]) << <<~DOCS
|
91
|
+
Missing demonstration GIF. Each request and extension must be documented with a GIF that shows the feature
|
92
|
+
working. For example:
|
93
|
+
|
94
|
+
# [Inlay hint demo](../../inlay_hint.gif)
|
95
|
+
DOCS
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if missing_docs.any?
|
100
|
+
warn(<<~WARN)
|
101
|
+
The following listeners are missing documentation:
|
102
|
+
|
103
|
+
#{missing_docs.map { |k, v| "#{k}\n\n#{v.join("\n")}" }.join("\n\n")}
|
104
|
+
WARN
|
105
|
+
|
106
|
+
abort
|
107
|
+
end
|
108
|
+
|
109
|
+
puts "All listeners are documented!"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/ruby_lsp/document.rb
CHANGED
@@ -23,7 +23,7 @@ module RubyLsp
|
|
23
23
|
|
24
24
|
sig { params(source: String, version: Integer, uri: String, encoding: String).void }
|
25
25
|
def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
|
26
|
-
@cache = T.let({}, T::Hash[
|
26
|
+
@cache = T.let({}, T::Hash[String, T.untyped])
|
27
27
|
@encoding = T.let(encoding, String)
|
28
28
|
@source = T.let(source, String)
|
29
29
|
@version = T.let(version, Integer)
|
@@ -40,10 +40,11 @@ module RubyLsp
|
|
40
40
|
@source == other.source
|
41
41
|
end
|
42
42
|
|
43
|
+
# TODO: remove this method once all nonpositional requests have been migrated to the listener pattern
|
43
44
|
sig do
|
44
45
|
type_parameters(:T)
|
45
46
|
.params(
|
46
|
-
request_name:
|
47
|
+
request_name: String,
|
47
48
|
block: T.proc.params(document: Document).returns(T.type_parameter(:T)),
|
48
49
|
).returns(T.type_parameter(:T))
|
49
50
|
end
|
@@ -56,6 +57,16 @@ module RubyLsp
|
|
56
57
|
result
|
57
58
|
end
|
58
59
|
|
60
|
+
sig { type_parameters(:T).params(request_name: String, value: T.type_parameter(:T)).returns(T.type_parameter(:T)) }
|
61
|
+
def cache_set(request_name, value)
|
62
|
+
@cache[request_name] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
sig { params(request_name: String).returns(T.untyped) }
|
66
|
+
def cache_get(request_name)
|
67
|
+
@cache[request_name]
|
68
|
+
end
|
69
|
+
|
59
70
|
sig { params(edits: T::Array[EditShape], version: Integer).void }
|
60
71
|
def push_edits(edits, version:)
|
61
72
|
edits.each do |edit|
|
@@ -13,42 +13,108 @@ module RubyLsp
|
|
13
13
|
#
|
14
14
|
# ```ruby
|
15
15
|
# target_node = document.locate_node(position)
|
16
|
-
#
|
17
|
-
#
|
16
|
+
# emitter = EventEmitter.new
|
17
|
+
# listener = Requests::Hover.new(emitter, @message_queue)
|
18
|
+
# emitter.emit_for_target(target_node)
|
18
19
|
# listener.response
|
19
20
|
# ```
|
20
21
|
class EventEmitter < SyntaxTree::Visitor
|
21
22
|
extend T::Sig
|
22
23
|
|
23
|
-
sig {
|
24
|
-
def initialize
|
25
|
-
@listeners =
|
26
|
-
|
27
|
-
# Create a map of event name to listeners that have registered it, so that we avoid unnecessary invocations
|
28
|
-
@event_to_listener_map = T.let(
|
29
|
-
listeners.each_with_object(Hash.new { |h, k| h[k] = [] }) do |listener, hash|
|
30
|
-
listener.class.events&.each { |event| hash[event] << listener }
|
31
|
-
end,
|
32
|
-
T::Hash[Symbol, T::Array[Listener[T.untyped]]],
|
33
|
-
)
|
34
|
-
|
24
|
+
sig { void }
|
25
|
+
def initialize
|
26
|
+
@listeners = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[Symbol, T::Array[Listener[T.untyped]]])
|
35
27
|
super()
|
36
28
|
end
|
37
29
|
|
30
|
+
sig { params(listener: Listener[T.untyped], events: Symbol).void }
|
31
|
+
def register(listener, *events)
|
32
|
+
events.each { |event| T.must(@listeners[event]) << listener }
|
33
|
+
end
|
34
|
+
|
38
35
|
# Emit events for a specific node. This is similar to the regular `visit` method, but avoids going deeper into the
|
39
36
|
# tree for performance
|
40
37
|
sig { params(node: T.nilable(SyntaxTree::Node)).void }
|
41
38
|
def emit_for_target(node)
|
42
39
|
case node
|
43
40
|
when SyntaxTree::Command
|
44
|
-
@
|
41
|
+
@listeners[:on_command]&.each { |l| T.unsafe(l).on_command(node) }
|
45
42
|
when SyntaxTree::CallNode
|
46
|
-
@
|
43
|
+
@listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
|
44
|
+
when SyntaxTree::TStringContent
|
45
|
+
@listeners[:on_tstring_content]&.each { |l| T.unsafe(l).on_tstring_content(node) }
|
47
46
|
when SyntaxTree::ConstPathRef
|
48
|
-
@
|
47
|
+
@listeners[:on_const_path_ref]&.each { |l| T.unsafe(l).on_const_path_ref(node) }
|
49
48
|
when SyntaxTree::Const
|
50
|
-
@
|
49
|
+
@listeners[:on_const]&.each { |l| T.unsafe(l).on_const(node) }
|
51
50
|
end
|
52
51
|
end
|
52
|
+
|
53
|
+
# Visit dispatchers are below. Notice that for nodes that create a new scope (e.g.: classes, modules, method defs)
|
54
|
+
# we need both an `on_*` and `after_*` event. This is because some requests must know when we exit the scope
|
55
|
+
sig { override.params(node: SyntaxTree::ClassDeclaration).void }
|
56
|
+
def visit_class(node)
|
57
|
+
@listeners[:on_class]&.each { |l| T.unsafe(l).on_class(node) }
|
58
|
+
super
|
59
|
+
@listeners[:after_class]&.each { |l| T.unsafe(l).after_class(node) }
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
|
63
|
+
def visit_module(node)
|
64
|
+
@listeners[:on_module]&.each { |l| T.unsafe(l).on_module(node) }
|
65
|
+
super
|
66
|
+
@listeners[:after_module]&.each { |l| T.unsafe(l).after_module(node) }
|
67
|
+
end
|
68
|
+
|
69
|
+
sig { override.params(node: SyntaxTree::Command).void }
|
70
|
+
def visit_command(node)
|
71
|
+
@listeners[:on_command]&.each { |l| T.unsafe(l).on_command(node) }
|
72
|
+
super
|
73
|
+
@listeners[:after_command]&.each { |l| T.unsafe(l).after_command(node) }
|
74
|
+
end
|
75
|
+
|
76
|
+
sig { override.params(node: SyntaxTree::CallNode).void }
|
77
|
+
def visit_call(node)
|
78
|
+
@listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
|
79
|
+
super
|
80
|
+
@listeners[:after_call]&.each { |l| T.unsafe(l).after_call(node) }
|
81
|
+
end
|
82
|
+
|
83
|
+
sig { override.params(node: SyntaxTree::VCall).void }
|
84
|
+
def visit_vcall(node)
|
85
|
+
@listeners[:on_vcall]&.each { |l| T.unsafe(l).on_vcall(node) }
|
86
|
+
super
|
87
|
+
end
|
88
|
+
|
89
|
+
sig { override.params(node: SyntaxTree::ConstPathField).void }
|
90
|
+
def visit_const_path_field(node)
|
91
|
+
@listeners[:on_const_path_field]&.each { |l| T.unsafe(l).on_const_path_field(node) }
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
sig { override.params(node: SyntaxTree::TopConstField).void }
|
96
|
+
def visit_top_const_field(node)
|
97
|
+
@listeners[:on_top_const_field]&.each { |l| T.unsafe(l).on_top_const_field(node) }
|
98
|
+
super
|
99
|
+
end
|
100
|
+
|
101
|
+
sig { override.params(node: SyntaxTree::DefNode).void }
|
102
|
+
def visit_def(node)
|
103
|
+
@listeners[:on_def]&.each { |l| T.unsafe(l).on_def(node) }
|
104
|
+
super
|
105
|
+
@listeners[:after_def]&.each { |l| T.unsafe(l).after_def(node) }
|
106
|
+
end
|
107
|
+
|
108
|
+
sig { override.params(node: SyntaxTree::VarField).void }
|
109
|
+
def visit_var_field(node)
|
110
|
+
@listeners[:on_var_field]&.each { |l| T.unsafe(l).on_var_field(node) }
|
111
|
+
super
|
112
|
+
end
|
113
|
+
|
114
|
+
sig { override.params(node: SyntaxTree::Comment).void }
|
115
|
+
def visit_comment(node)
|
116
|
+
@listeners[:on_comment]&.each { |l| T.unsafe(l).on_comment(node) }
|
117
|
+
super
|
118
|
+
end
|
53
119
|
end
|
54
120
|
end
|