lib/ruby_lsp/event_emitter.rb in ruby-lsp-0.4.5 vs lib/ruby_lsp/event_emitter.rb in ruby-lsp-0.5.0
- old
+ new
@@ -11,44 +11,110 @@
#
# # Example
#
# ```ruby
# target_node = document.locate_node(position)
- # listener = Requests::Hover.new
- # EventEmitter.new(listener).emit_for_target(target_node)
+ # emitter = EventEmitter.new
+ # listener = Requests::Hover.new(emitter, @message_queue)
+ # emitter.emit_for_target(target_node)
# listener.response
# ```
class EventEmitter < SyntaxTree::Visitor
extend T::Sig
- sig { params(listeners: Listener[T.untyped]).void }
- def initialize(*listeners)
- @listeners = listeners
-
- # Create a map of event name to listeners that have registered it, so that we avoid unnecessary invocations
- @event_to_listener_map = T.let(
- listeners.each_with_object(Hash.new { |h, k| h[k] = [] }) do |listener, hash|
- listener.class.events&.each { |event| hash[event] << listener }
- end,
- T::Hash[Symbol, T::Array[Listener[T.untyped]]],
- )
-
+ sig { void }
+ def initialize
+ @listeners = T.let(Hash.new { |h, k| h[k] = [] }, T::Hash[Symbol, T::Array[Listener[T.untyped]]])
super()
end
+ sig { params(listener: Listener[T.untyped], events: Symbol).void }
+ def register(listener, *events)
+ events.each { |event| T.must(@listeners[event]) << listener }
+ end
+
# Emit events for a specific node. This is similar to the regular `visit` method, but avoids going deeper into the
# tree for performance
sig { params(node: T.nilable(SyntaxTree::Node)).void }
def emit_for_target(node)
case node
when SyntaxTree::Command
- @event_to_listener_map[:on_command]&.each { |listener| T.unsafe(listener).on_command(node) }
+ @listeners[:on_command]&.each { |l| T.unsafe(l).on_command(node) }
when SyntaxTree::CallNode
- @event_to_listener_map[:on_call]&.each { |listener| T.unsafe(listener).on_call(node) }
+ @listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
+ when SyntaxTree::TStringContent
+ @listeners[:on_tstring_content]&.each { |l| T.unsafe(l).on_tstring_content(node) }
when SyntaxTree::ConstPathRef
- @event_to_listener_map[:on_const_path_ref]&.each { |listener| T.unsafe(listener).on_const_path_ref(node) }
+ @listeners[:on_const_path_ref]&.each { |l| T.unsafe(l).on_const_path_ref(node) }
when SyntaxTree::Const
- @event_to_listener_map[:on_const]&.each { |listener| T.unsafe(listener).on_const(node) }
+ @listeners[:on_const]&.each { |l| T.unsafe(l).on_const(node) }
end
+ end
+
+ # Visit dispatchers are below. Notice that for nodes that create a new scope (e.g.: classes, modules, method defs)
+ # we need both an `on_*` and `after_*` event. This is because some requests must know when we exit the scope
+ sig { override.params(node: SyntaxTree::ClassDeclaration).void }
+ def visit_class(node)
+ @listeners[:on_class]&.each { |l| T.unsafe(l).on_class(node) }
+ super
+ @listeners[:after_class]&.each { |l| T.unsafe(l).after_class(node) }
+ end
+
+ sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
+ def visit_module(node)
+ @listeners[:on_module]&.each { |l| T.unsafe(l).on_module(node) }
+ super
+ @listeners[:after_module]&.each { |l| T.unsafe(l).after_module(node) }
+ end
+
+ sig { override.params(node: SyntaxTree::Command).void }
+ def visit_command(node)
+ @listeners[:on_command]&.each { |l| T.unsafe(l).on_command(node) }
+ super
+ @listeners[:after_command]&.each { |l| T.unsafe(l).after_command(node) }
+ end
+
+ sig { override.params(node: SyntaxTree::CallNode).void }
+ def visit_call(node)
+ @listeners[:on_call]&.each { |l| T.unsafe(l).on_call(node) }
+ super
+ @listeners[:after_call]&.each { |l| T.unsafe(l).after_call(node) }
+ end
+
+ sig { override.params(node: SyntaxTree::VCall).void }
+ def visit_vcall(node)
+ @listeners[:on_vcall]&.each { |l| T.unsafe(l).on_vcall(node) }
+ super
+ end
+
+ sig { override.params(node: SyntaxTree::ConstPathField).void }
+ def visit_const_path_field(node)
+ @listeners[:on_const_path_field]&.each { |l| T.unsafe(l).on_const_path_field(node) }
+ super
+ end
+
+ sig { override.params(node: SyntaxTree::TopConstField).void }
+ def visit_top_const_field(node)
+ @listeners[:on_top_const_field]&.each { |l| T.unsafe(l).on_top_const_field(node) }
+ super
+ end
+
+ sig { override.params(node: SyntaxTree::DefNode).void }
+ def visit_def(node)
+ @listeners[:on_def]&.each { |l| T.unsafe(l).on_def(node) }
+ super
+ @listeners[:after_def]&.each { |l| T.unsafe(l).after_def(node) }
+ end
+
+ sig { override.params(node: SyntaxTree::VarField).void }
+ def visit_var_field(node)
+ @listeners[:on_var_field]&.each { |l| T.unsafe(l).on_var_field(node) }
+ super
+ end
+
+ sig { override.params(node: SyntaxTree::Comment).void }
+ def visit_comment(node)
+ @listeners[:on_comment]&.each { |l| T.unsafe(l).on_comment(node) }
+ super
end
end
end