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