module RBS
class Locator
attr_reader :decls
def initialize(decls:)
@decls = decls
end
def buffer
decls[0].location&.buffer or raise
end
def find(line:, column:)
pos = buffer.loc_to_pos([line, column])
decls.each do |decl|
array = []
find_in_decl(pos, decl: decl, array: array) and return array
end
[]
end
def find2(line:, column:)
path = find(line: line, column: column)
return if path.empty?
hd, *tl = path
if hd.is_a?(Symbol)
[hd, tl]
else
[nil, path]
end
end
def find_in_decl(pos, decl:, array:)
if test_loc(pos, location: decl.location)
array.unshift(decl)
case decl
when AST::Declarations::Class
decl.type_params.each do |param|
if test_loc(pos, location: param.location)
array.unshift(param)
find_in_loc(pos, array: array, location: param.location)
return true
end
end
if super_class = decl.super_class
if test_loc(pos, location: super_class.location)
array.unshift(super_class)
find_in_loc(pos, array: array, location: super_class.location)
return true
end
end
decl.each_decl do |decl_|
find_in_decl(pos, decl: decl_, array: array) and return true
end
decl.each_member do |member|
find_in_member(pos, array: array, member: member) and return true
end
when AST::Declarations::Module
decl.type_params.each do |param|
if test_loc(pos, location: param.location)
array.unshift(param)
find_in_loc(pos, array: array, location: param.location)
return true
end
end
decl.self_types.each do |self_type|
if test_loc(pos, location: self_type.location)
array.unshift(self_type)
find_in_loc(pos, array: array, location: self_type.location)
return true
end
end
decl.each_decl do |decl_|
find_in_decl(pos, decl: decl_, array: array) and return true
end
decl.each_member do |member|
find_in_member(pos, array: array, member: member) and return true
end
when AST::Declarations::Interface
decl.type_params.each do |param|
if test_loc(pos, location: param.location)
array.unshift(param)
find_in_loc(pos, array: array, location: param.location)
return true
end
end
decl.members.each do |member|
find_in_member(pos, array: array, member: member) and return true
end
when AST::Declarations::Constant, AST::Declarations::Global
find_in_type(pos, array: array, type: decl.type) and return true
when AST::Declarations::Alias
find_in_type(pos, array: array, type: decl.type) and return true
end
find_in_loc(pos, location: decl.location, array: array)
true
else
false
end
end
def find_in_member(pos, member:, array:)
if test_loc(pos, location: member.location)
array.unshift(member)
case member
when AST::Members::MethodDefinition
member.types.each do |method_type|
find_in_method_type(pos, array: array, method_type: method_type) and return true
end
when AST::Members::InstanceVariable, AST::Members::ClassInstanceVariable, AST::Members::ClassVariable
find_in_type(pos, array: array, type: member.type) and return true
when AST::Members::AttrReader, AST::Members::AttrWriter, AST::Members::AttrAccessor
find_in_type(pos, array: array, type: member.type) and return true
end
find_in_loc(pos, location: member.location, array: array)
true
else
false
end
end
def find_in_method_type(pos, method_type:, array:)
if test_loc(pos, location: method_type.location)
array.unshift(method_type)
method_type.each_type do |type|
find_in_type(pos, array: array, type: type) and break
end
true
else
false
end
end
def find_in_type(pos, type:, array:)
if test_loc(pos, location: type.location)
array.unshift(type)
type.each_type do |type_|
find_in_type(pos, array: array, type: type_) and return true
end
find_in_loc(pos, array: array, location: type.location)
true
else
false
end
end
def find_in_loc(pos, location:, array:)
if test_loc(pos, location: location)
if location.is_a?(Location::WithChildren)
location.optional_children.each do |key, range|
if range === pos
array.unshift(key)
return true
end
end
location.required_children.each do |key, range|
if range === pos
array.unshift(key)
return true
end
end
end
true
else
false
end
end
def test_loc(pos, location:)
if location
location.range === pos
else
false
end
end
end
end