class RubyLsp::Requests::Hover

: [ResponseType = Interface::Hover?]
displays the documentation for the symbol currently under the cursor.
The [hover request](microsoft.github.io/language-server-protocol/specification#textDocument_hover)

def initialize(document, global_state, position, dispatcher, sorbet_level)

: ((RubyDocument | ERBDocument) document, GlobalState global_state, Hash[Symbol, untyped] position, Prism::Dispatcher dispatcher, SorbetLevel sorbet_level) -> void
def initialize(document, global_state, position, dispatcher, sorbet_level)
  super()
  char_position, _ = document.find_index_by_position(position)
  delegate_request_if_needed!(global_state, document, char_position)
  node_context = RubyDocument.locate(
    document.ast,
    char_position,
    node_types: Listeners::Hover::ALLOWED_TARGETS,
    code_units_cache: document.code_units_cache,
  )
  target = node_context.node
  parent = node_context.parent
  if should_refine_target?(parent, target)
    target = determine_target(
      target, #: as !nil
      parent, #: as !nil
      position,
    )
  elsif position_outside_target?(position, target)
    target = nil
  end
  # Don't need to instantiate any listeners if there's no target
  return unless target
  @target = target #: Prism::Node?
  uri = document.uri
  @response_builder = ResponseBuilders::Hover.new #: ResponseBuilders::Hover
  Listeners::Hover.new(@response_builder, global_state, uri, node_context, dispatcher, sorbet_level)
  Addon.addons.each do |addon|
    addon.create_hover_listener(@response_builder, node_context, dispatcher)
  end
  @dispatcher = dispatcher
end

def perform

: -> ResponseType
@override
def perform
  return unless @target
  @dispatcher.dispatch_once(@target)
  return if @response_builder.empty?
  Interface::Hover.new(
    contents: Interface::MarkupContent.new(
      kind: "markdown",
      value: @response_builder.response,
    ),
  )
end

def position_outside_target?(position, target)

: (Hash[Symbol, untyped] position, Prism::Node? target) -> bool
def position_outside_target?(position, target)
  case target
  when Prism::GlobalVariableAndWriteNode,
    Prism::GlobalVariableOperatorWriteNode,
    Prism::GlobalVariableOrWriteNode,
    Prism::GlobalVariableWriteNode
    !covers_position?(target.name_loc, position)
  when Prism::CallNode
    !covers_position?(target.message_loc, position)
  else
    false
  end
end

def provider

: -> Interface::HoverOptions
def provider
  Interface::HoverOptions.new
end

def should_refine_target?(parent, target)

: (Prism::Node? parent, Prism::Node? target) -> bool
def should_refine_target?(parent, target)
  (Listeners::Hover::ALLOWED_TARGETS.include?(parent.class) &&
  !Listeners::Hover::ALLOWED_TARGETS.include?(target.class)) ||
    (parent.is_a?(Prism::ConstantPathNode) && target.is_a?(Prism::ConstantReadNode))
end