@@ -9,65 +9,101 @@ module Requests
99 class TypeHierarchySupertypes < Request
1010 include Support ::Common
1111
12- #: (RubyIndexer::Index index , Hash[Symbol, untyped] item ) -> void
13- def initialize ( index , item )
12+ #: (GlobalState , Hash[Symbol, untyped]) -> void
13+ def initialize ( global_state , item )
1414 super ( )
1515
16- @index = index
16+ @graph = global_state . graph #: Rubydex::Graph
1717 @item = item
1818 end
1919
2020 # @override
2121 #: -> Array[Interface::TypeHierarchyItem]?
2222 def perform
23- name = @item [ :name ]
24- entries = @index [ name ]
25-
26- parents = Set . new #: Set[RubyIndexer::Entry::Namespace]
27- return unless entries &.any?
28-
29- entries . each do |entry |
30- next unless entry . is_a? ( RubyIndexer ::Entry ::Namespace )
31-
32- if entry . is_a? ( RubyIndexer ::Entry ::Class )
33- parent_class_name = entry . parent_class
34- if parent_class_name
35- resolved_parent_entries = @index . resolve ( parent_class_name , entry . nesting )
36- resolved_parent_entries &.each do |entry |
37- next unless entry . is_a? ( RubyIndexer ::Entry ::Class )
38-
39- parents << entry
40- end
41- end
42- end
43-
44- entry . mixin_operations . each do |mixin_operation |
45- mixin_name = mixin_operation . module_name
46- resolved_mixin_entries = @index . resolve ( mixin_name , entry . nesting )
47- next unless resolved_mixin_entries
48-
49- resolved_mixin_entries . each do |mixin_entry |
50- next unless mixin_entry . is_a? ( RubyIndexer ::Entry ::Module )
51-
52- parents << mixin_entry
53- end
54- end
55- end
23+ fully_qualified_name = @item . dig ( :data , :fully_qualified_name ) || @item [ :name ] #: String?
24+ return unless fully_qualified_name
25+
26+ declaration = @graph [ fully_qualified_name ]
27+ return unless declaration . is_a? ( Rubydex ::Namespace )
5628
57- parents . map { |entry | hierarchy_item ( entry ) }
29+ compute_supertypes ( declaration ) . filter_map { |name , backing | hierarchy_item ( name , backing ) }
5830 end
5931
6032 private
6133
62- #: (RubyIndexer::Entry entry) -> Interface::TypeHierarchyItem
63- def hierarchy_item ( entry )
64- Interface ::TypeHierarchyItem . new (
65- name : entry . name ,
66- kind : kind_for_entry ( entry ) ,
67- uri : entry . uri . to_s ,
68- range : range_from_location ( entry . location ) ,
69- selection_range : range_from_location ( entry . name_location ) ,
70- detail : entry . file_name ,
34+ # Returns an array of `[display_name, backing_declaration]` pairs. `display_name` is the name shown in the type
35+ # hierarchy item (which may be a synthesized singleton class name like `Object::<Object>`). `backing_declaration`
36+ # is the namespace whose primary definition provides the location for the hierarchy item — it may differ from the
37+ # display name when the singleton class is implicit and has no definitions of its own, in which case we fall back
38+ # to the attached object's definition so the user still lands somewhere useful.
39+ #
40+ #: (Rubydex::Namespace) -> Array[[String, Rubydex::Namespace]]
41+ def compute_supertypes ( declaration )
42+ case declaration
43+ when Rubydex ::SingletonClass
44+ singleton_supertypes ( declaration )
45+ when Rubydex ::Class
46+ class_supertypes ( declaration )
47+ else
48+ explicit_supertypes ( declaration )
49+ end
50+ end
51+
52+ #: (Rubydex::Class) -> Array[[String, Rubydex::Namespace]]
53+ def class_supertypes ( declaration )
54+ # `BasicObject` is the root of the Ruby class hierarchy
55+ supertypes = explicit_supertypes ( declaration )
56+ return supertypes if declaration . name == "BasicObject"
57+
58+ # If the class has any superclass reference (resolved or unresolved), don't re-add the implicit `Object`.
59+ has_superclass = declaration . definitions . any? do |d |
60+ d . is_a? ( Rubydex ::ClassDefinition ) && !d . superclass . nil?
61+ end
62+ return supertypes if has_superclass
63+
64+ object = @graph [ "Object" ] #: as Rubydex::Namespace
65+ supertypes << [ "Object" , object ]
66+ supertypes
67+ end
68+
69+ #: (Rubydex::Namespace) -> Array[[String, Rubydex::Namespace]]
70+ def explicit_supertypes ( declaration )
71+ declaration . direct_supertypes . map { |s | [ s . name , s ] }
72+ end
73+
74+ # Singleton classes don't have their own superclass references. Their direct supertype is the singleton class of
75+ # the attached object's superclass, computed recursively so that nested singleton classes (e.g.
76+ # `Foo::<Foo>::<<Foo>>`) still resolve to the matching depth on the parent chain. When the synthesized singleton
77+ # class name has no backing declaration with definitions (implicit singleton), we fall back to the attached
78+ # supertype's backing so the user is still navigated to a meaningful location.
79+ #
80+ #: (Rubydex::SingletonClass) -> Array[[String, Rubydex::Namespace]]
81+ def singleton_supertypes ( declaration )
82+ attached = declaration . owner
83+ return [ ] unless attached . is_a? ( Rubydex ::Namespace )
84+
85+ compute_supertypes ( attached ) . map do |parent_name , parent_backing |
86+ singleton_name = singleton_name_of ( parent_name )
87+ found = @graph [ singleton_name ]
88+ backing = found . is_a? ( Rubydex ::Namespace ) && found . definitions . any? ? found : parent_backing
89+ [ singleton_name , backing ]
90+ end
91+ end
92+
93+ #: (String) -> String
94+ def singleton_name_of ( name )
95+ unqualified = name . split ( "::" ) . last || name
96+ "#{ name } ::<#{ unqualified } >"
97+ end
98+
99+ #: (String, Rubydex::Namespace) -> Interface::TypeHierarchyItem?
100+ def hierarchy_item ( name , declaration )
101+ primary = declaration . definitions . first #: Rubydex::Definition?
102+ return unless primary
103+
104+ primary . to_lsp_type_hierarchy_item (
105+ name ,
106+ detail : declaration . lsp_type_hierarchy_detail ,
71107 )
72108 end
73109 end
0 commit comments