# typed: strict # frozen_string_literal: true module RubyLsp module Requests # :nodoc: class Request extend T::Sig extend T::Generic class InvalidFormatter < StandardError; end abstract! sig { abstract.returns(T.anything) } def perform; end private # Checks if a location covers a position sig { params(location: Prism::Location, position: T.untyped).returns(T::Boolean) } def cover?(location, position) start_covered = location.start_line - 1 < position[:line] || ( location.start_line - 1 == position[:line] && location.start_column <= position[:character] ) end_covered = location.end_line - 1 > position[:line] || ( location.end_line - 1 == position[:line] && location.end_column >= position[:character] ) start_covered && end_covered end # Based on a constant node target, a constant path node parent and a position, this method will find the exact # portion of the constant path that matches the requested position, for higher precision in hover and # definition. For example: # # ```ruby # Foo::Bar::Baz # # ^ Going to definition here should go to Foo::Bar::Baz # # ^ Going to definition here should go to Foo::Bar # #^ Going to definition here should go to Foo # ``` sig do params( target: Prism::Node, parent: Prism::Node, position: T::Hash[Symbol, Integer], ).returns(Prism::Node) end def determine_target(target, parent, position) return target unless parent.is_a?(Prism::ConstantPathNode) target = T.let(parent, Prism::Node) parent = T.let(T.cast(target, Prism::ConstantPathNode).parent, T.nilable(Prism::Node)) while parent && cover?(parent.location, position) target = parent parent = target.is_a?(Prism::ConstantPathNode) ? target.parent : nil end target end # Checks if a given location covers the position requested sig { params(location: T.nilable(Prism::Location), position: T::Hash[Symbol, T.untyped]).returns(T::Boolean) } def covers_position?(location, position) return false unless location start_line = location.start_line - 1 end_line = location.end_line - 1 line = position[:line] character = position[:character] (start_line < line || (start_line == line && location.start_column <= character)) && (end_line > line || (end_line == line && location.end_column >= character)) end end end end