solargraph 0.46.0 → 0.54.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (279) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/plugins.yml +40 -0
  4. data/.github/workflows/rspec.yml +37 -41
  5. data/.github/workflows/typecheck.yml +34 -0
  6. data/.gitignore +9 -9
  7. data/.rspec +2 -2
  8. data/.yardopts +2 -2
  9. data/CHANGELOG.md +1338 -1115
  10. data/Gemfile +0 -0
  11. data/LICENSE +1 -1
  12. data/README.md +131 -128
  13. data/Rakefile +0 -0
  14. data/SPONSORS.md +10 -18
  15. data/bin/solargraph +0 -0
  16. data/lib/solargraph/api_map/cache.rb +109 -70
  17. data/lib/solargraph/api_map/index.rb +167 -0
  18. data/lib/solargraph/api_map/source_to_yard.rb +88 -81
  19. data/lib/solargraph/api_map/store.rb +260 -256
  20. data/lib/solargraph/api_map.rb +870 -686
  21. data/lib/solargraph/bench.rb +44 -27
  22. data/lib/solargraph/cache.rb +77 -0
  23. data/lib/solargraph/complex_type/type_methods.rb +217 -130
  24. data/lib/solargraph/complex_type/unique_type.rb +386 -75
  25. data/lib/solargraph/complex_type.rb +394 -221
  26. data/lib/solargraph/convention/base.rb +33 -33
  27. data/lib/solargraph/convention/gemfile.rb +15 -15
  28. data/lib/solargraph/convention/gemspec.rb +22 -22
  29. data/lib/solargraph/convention/rakefile.rb +17 -0
  30. data/lib/solargraph/convention.rb +47 -47
  31. data/lib/solargraph/converters/dd.rb +17 -12
  32. data/lib/solargraph/converters/dl.rb +15 -12
  33. data/lib/solargraph/converters/dt.rb +15 -12
  34. data/lib/solargraph/converters/misc.rb +1 -1
  35. data/lib/solargraph/diagnostics/base.rb +29 -29
  36. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  37. data/lib/solargraph/diagnostics/rubocop.rb +113 -98
  38. data/lib/solargraph/diagnostics/rubocop_helpers.rb +66 -63
  39. data/lib/solargraph/diagnostics/severities.rb +15 -15
  40. data/lib/solargraph/diagnostics/type_check.rb +55 -54
  41. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  42. data/lib/solargraph/diagnostics.rb +55 -55
  43. data/lib/solargraph/doc_map.rb +188 -0
  44. data/lib/solargraph/environ.rb +45 -45
  45. data/lib/solargraph/equality.rb +33 -0
  46. data/lib/solargraph/gem_pins.rb +72 -0
  47. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -35
  48. data/lib/solargraph/language_server/error_codes.rb +20 -20
  49. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  50. data/lib/solargraph/language_server/host/dispatch.rb +128 -111
  51. data/lib/solargraph/language_server/host/message_worker.rb +106 -59
  52. data/lib/solargraph/language_server/host/sources.rb +99 -156
  53. data/lib/solargraph/language_server/host.rb +861 -865
  54. data/lib/solargraph/language_server/message/base.rb +96 -89
  55. data/lib/solargraph/language_server/message/cancel_request.rb +13 -13
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  57. data/lib/solargraph/language_server/message/client.rb +11 -11
  58. data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -58
  59. data/lib/solargraph/language_server/message/completion_item.rb +11 -11
  60. data/lib/solargraph/language_server/message/exit_notification.rb +13 -13
  61. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +112 -100
  62. data/lib/solargraph/language_server/message/extended/document.rb +20 -20
  63. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  64. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -23
  65. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  66. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  67. data/lib/solargraph/language_server/message/extended.rb +21 -21
  68. data/lib/solargraph/language_server/message/initialize.rb +191 -162
  69. data/lib/solargraph/language_server/message/initialized.rb +28 -27
  70. data/lib/solargraph/language_server/message/method_not_found.rb +16 -16
  71. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -14
  72. data/lib/solargraph/language_server/message/shutdown.rb +13 -13
  73. data/lib/solargraph/language_server/message/text_document/base.rb +19 -19
  74. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -17
  75. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -59
  76. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -38
  77. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -15
  78. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -15
  79. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -15
  80. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -17
  81. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  82. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -23
  83. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -26
  84. data/lib/solargraph/language_server/message/text_document/formatting.rb +131 -126
  85. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -54
  86. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -34
  87. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  88. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  89. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  90. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -29
  91. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  92. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  93. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -30
  94. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -33
  95. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -24
  96. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  97. data/lib/solargraph/language_server/message/workspace.rb +14 -14
  98. data/lib/solargraph/language_server/message.rb +94 -93
  99. data/lib/solargraph/language_server/message_types.rb +14 -14
  100. data/lib/solargraph/language_server/progress.rb +135 -0
  101. data/lib/solargraph/language_server/request.rb +24 -24
  102. data/lib/solargraph/language_server/symbol_kinds.rb +36 -36
  103. data/lib/solargraph/language_server/transport/adapter.rb +68 -53
  104. data/lib/solargraph/language_server/transport/data_reader.rb +74 -72
  105. data/lib/solargraph/language_server/transport.rb +13 -13
  106. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  107. data/lib/solargraph/language_server.rb +20 -19
  108. data/lib/solargraph/library.rb +663 -546
  109. data/lib/solargraph/location.rb +58 -37
  110. data/lib/solargraph/logging.rb +27 -27
  111. data/lib/solargraph/page.rb +89 -83
  112. data/lib/solargraph/parser/comment_ripper.rb +56 -52
  113. data/lib/solargraph/parser/node_methods.rb +83 -43
  114. data/lib/solargraph/parser/node_processor/base.rb +87 -77
  115. data/lib/solargraph/parser/node_processor.rb +45 -43
  116. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +153 -135
  117. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +18 -16
  118. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +164 -148
  119. data/lib/solargraph/parser/parser_gem/node_methods.rb +495 -0
  120. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +23 -23
  121. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +57 -0
  122. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +15 -15
  123. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +43 -42
  124. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +35 -25
  125. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +23 -23
  126. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +50 -63
  127. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +36 -36
  128. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +23 -23
  129. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +38 -38
  130. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +28 -28
  131. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +53 -0
  132. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +39 -39
  133. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +16 -16
  134. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +36 -36
  135. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
  136. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +259 -257
  137. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +18 -18
  138. data/lib/solargraph/parser/parser_gem/node_processors.rb +56 -0
  139. data/lib/solargraph/parser/parser_gem.rb +12 -0
  140. data/lib/solargraph/parser/region.rb +66 -66
  141. data/lib/solargraph/parser/snippet.rb +15 -13
  142. data/lib/solargraph/parser.rb +22 -26
  143. data/lib/solargraph/pin/base.rb +378 -296
  144. data/lib/solargraph/pin/base_variable.rb +118 -84
  145. data/lib/solargraph/pin/block.rb +101 -72
  146. data/lib/solargraph/pin/callable.rb +147 -0
  147. data/lib/solargraph/pin/class_variable.rb +8 -8
  148. data/lib/solargraph/pin/closure.rb +57 -37
  149. data/lib/solargraph/pin/common.rb +70 -70
  150. data/lib/solargraph/pin/constant.rb +43 -43
  151. data/lib/solargraph/pin/conversions.rb +123 -96
  152. data/lib/solargraph/pin/delegated_method.rb +101 -0
  153. data/lib/solargraph/pin/documenting.rb +98 -105
  154. data/lib/solargraph/pin/duck_method.rb +16 -16
  155. data/lib/solargraph/pin/global_variable.rb +8 -8
  156. data/lib/solargraph/pin/instance_variable.rb +34 -30
  157. data/lib/solargraph/pin/keyword.rb +15 -15
  158. data/lib/solargraph/pin/keyword_param.rb +8 -8
  159. data/lib/solargraph/pin/local_variable.rb +67 -55
  160. data/lib/solargraph/pin/method.rb +527 -245
  161. data/lib/solargraph/pin/method_alias.rb +31 -31
  162. data/lib/solargraph/pin/namespace.rb +107 -91
  163. data/lib/solargraph/pin/parameter.rb +212 -201
  164. data/lib/solargraph/pin/proxy_type.rb +29 -29
  165. data/lib/solargraph/pin/reference/extend.rb +10 -10
  166. data/lib/solargraph/pin/reference/include.rb +10 -10
  167. data/lib/solargraph/pin/reference/override.rb +29 -29
  168. data/lib/solargraph/pin/reference/prepend.rb +10 -10
  169. data/lib/solargraph/pin/reference/require.rb +14 -14
  170. data/lib/solargraph/pin/reference/superclass.rb +10 -10
  171. data/lib/solargraph/pin/reference.rb +22 -14
  172. data/lib/solargraph/pin/search.rb +56 -56
  173. data/lib/solargraph/pin/signature.rb +17 -0
  174. data/lib/solargraph/pin/singleton.rb +11 -11
  175. data/lib/solargraph/pin/symbol.rb +47 -47
  176. data/lib/solargraph/pin.rb +41 -37
  177. data/lib/solargraph/position.rb +107 -100
  178. data/lib/solargraph/range.rb +98 -95
  179. data/lib/solargraph/rbs_map/conversions.rb +646 -0
  180. data/lib/solargraph/rbs_map/core_fills.rb +50 -0
  181. data/lib/solargraph/rbs_map/core_map.rb +28 -0
  182. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
  183. data/lib/solargraph/rbs_map.rb +93 -0
  184. data/lib/solargraph/server_methods.rb +16 -16
  185. data/lib/solargraph/shell.rb +269 -226
  186. data/lib/solargraph/source/chain/array.rb +33 -0
  187. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  188. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  189. data/lib/solargraph/source/chain/call.rb +303 -204
  190. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  191. data/lib/solargraph/source/chain/constant.rb +89 -75
  192. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  193. data/lib/solargraph/source/chain/hash.rb +33 -28
  194. data/lib/solargraph/source/chain/head.rb +19 -19
  195. data/lib/solargraph/source/chain/if.rb +28 -0
  196. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  197. data/lib/solargraph/source/chain/link.rb +98 -71
  198. data/lib/solargraph/source/chain/literal.rb +28 -23
  199. data/lib/solargraph/source/chain/or.rb +23 -23
  200. data/lib/solargraph/source/chain/q_call.rb +11 -11
  201. data/lib/solargraph/source/chain/variable.rb +13 -13
  202. data/lib/solargraph/source/chain/z_super.rb +30 -30
  203. data/lib/solargraph/source/chain.rb +252 -164
  204. data/lib/solargraph/source/change.rb +82 -79
  205. data/lib/solargraph/source/cursor.rb +167 -164
  206. data/lib/solargraph/source/source_chainer.rb +194 -191
  207. data/lib/solargraph/source/updater.rb +55 -54
  208. data/lib/solargraph/source.rb +495 -522
  209. data/lib/solargraph/source_map/clip.rb +232 -224
  210. data/lib/solargraph/source_map/completion.rb +23 -23
  211. data/lib/solargraph/source_map/data.rb +30 -0
  212. data/lib/solargraph/source_map/mapper.rb +255 -212
  213. data/lib/solargraph/source_map.rb +217 -180
  214. data/lib/solargraph/type_checker/checks.rb +120 -99
  215. data/lib/solargraph/type_checker/param_def.rb +35 -35
  216. data/lib/solargraph/type_checker/problem.rb +32 -32
  217. data/lib/solargraph/type_checker/rules.rb +62 -57
  218. data/lib/solargraph/type_checker.rb +672 -543
  219. data/lib/solargraph/version.rb +5 -5
  220. data/lib/solargraph/views/environment.erb +56 -58
  221. data/lib/solargraph/workspace/config.rb +239 -231
  222. data/lib/solargraph/workspace.rb +239 -215
  223. data/lib/solargraph/yard_map/cache.rb +25 -19
  224. data/lib/solargraph/yard_map/helpers.rb +16 -16
  225. data/lib/solargraph/yard_map/mapper/to_constant.rb +26 -25
  226. data/lib/solargraph/yard_map/mapper/to_method.rb +94 -78
  227. data/lib/solargraph/yard_map/mapper/to_namespace.rb +28 -27
  228. data/lib/solargraph/yard_map/mapper.rb +78 -77
  229. data/lib/solargraph/yard_map/to_method.rb +86 -79
  230. data/lib/solargraph/yard_map.rb +18 -460
  231. data/lib/solargraph/yard_tags.rb +20 -0
  232. data/lib/solargraph/yardoc.rb +52 -0
  233. data/lib/solargraph.rb +72 -69
  234. data/solargraph.gemspec +21 -10
  235. metadata +184 -115
  236. data/.travis.yml +0 -19
  237. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  238. data/lib/solargraph/compat.rb +0 -37
  239. data/lib/solargraph/convention/rspec.rb +0 -30
  240. data/lib/solargraph/documentor.rb +0 -76
  241. data/lib/solargraph/language_server/host/cataloger.rb +0 -56
  242. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  243. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  244. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  245. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  246. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
  247. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
  248. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  249. data/lib/solargraph/parser/legacy.rb +0 -12
  250. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
  251. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  252. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  253. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  254. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  255. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
  256. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  257. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
  258. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  259. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  260. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  261. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  262. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  263. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  264. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  265. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  266. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  267. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
  268. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  269. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
  270. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
  271. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  272. data/lib/solargraph/parser/rubyvm.rb +0 -40
  273. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  274. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  275. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  276. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  277. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  278. data/lib/yard-solargraph.rb +0 -33
  279. data/yardoc/2.2.2.tar.gz +0 -0
@@ -1,686 +1,870 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rubygems'
4
- require 'set'
5
- require 'pathname'
6
- require 'yard'
7
- require 'yard-solargraph'
8
-
9
- module Solargraph
10
- # An aggregate provider for information about workspaces, sources, gems, and
11
- # the Ruby core.
12
- #
13
- class ApiMap
14
- autoload :Cache, 'solargraph/api_map/cache'
15
- autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
16
- autoload :Store, 'solargraph/api_map/store'
17
- autoload :BundlerMethods, 'solargraph/api_map/bundler_methods'
18
-
19
- include SourceToYard
20
-
21
- # @return [Array<String>]
22
- attr_reader :unresolved_requires
23
-
24
- # @return [Array<String>]
25
- attr_reader :missing_docs
26
-
27
- # @param pins [Array<Solargraph::Pin::Base>]
28
- def initialize pins: []
29
- @source_map_hash = {}
30
- @cache = Cache.new
31
- @method_alias_stack = []
32
- index pins
33
- end
34
-
35
- # @param pins [Array<Pin::Base>]
36
- # @return [self]
37
- def index pins
38
- # @todo This implementation is incomplete. It should probably create a
39
- # Bench.
40
- @source_map_hash = {}
41
- implicit.clear
42
- cache.clear
43
- @store = Store.new(yard_map.pins + pins)
44
- self
45
- end
46
-
47
- # Map a single source.
48
- #
49
- # @param source [Source]
50
- # @return [self]
51
- def map source
52
- map = Solargraph::SourceMap.map(source)
53
- catalog Bench.new(source_maps: [map])
54
- self
55
- end
56
-
57
- # Catalog a bench.
58
- #
59
- # @param bench [Bench]
60
- def catalog bench
61
- implicit.clear
62
- @cache.clear
63
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
64
- pins = bench.source_maps.map(&:pins).flatten
65
- external_requires = bench.external_requires
66
- source_map_hash.each_value do |map|
67
- implicit.merge map.environ
68
- end
69
- external_requires.merge implicit.requires
70
- external_requires.merge bench.workspace.config.required
71
- yard_map.change(external_requires, bench.workspace.directory, bench.workspace.source_gems)
72
- @store = Store.new(yard_map.pins + implicit.pins + pins)
73
- @unresolved_requires = yard_map.unresolved_requires
74
- @missing_docs = yard_map.missing_docs
75
- @rebindable_method_names = nil
76
- store.block_pins.each { |blk| blk.rebind(self) }
77
- self
78
- end
79
-
80
- # @param name [String]
81
- # @return [YARD::Tags::MacroDirective, nil]
82
- def named_macro name
83
- store.named_macros[name]
84
- end
85
-
86
- def required
87
- @required ||= Set.new
88
- end
89
-
90
- # @return [Environ]
91
- def implicit
92
- @implicit ||= Environ.new
93
- end
94
-
95
- # @param filename [String]
96
- # @param position [Position, Array(Integer, Integer)]
97
- # @return [Source::Cursor]
98
- def cursor_at filename, position
99
- position = Position.normalize(position)
100
- raise FileNotFoundError, "File not found: #{filename}" unless source_map_hash.key?(filename)
101
- source_map_hash[filename].cursor_at(position)
102
- end
103
-
104
- # Get a clip by filename and position.
105
- #
106
- # @param filename [String]
107
- # @param position [Position, Array(Integer, Integer)]
108
- # @return [SourceMap::Clip]
109
- def clip_at filename, position
110
- position = Position.normalize(position)
111
- SourceMap::Clip.new(self, cursor_at(filename, position))
112
- end
113
-
114
- # Create an ApiMap with a workspace in the specified directory.
115
- #
116
- # @param directory [String]
117
- # @return [ApiMap]
118
- def self.load directory
119
- api_map = new
120
- workspace = Solargraph::Workspace.new(directory)
121
- # api_map.catalog Bench.new(workspace: workspace)
122
- library = Library.new(workspace)
123
- library.map!
124
- api_map.catalog library.bench
125
- api_map
126
- end
127
-
128
- # @return [Array<Solargraph::Pin::Base>]
129
- def pins
130
- store.pins
131
- end
132
-
133
- def rebindable_method_names
134
- @rebindable_method_names ||= begin
135
- result = yard_map.rebindable_method_names
136
- source_maps.each do |map|
137
- result.merge map.rebindable_method_names
138
- end
139
- result
140
- end
141
- end
142
-
143
- # An array of pins based on Ruby keywords (`if`, `end`, etc.).
144
- #
145
- # @return [Enumerable<Solargraph::Pin::Keyword>]
146
- def keyword_pins
147
- store.pins_by_class(Pin::Keyword)
148
- end
149
-
150
- # An array of namespace names defined in the ApiMap.
151
- #
152
- # @return [Set<String>]
153
- def namespaces
154
- store.namespaces
155
- end
156
-
157
- # True if the namespace exists.
158
- #
159
- # @param name [String] The namespace to match
160
- # @param context [String] The context to search
161
- # @return [Boolean]
162
- def namespace_exists? name, context = ''
163
- !qualify(name, context).nil?
164
- end
165
-
166
- # Get suggestions for constants in the specified namespace. The result
167
- # may contain both constant and namespace pins.
168
- #
169
- # @param namespace [String] The namespace
170
- # @param contexts [Array<String>] The contexts
171
- # @return [Array<Solargraph::Pin::Base>]
172
- def get_constants namespace, *contexts
173
- namespace ||= ''
174
- contexts.push '' if contexts.empty?
175
- cached = cache.get_constants(namespace, contexts)
176
- return cached.clone unless cached.nil?
177
- skip = Set.new
178
- result = []
179
- contexts.each do |context|
180
- fqns = qualify(namespace, context)
181
- visibility = [:public]
182
- visibility.push :private if fqns == context
183
- result.concat inner_get_constants(fqns, visibility, skip)
184
- end
185
- cache.set_constants(namespace, contexts, result)
186
- result
187
- end
188
-
189
- # Get a fully qualified namespace name. This method will start the search
190
- # in the specified context until it finds a match for the name.
191
- #
192
- # @param namespace [String, nil] The namespace to match
193
- # @param context [String] The context to search
194
- # @return [String]
195
- def qualify namespace, context = ''
196
- return namespace if ['self', nil].include?(namespace)
197
- cached = cache.get_qualified_namespace(namespace, context)
198
- return cached.clone unless cached.nil?
199
- result = if namespace.start_with?('::')
200
- inner_qualify(namespace[2..-1], '', Set.new)
201
- else
202
- inner_qualify(namespace, context, Set.new)
203
- end
204
- cache.set_qualified_namespace(namespace, context, result)
205
- result
206
- end
207
-
208
- # Get an array of instance variable pins defined in specified namespace
209
- # and scope.
210
- #
211
- # @param namespace [String] A fully qualified namespace
212
- # @param scope [Symbol] :instance or :class
213
- # @return [Array<Solargraph::Pin::InstanceVariable>]
214
- def get_instance_variable_pins(namespace, scope = :instance)
215
- result = []
216
- used = [namespace]
217
- result.concat store.get_instance_variables(namespace, scope)
218
- sc = qualify_lower(store.get_superclass(namespace), namespace)
219
- until sc.nil? || used.include?(sc)
220
- used.push sc
221
- result.concat store.get_instance_variables(sc, scope)
222
- sc = qualify_lower(store.get_superclass(sc), sc)
223
- end
224
- result
225
- end
226
-
227
- # Get an array of class variable pins for a namespace.
228
- #
229
- # @param namespace [String] A fully qualified namespace
230
- # @return [Array<Solargraph::Pin::ClassVariable>]
231
- def get_class_variable_pins(namespace)
232
- prefer_non_nil_variables(store.get_class_variables(namespace))
233
- end
234
-
235
- # @return [Array<Solargraph::Pin::Base>]
236
- def get_symbols
237
- store.get_symbols
238
- end
239
-
240
- # @return [Array<Solargraph::Pin::GlobalVariable>]
241
- def get_global_variable_pins
242
- store.pins_by_class(Pin::GlobalVariable)
243
- end
244
-
245
- # Get an array of methods available in a particular context.
246
- #
247
- # @param fqns [String] The fully qualified namespace to search for methods
248
- # @param scope [Symbol] :class or :instance
249
- # @param visibility [Array<Symbol>] :public, :protected, and/or :private
250
- # @param deep [Boolean] True to include superclasses, mixins, etc.
251
- # @return [Array<Solargraph::Pin::Method>]
252
- def get_methods fqns, scope: :instance, visibility: [:public], deep: true
253
- cached = cache.get_methods(fqns, scope, visibility, deep)
254
- return cached.clone unless cached.nil?
255
- result = []
256
- skip = Set.new
257
- if fqns == ''
258
- # @todo Implement domains
259
- implicit.domains.each do |domain|
260
- type = ComplexType.try_parse(domain)
261
- next if type.undefined?
262
- result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
263
- end
264
- result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
265
- result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
266
- result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
267
- else
268
- result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
269
- result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
270
- end
271
- resolved = resolve_method_aliases(result, visibility)
272
- cache.set_methods(fqns, scope, visibility, deep, resolved)
273
- resolved
274
- end
275
-
276
- # Get an array of method pins for a complex type.
277
- #
278
- # The type's namespace and the context should be fully qualified. If the
279
- # context matches the namespace type or is a subclass of the type,
280
- # protected methods are included in the results. If protected methods are
281
- # included and internal is true, private methods are also included.
282
- #
283
- # @example
284
- # api_map = Solargraph::ApiMap.new
285
- # type = Solargraph::ComplexType.parse('String')
286
- # api_map.get_complex_type_methods(type)
287
- #
288
- # @param type [Solargraph::ComplexType] The complex type of the namespace
289
- # @param context [String] The context from which the type is referenced
290
- # @param internal [Boolean] True to include private methods
291
- # @return [Array<Solargraph::Pin::Base>]
292
- def get_complex_type_methods type, context = '', internal = false
293
- # This method does not qualify the complex type's namespace because
294
- # it can cause conflicts between similar names, e.g., `Foo` vs.
295
- # `Other::Foo`. It still takes a context argument to determine whether
296
- # protected and private methods are visible.
297
- return [] if type.undefined? || type.void?
298
- result = []
299
- if type.duck_type?
300
- type.select(&:duck_type?).each do |t|
301
- result.push Pin::DuckMethod.new(name: t.tag[1..-1])
302
- end
303
- result.concat get_methods('Object')
304
- else
305
- unless type.nil? || type.name == 'void'
306
- visibility = [:public]
307
- if type.namespace == context || super_and_sub?(type.namespace, context)
308
- visibility.push :protected
309
- visibility.push :private if internal
310
- end
311
- result.concat get_methods(type.namespace, scope: type.scope, visibility: visibility)
312
- end
313
- end
314
- result
315
- end
316
-
317
- # Get a stack of method pins for a method name in a namespace. The order
318
- # of the pins corresponds to the ancestry chain, with highest precedence
319
- # first.
320
- #
321
- # @example
322
- # api_map.get_method_stack('Subclass', 'method_name')
323
- # #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
324
- #
325
- # @param fqns [String]
326
- # @param name [String]
327
- # @param scope [Symbol] :instance or :class
328
- # @return [Array<Solargraph::Pin::Method>]
329
- def get_method_stack fqns, name, scope: :instance
330
- get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
331
- end
332
-
333
- # Get an array of all suggestions that match the specified path.
334
- #
335
- # @deprecated Use #get_path_pins instead.
336
- #
337
- # @param path [String] The path to find
338
- # @return [Array<Solargraph::Pin::Base>]
339
- def get_path_suggestions path
340
- return [] if path.nil?
341
- resolve_method_aliases store.get_path_pins(path)
342
- end
343
-
344
- # Get an array of pins that match the specified path.
345
- #
346
- # @param path [String]
347
- # @return [Array<Pin::Base>]
348
- def get_path_pins path
349
- get_path_suggestions(path)
350
- end
351
-
352
- # Get a list of documented paths that match the query.
353
- #
354
- # @example
355
- # api_map.query('str') # Results will include `String` and `Struct`
356
- #
357
- # @param query [String] The text to match
358
- # @return [Array<String>]
359
- def search query
360
- rake_yard(store)
361
- found = []
362
- code_object_paths.each do |k|
363
- if (found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))) &&
364
- k.downcase.include?(query.downcase)
365
- found.push k
366
- end
367
- end
368
- found
369
- end
370
-
371
- # Get YARD documentation for the specified path.
372
- #
373
- # @example
374
- # api_map.document('String#split')
375
- #
376
- # @param path [String] The path to find
377
- # @return [Array<YARD::CodeObjects::Base>]
378
- def document path
379
- rake_yard(store)
380
- docs = []
381
- docs.push code_object_at(path) unless code_object_at(path).nil?
382
- docs
383
- end
384
-
385
- # Get an array of all symbols in the workspace that match the query.
386
- #
387
- # @param query [String]
388
- # @return [Array<Pin::Base>]
389
- def query_symbols query
390
- Pin::Search.new(
391
- source_map_hash.values.flat_map(&:document_symbols),
392
- query
393
- ).results
394
- end
395
-
396
- # @param location [Solargraph::Location]
397
- # @return [Array<Solargraph::Pin::Base>]
398
- def locate_pins location
399
- return [] if location.nil? || !source_map_hash.key?(location.filename)
400
- resolve_method_aliases source_map_hash[location.filename].locate_pins(location)
401
- end
402
-
403
- # @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
404
- # @param cursor [Source::Cursor]
405
- # @return [SourceMap::Clip]
406
- def clip cursor
407
- raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
408
- SourceMap::Clip.new(self, cursor)
409
- end
410
-
411
- # Get an array of document symbols from a file.
412
- #
413
- # @param filename [String]
414
- # @return [Array<Pin::Symbol>]
415
- def document_symbols filename
416
- return [] unless source_map_hash.key?(filename) # @todo Raise error?
417
- resolve_method_aliases source_map_hash[filename].document_symbols
418
- end
419
-
420
- # @return [Array<SourceMap>]
421
- def source_maps
422
- source_map_hash.values
423
- end
424
-
425
- # Get a source map by filename.
426
- #
427
- # @param filename [String]
428
- # @return [SourceMap]
429
- def source_map filename
430
- raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.key?(filename)
431
- source_map_hash[filename]
432
- end
433
-
434
- # True if the specified file was included in a bundle, i.e., it's either
435
- # included in a workspace or open in a library.
436
- #
437
- # @param filename [String]
438
- def bundled? filename
439
- source_map_hash.keys.include?(filename)
440
- end
441
-
442
- # Check if a class is a superclass of another class.
443
- #
444
- # @param sup [String] The superclass
445
- # @param sub [String] The subclass
446
- # @return [Boolean]
447
- def super_and_sub?(sup, sub)
448
- fqsup = qualify(sup)
449
- cls = qualify(sub)
450
- until fqsup.nil? || cls.nil?
451
- return true if cls == fqsup
452
- cls = qualify_superclass(cls)
453
- end
454
- false
455
- end
456
-
457
- # @return [YardMap]
458
- def yard_map
459
- @yard_map ||= YardMap.new
460
- end
461
-
462
- # Check if the host class includes the specified module.
463
- #
464
- # @param host [String] The class
465
- # @param mod [String] The module
466
- # @return [Boolean]
467
- def type_include?(host, mod)
468
- store.get_includes(host).include?(mod)
469
- end
470
-
471
- private
472
-
473
- # A hash of source maps with filename keys.
474
- #
475
- # @return [Hash{String => SourceMap}]
476
- attr_reader :source_map_hash
477
-
478
- # @return [ApiMap::Store]
479
- def store
480
- @store ||= Store.new
481
- end
482
-
483
- # @return [Solargraph::ApiMap::Cache]
484
- attr_reader :cache
485
-
486
- # @param fqns [String] A fully qualified namespace
487
- # @param scope [Symbol] :class or :instance
488
- # @param visibility [Array<Symbol>] :public, :protected, and/or :private
489
- # @param deep [Boolean]
490
- # @param skip [Set<String>]
491
- # @param no_core [Boolean] Skip core classes if true
492
- # @return [Array<Pin::Base>]
493
- def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
494
- return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
495
- reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
496
- return [] if skip.include?(reqstr)
497
- skip.add reqstr
498
- result = []
499
- if deep && scope == :instance
500
- store.get_prepends(fqns).reverse.each do |im|
501
- fqim = qualify(im, fqns)
502
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
503
- end
504
- end
505
- result.concat store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
506
- if deep
507
- if scope == :instance
508
- store.get_includes(fqns).reverse.each do |im|
509
- fqim = qualify(im, fqns)
510
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
511
- end
512
- fqsc = qualify_superclass(fqns)
513
- unless fqsc.nil?
514
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
515
- end
516
- else
517
- store.get_extends(fqns).reverse.each do |em|
518
- fqem = qualify(em, fqns)
519
- result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
520
- end
521
- fqsc = qualify_superclass(fqns)
522
- unless fqsc.nil?
523
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
524
- end
525
- unless no_core || fqns.empty?
526
- type = get_namespace_type(fqns)
527
- result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
528
- result.concat inner_get_methods('Module', :instance, visibility, deep, skip, no_core)
529
- end
530
- end
531
- store.domains(fqns).each do |d|
532
- dt = ComplexType.try_parse(d)
533
- result.concat inner_get_methods(dt.namespace, dt.scope, visibility, deep, skip)
534
- end
535
- end
536
- result
537
- end
538
-
539
- # @param fqns [String]
540
- # @param visibility [Array<Symbol>]
541
- # @param skip [Set<String>]
542
- # @return [Array<Pin::Base>]
543
- def inner_get_constants fqns, visibility, skip
544
- return [] if fqns.nil? || skip.include?(fqns)
545
- skip.add fqns
546
- result = []
547
- store.get_prepends(fqns).each do |is|
548
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
549
- end
550
- result.concat store.get_constants(fqns, visibility)
551
- .sort { |a, b| a.name <=> b.name }
552
- store.get_includes(fqns).each do |is|
553
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
554
- end
555
- fqsc = qualify_superclass(fqns)
556
- unless %w[Object BasicObject].include?(fqsc)
557
- result.concat inner_get_constants(fqsc, [:public], skip)
558
- end
559
- result
560
- end
561
-
562
- # @return [Hash]
563
- def path_macros
564
- @path_macros ||= {}
565
- end
566
-
567
- # @param namespace [String]
568
- # @param context [String]
569
- # @return [String]
570
- def qualify_lower namespace, context
571
- qualify namespace, context.split('::')[0..-2].join('::')
572
- end
573
-
574
- def qualify_superclass fqsub
575
- sup = store.get_superclass(fqsub)
576
- return nil if sup.nil?
577
- parts = fqsub.split('::')
578
- last = parts.pop
579
- parts.pop if last == sup
580
- qualify(sup, parts.join('::'))
581
- end
582
-
583
- # @param name [String]
584
- # @param root [String]
585
- # @param skip [Set<String>]
586
- # @return [String, nil]
587
- def inner_qualify name, root, skip
588
- return nil if name.nil?
589
- return nil if skip.include?(root)
590
- skip.add root
591
- possibles = []
592
- if name == ''
593
- if root == ''
594
- return ''
595
- else
596
- return inner_qualify(root, '', skip)
597
- end
598
- else
599
- return name if root == '' && store.namespace_exists?(name)
600
- roots = root.to_s.split('::')
601
- while roots.length > 0
602
- fqns = roots.join('::') + '::' + name
603
- return fqns if store.namespace_exists?(fqns)
604
- incs = store.get_includes(roots.join('::'))
605
- incs.each do |inc|
606
- foundinc = inner_qualify(name, inc, skip)
607
- possibles.push foundinc unless foundinc.nil?
608
- end
609
- roots.pop
610
- end
611
- if possibles.empty?
612
- incs = store.get_includes('')
613
- incs.each do |inc|
614
- foundinc = inner_qualify(name, inc, skip)
615
- possibles.push foundinc unless foundinc.nil?
616
- end
617
- end
618
- return name if store.namespace_exists?(name)
619
- return possibles.last
620
- end
621
- end
622
-
623
- # Get the namespace's type (Class or Module).
624
- #
625
- # @param fqns [String] A fully qualified namespace
626
- # @return [Symbol, nil] :class, :module, or nil
627
- def get_namespace_type fqns
628
- return nil if fqns.nil?
629
- # @type [Pin::Namespace, nil]
630
- pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
631
- return nil if pin.nil?
632
- pin.type
633
- end
634
-
635
- # Sort an array of pins to put nil or undefined variables last.
636
- #
637
- # @param pins [Array<Solargraph::Pin::Base>]
638
- # @return [Array<Solargraph::Pin::Base>]
639
- def prefer_non_nil_variables pins
640
- result = []
641
- nil_pins = []
642
- pins.each do |pin|
643
- if pin.variable? && pin.nil_assignment?
644
- nil_pins.push pin
645
- else
646
- result.push pin
647
- end
648
- end
649
- result + nil_pins
650
- end
651
-
652
- # @param pins [Array<Pin::Base>]
653
- # @param visibility [Array<Symbol>]
654
- # @return [Array<Pin::Base>]
655
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
656
- result = []
657
- pins.each do |pin|
658
- resolved = resolve_method_alias(pin)
659
- next if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
660
- result.push resolved
661
- end
662
- result
663
- end
664
-
665
- # @param pin [Pin::MethodAlias, Pin::Base]
666
- # @return [Pin::Method]
667
- def resolve_method_alias pin
668
- return pin if !pin.is_a?(Pin::MethodAlias) || @method_alias_stack.include?(pin.path)
669
- @method_alias_stack.push pin.path
670
- origin = get_method_stack(pin.full_context.namespace, pin.original, scope: pin.scope).first
671
- @method_alias_stack.pop
672
- return pin if origin.nil?
673
- args = {
674
- location: pin.location,
675
- closure: pin.closure,
676
- name: pin.name,
677
- comments: origin.comments,
678
- scope: origin.scope,
679
- visibility: origin.visibility,
680
- parameters: origin.parameters,
681
- attribute: origin.attribute?
682
- }
683
- Pin::Method.new **args
684
- end
685
- end
686
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'yard'
5
+ require 'solargraph/yard_tags'
6
+
7
+ module Solargraph
8
+ # An aggregate provider for information about Workspaces, Sources, gems, and
9
+ # the Ruby core.
10
+ #
11
+ class ApiMap
12
+ autoload :Cache, 'solargraph/api_map/cache'
13
+ autoload :SourceToYard, 'solargraph/api_map/source_to_yard'
14
+ autoload :Store, 'solargraph/api_map/store'
15
+ autoload :Index, 'solargraph/api_map/index'
16
+
17
+ # @return [Array<String>]
18
+ attr_reader :unresolved_requires
19
+
20
+ @@core_map = RbsMap::CoreMap.new
21
+
22
+ # @return [Array<String>]
23
+ attr_reader :missing_docs
24
+
25
+ # @param pins [Array<Solargraph::Pin::Base>]
26
+ def initialize pins: []
27
+ @source_map_hash = {}
28
+ @cache = Cache.new
29
+ @method_alias_stack = []
30
+ index pins
31
+ end
32
+
33
+ #
34
+ # This is a mutable object, which is cached in the Chain class -
35
+ # if you add any fields which change the results of calls (not
36
+ # just caches), please also change `equality_fields` below.
37
+ #
38
+
39
+ def eql?(other)
40
+ self.class == other.class &&
41
+ equality_fields == other.equality_fields
42
+ end
43
+
44
+ def ==(other)
45
+ self.eql?(other)
46
+ end
47
+
48
+ def hash
49
+ equality_fields.hash
50
+ end
51
+
52
+ def to_s
53
+ self.class.to_s
54
+ end
55
+
56
+ # avoid enormous dump
57
+ def inspect
58
+ to_s
59
+ end
60
+
61
+ # @param pins [Array<Pin::Base>]
62
+ # @return [self]
63
+ def index pins
64
+ # @todo This implementation is incomplete. It should probably create a
65
+ # Bench.
66
+ @source_map_hash = {}
67
+ implicit.clear
68
+ cache.clear
69
+ store.update @@core_map.pins, pins
70
+ self
71
+ end
72
+
73
+ # Map a single source.
74
+ #
75
+ # @param source [Source]
76
+ # @return [self]
77
+ def map source
78
+ map = Solargraph::SourceMap.map(source)
79
+ catalog Bench.new(source_maps: [map])
80
+ self
81
+ end
82
+
83
+ # Catalog a bench.
84
+ #
85
+ # @param bench [Bench]
86
+ # @return [self]
87
+ def catalog bench
88
+ @source_map_hash = bench.source_map_hash
89
+ iced_pins = bench.icebox.flat_map(&:pins)
90
+ live_pins = bench.live_map&.pins || []
91
+ implicit.clear
92
+ source_map_hash.each_value do |map|
93
+ implicit.merge map.environ
94
+ end
95
+ unresolved_requires = (bench.external_requires + implicit.requires + bench.workspace.config.required).to_a.compact.uniq
96
+ if @unresolved_requires != unresolved_requires || @doc_map&.uncached_gemspecs&.any?
97
+ @doc_map = DocMap.new(unresolved_requires, [], bench.workspace.rbs_collection_path) # @todo Implement gem preferences
98
+ @unresolved_requires = unresolved_requires
99
+ end
100
+ @cache.clear if store.update(@@core_map.pins, @doc_map.pins, implicit.pins, iced_pins, live_pins)
101
+ @missing_docs = [] # @todo Implement missing docs
102
+ self
103
+ end
104
+
105
+ # @todo need to model type def statement in chains as a symbol so
106
+ # that this overload of 'protected' will typecheck @sg-ignore
107
+ # @sg-ignore
108
+ protected def equality_fields
109
+ [self.class, @source_map_hash, implicit, @doc_map, @unresolved_requires]
110
+ end
111
+
112
+ # @return [::Array<Gem::Specification>]
113
+ def uncached_gemspecs
114
+ @doc_map&.uncached_gemspecs || []
115
+ end
116
+
117
+ # @return [Array<Pin::Base>]
118
+ def core_pins
119
+ @@core_map.pins
120
+ end
121
+
122
+ # @param name [String]
123
+ # @return [YARD::Tags::MacroDirective, nil]
124
+ def named_macro name
125
+ store.named_macros[name]
126
+ end
127
+
128
+ # @return [Set<String>]
129
+ def required
130
+ @required ||= Set.new
131
+ end
132
+
133
+ # @return [Environ]
134
+ def implicit
135
+ @implicit ||= Environ.new
136
+ end
137
+
138
+ # @param filename [String]
139
+ # @param position [Position, Array(Integer, Integer)]
140
+ # @return [Source::Cursor]
141
+ def cursor_at filename, position
142
+ position = Position.normalize(position)
143
+ raise FileNotFoundError, "File not found: #{filename}" unless source_map_hash.key?(filename)
144
+ source_map_hash[filename].cursor_at(position)
145
+ end
146
+
147
+ # Get a clip by filename and position.
148
+ #
149
+ # @param filename [String]
150
+ # @param position [Position, Array(Integer, Integer)]
151
+ # @return [SourceMap::Clip]
152
+ def clip_at filename, position
153
+ position = Position.normalize(position)
154
+ clip(cursor_at(filename, position))
155
+ end
156
+
157
+ # Create an ApiMap with a workspace in the specified directory.
158
+ #
159
+ # @param directory [String]
160
+ # @return [ApiMap]
161
+ def self.load directory
162
+ api_map = new
163
+ workspace = Solargraph::Workspace.new(directory)
164
+ # api_map.catalog Bench.new(workspace: workspace)
165
+ library = Library.new(workspace)
166
+ library.map!
167
+ api_map.catalog library.bench
168
+ api_map
169
+ end
170
+
171
+ # Create an ApiMap with a workspace in the specified directory and cache
172
+ # any missing gems.
173
+ #
174
+ #
175
+ # @todo IO::NULL is incorrectly inferred to be a String.
176
+ # @sg-ignore
177
+ #
178
+ # @param directory [String]
179
+ # @param out [IO] The output stream for messages
180
+ # @return [ApiMap]
181
+ def self.load_with_cache directory, out = IO::NULL
182
+ api_map = load(directory)
183
+ return api_map if api_map.uncached_gemspecs.empty?
184
+
185
+ api_map.uncached_gemspecs.each do |gemspec|
186
+ out.puts "Caching gem #{gemspec.name} #{gemspec.version}"
187
+ pins = GemPins.build(gemspec)
188
+ Solargraph::Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
189
+ end
190
+ load(directory)
191
+ end
192
+
193
+ # @return [Array<Solargraph::Pin::Base>]
194
+ def pins
195
+ store.pins.clone.freeze
196
+ end
197
+
198
+ # An array of pins based on Ruby keywords (`if`, `end`, etc.).
199
+ #
200
+ # @return [Enumerable<Solargraph::Pin::Keyword>]
201
+ def keyword_pins
202
+ store.pins_by_class(Pin::Keyword)
203
+ end
204
+
205
+ # An array of namespace names defined in the ApiMap.
206
+ #
207
+ # @return [Set<String>]
208
+ def namespaces
209
+ store.namespaces
210
+ end
211
+
212
+ # True if the namespace exists.
213
+ #
214
+ # @param name [String] The namespace to match
215
+ # @param context [String] The context to search
216
+ # @return [Boolean]
217
+ def namespace_exists? name, context = ''
218
+ !qualify(name, context).nil?
219
+ end
220
+
221
+ # Get suggestions for constants in the specified namespace. The result
222
+ # may contain both constant and namespace pins.
223
+ #
224
+ # @param namespace [String] The namespace
225
+ # @param contexts [Array<String>] The contexts
226
+ # @return [Array<Solargraph::Pin::Base>]
227
+ def get_constants namespace, *contexts
228
+ namespace ||= ''
229
+ contexts.push '' if contexts.empty?
230
+ cached = cache.get_constants(namespace, contexts)
231
+ return cached.clone unless cached.nil?
232
+ skip = Set.new
233
+ result = []
234
+ contexts.each do |context|
235
+ fqns = qualify(namespace, context)
236
+ visibility = [:public]
237
+ visibility.push :private if fqns == context
238
+ result.concat inner_get_constants(fqns, visibility, skip)
239
+ end
240
+ cache.set_constants(namespace, contexts, result)
241
+ result
242
+ end
243
+
244
+ # @param namespace [String]
245
+ # @param context [String]
246
+ # @return [Array<Pin::Namespace>]
247
+ def get_namespace_pins namespace, context
248
+ store.fqns_pins(qualify(namespace, context))
249
+ end
250
+
251
+ # Determine fully qualified tag for a given tag used inside the
252
+ # definition of another tag ("context"). This method will start
253
+ # the search in the specified context until it finds a match for
254
+ # the tag.
255
+ #
256
+ # Does not recurse into qualifying the type parameters, but
257
+ # returns any which were passed in unchanged.
258
+ #
259
+ # @param tag [String, nil] The namespace to
260
+ # match, complete with generic parameters set to appropriate
261
+ # values if available
262
+ # @param context_tag [String] The fully qualified context in which
263
+ # the tag was referenced; start from here to resolve the name.
264
+ # Should not be prefixed with '::'.
265
+ # @return [String, nil] fully qualified tag
266
+ def qualify tag, context_tag = ''
267
+ return tag if ['self', nil].include?(tag)
268
+
269
+ context_type = ComplexType.parse(context_tag).force_rooted
270
+ return unless context_type
271
+
272
+ type = ComplexType.try_parse(tag)
273
+ return unless type
274
+
275
+ fqns = qualify_namespace(type.rooted_namespace, context_type.namespace)
276
+ return unless fqns
277
+
278
+ fqns + type.substring
279
+ end
280
+
281
+ # Determine fully qualified namespace for a given namespace used
282
+ # inside the definition of another tag ("context"). This method
283
+ # will start the search in the specified context until it finds a
284
+ # match for the namespace.
285
+ #
286
+ # @param namespace [String, nil] The namespace to
287
+ # match
288
+ # @param context_namespace [String] The context namespace in which the
289
+ # tag was referenced; start from here to resolve the name
290
+ # @return [String, nil] fully qualified namespace
291
+ def qualify_namespace(namespace, context_namespace = '')
292
+ cached = cache.get_qualified_namespace(namespace, context_namespace)
293
+ return cached.clone unless cached.nil?
294
+ result = if namespace.start_with?('::')
295
+ inner_qualify(namespace[2..-1], '', Set.new)
296
+ else
297
+ inner_qualify(namespace, context_namespace, Set.new)
298
+ end
299
+ cache.set_qualified_namespace(namespace, context_namespace, result)
300
+ result
301
+ end
302
+
303
+ # Get an array of instance variable pins defined in specified namespace
304
+ # and scope.
305
+ #
306
+ # @param namespace [String] A fully qualified namespace
307
+ # @param scope [Symbol] :instance or :class
308
+ # @return [Array<Solargraph::Pin::InstanceVariable>]
309
+ def get_instance_variable_pins(namespace, scope = :instance)
310
+ result = []
311
+ used = [namespace]
312
+ result.concat store.get_instance_variables(namespace, scope)
313
+ sc = qualify_lower(store.get_superclass(namespace), namespace)
314
+ until sc.nil? || used.include?(sc)
315
+ used.push sc
316
+ result.concat store.get_instance_variables(sc, scope)
317
+ sc = qualify_lower(store.get_superclass(sc), sc)
318
+ end
319
+ result
320
+ end
321
+
322
+ # Get an array of class variable pins for a namespace.
323
+ #
324
+ # @param namespace [String] A fully qualified namespace
325
+ # @return [Enumerable<Solargraph::Pin::ClassVariable>]
326
+ def get_class_variable_pins(namespace)
327
+ prefer_non_nil_variables(store.get_class_variables(namespace))
328
+ end
329
+
330
+ # @return [Enumerable<Solargraph::Pin::Base>]
331
+ def get_symbols
332
+ store.get_symbols
333
+ end
334
+
335
+ # @return [Enumerable<Solargraph::Pin::GlobalVariable>]
336
+ def get_global_variable_pins
337
+ store.pins_by_class(Pin::GlobalVariable)
338
+ end
339
+
340
+ # @return [Enumerable<Solargraph::Pin::Block>]
341
+ def get_block_pins
342
+ store.pins_by_class(Pin::Block)
343
+ end
344
+
345
+ # Get an array of methods available in a particular context.
346
+ #
347
+ # @param rooted_tag [String] The fully qualified namespace to search for methods
348
+ # @param scope [Symbol] :class or :instance
349
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
350
+ # @param deep [Boolean] True to include superclasses, mixins, etc.
351
+ # @return [Array<Solargraph::Pin::Method>]
352
+ def get_methods rooted_tag, scope: :instance, visibility: [:public], deep: true
353
+ rooted_type = ComplexType.try_parse(rooted_tag)
354
+ fqns = rooted_type.namespace
355
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
356
+ cached = cache.get_methods(rooted_tag, scope, visibility, deep)
357
+ return cached.clone unless cached.nil?
358
+ # @type [Array<Solargraph::Pin::Method>]
359
+ result = []
360
+ skip = Set.new
361
+ if rooted_tag == ''
362
+ # @todo Implement domains
363
+ implicit.domains.each do |domain|
364
+ type = ComplexType.try_parse(domain)
365
+ next if type.undefined?
366
+ result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
367
+ end
368
+ result.concat inner_get_methods(rooted_tag, :class, visibility, deep, skip)
369
+ result.concat inner_get_methods(rooted_tag, :instance, visibility, deep, skip)
370
+ result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
371
+ else
372
+ result.concat inner_get_methods(rooted_tag, scope, visibility, deep, skip)
373
+ unless %w[Class Class<Class>].include?(rooted_tag)
374
+ result.map! do |pin|
375
+ next pin unless pin.path == 'Class#new'
376
+ init_pin = get_method_stack(rooted_tag, 'initialize').first
377
+ next pin unless init_pin
378
+
379
+ type = ComplexType.try_parse(ComplexType.try_parse(rooted_tag).namespace)
380
+ Pin::Method.new(
381
+ name: 'new',
382
+ scope: :class,
383
+ location: init_pin.location,
384
+ parameters: init_pin.parameters,
385
+ signatures: init_pin.signatures.map { |sig| sig.proxy(type) },
386
+ return_type: type,
387
+ comments: init_pin.comments,
388
+ closure: init_pin.closure
389
+ # @todo Hack to force TypeChecker#internal_or_core?
390
+ ).tap { |pin| pin.source = :rbs }
391
+ end
392
+ end
393
+ result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
394
+ result.concat inner_get_methods('Module', scope, visibility, deep, skip) if scope == :module
395
+ end
396
+ result = resolve_method_aliases(result, visibility)
397
+ if namespace_pin && rooted_tag != rooted_type.name
398
+ result = result.map { |method_pin| method_pin.resolve_generics(namespace_pin, rooted_type) }
399
+ end
400
+ cache.set_methods(rooted_tag, scope, visibility, deep, result)
401
+ result
402
+ end
403
+
404
+ # Get an array of method pins for a complex type.
405
+ #
406
+ # The type's namespace and the context should be fully qualified. If the
407
+ # context matches the namespace type or is a subclass of the type,
408
+ # protected methods are included in the results. If protected methods are
409
+ # included and internal is true, private methods are also included.
410
+ #
411
+ # @example
412
+ # api_map = Solargraph::ApiMap.new
413
+ # type = Solargraph::ComplexType.parse('String')
414
+ # api_map.get_complex_type_methods(type)
415
+ #
416
+ # @param complex_type [Solargraph::ComplexType] The complex type of the namespace
417
+ # @param context [String] The context from which the type is referenced
418
+ # @param internal [Boolean] True to include private methods
419
+ # @return [Array<Solargraph::Pin::Base>]
420
+ def get_complex_type_methods complex_type, context = '', internal = false
421
+ # This method does not qualify the complex type's namespace because
422
+ # it can cause conflicts between similar names, e.g., `Foo` vs.
423
+ # `Other::Foo`. It still takes a context argument to determine whether
424
+ # protected and private methods are visible.
425
+ return [] if complex_type.undefined? || complex_type.void?
426
+ result = Set.new
427
+ complex_type.each do |type|
428
+ if type.duck_type?
429
+ result.add Pin::DuckMethod.new(name: type.to_s[1..-1])
430
+ result.merge get_methods('Object')
431
+ else
432
+ unless type.nil? || type.name == 'void'
433
+ visibility = [:public]
434
+ if type.namespace == context || super_and_sub?(type.namespace, context)
435
+ visibility.push :protected
436
+ visibility.push :private if internal
437
+ end
438
+ result.merge get_methods(type.tag, scope: type.scope, visibility: visibility)
439
+ end
440
+ end
441
+ end
442
+ result.to_a
443
+ end
444
+
445
+ # Get a stack of method pins for a method name in a potentially
446
+ # parameterized namespace. The order of the pins corresponds to
447
+ # the ancestry chain, with highest precedence first.
448
+ #
449
+ # @example
450
+ # api_map.get_method_stack('Subclass', 'method_name')
451
+ # #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
452
+ #
453
+ # @param rooted_tag [String] Parameterized namespace, fully qualified
454
+ # @param name [String] Method name to look up
455
+ # @param scope [Symbol] :instance or :class
456
+ # @return [Array<Solargraph::Pin::Method>]
457
+ def get_method_stack rooted_tag, name, scope: :instance, visibility: [:private, :protected, :public], preserve_generics: false
458
+ rooted_type = ComplexType.parse(rooted_tag)
459
+ fqns = rooted_type.namespace
460
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
461
+ methods = get_methods(rooted_tag, scope: scope, visibility: visibility).select { |p| p.name == name }
462
+ methods = erase_generics(namespace_pin, rooted_type, methods) unless preserve_generics
463
+ methods
464
+ end
465
+
466
+ # Get an array of all suggestions that match the specified path.
467
+ #
468
+ # @deprecated Use #get_path_pins instead.
469
+ #
470
+ # @param path [String] The path to find
471
+ # @return [Enumerable<Solargraph::Pin::Base>]
472
+ def get_path_suggestions path
473
+ return [] if path.nil?
474
+ resolve_method_aliases store.get_path_pins(path)
475
+ end
476
+
477
+ # Get an array of pins that match the specified path.
478
+ #
479
+ # @param path [String]
480
+ # @return [Enumerable<Pin::Base>]
481
+ def get_path_pins path
482
+ get_path_suggestions(path)
483
+ end
484
+
485
+ # Get a list of documented paths that match the query.
486
+ #
487
+ # @example
488
+ # api_map.query('str') # Results will include `String` and `Struct`
489
+ #
490
+ # @param query [String] The text to match
491
+ # @return [Array<String>]
492
+ def search query
493
+ pins.map(&:path)
494
+ .compact
495
+ .select { |path| path.downcase.include?(query.downcase) }
496
+ end
497
+
498
+ # Get YARD documentation for the specified path.
499
+ #
500
+ # @example
501
+ # api_map.document('String#split')
502
+ #
503
+ # @todo This method is likely superfluous. Calling get_path_pins directly
504
+ # should be sufficient.
505
+ #
506
+ # @param path [String] The path to find
507
+ # @return [Enumerable<Pin::Base>]
508
+ def document path
509
+ get_path_pins(path)
510
+ end
511
+
512
+ # Get an array of all symbols in the workspace that match the query.
513
+ #
514
+ # @param query [String]
515
+ # @return [Array<Pin::Base>]
516
+ def query_symbols query
517
+ Pin::Search.new(
518
+ source_map_hash.values.flat_map(&:document_symbols),
519
+ query
520
+ ).results
521
+ end
522
+
523
+ # @param location [Solargraph::Location]
524
+ # @return [Array<Solargraph::Pin::Base>]
525
+ def locate_pins location
526
+ return [] if location.nil? || !source_map_hash.key?(location.filename)
527
+ resolve_method_aliases source_map_hash[location.filename].locate_pins(location)
528
+ end
529
+
530
+ # @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
531
+ # @param cursor [Source::Cursor]
532
+ # @return [SourceMap::Clip]
533
+ def clip cursor
534
+ raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
535
+
536
+ SourceMap::Clip.new(self, cursor)
537
+ end
538
+
539
+ # Get an array of document symbols from a file.
540
+ #
541
+ # @param filename [String]
542
+ # @return [Array<Pin::Symbol>]
543
+ def document_symbols filename
544
+ return [] unless source_map_hash.key?(filename) # @todo Raise error?
545
+ resolve_method_aliases source_map_hash[filename].document_symbols
546
+ end
547
+
548
+ # @return [Array<SourceMap>]
549
+ def source_maps
550
+ source_map_hash.values
551
+ end
552
+
553
+ # Get a source map by filename.
554
+ #
555
+ # @param filename [String]
556
+ # @return [SourceMap]
557
+ def source_map filename
558
+ raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.key?(filename)
559
+ source_map_hash[filename]
560
+ end
561
+
562
+ # True if the specified file was included in a bundle, i.e., it's either
563
+ # included in a workspace or open in a library.
564
+ #
565
+ # @param filename [String]
566
+ def bundled? filename
567
+ source_map_hash.keys.include?(filename)
568
+ end
569
+
570
+ # Check if a class is a superclass of another class.
571
+ #
572
+ # @param sup [String] The superclass
573
+ # @param sub [String] The subclass
574
+ # @return [Boolean]
575
+ def super_and_sub?(sup, sub)
576
+ fqsup = qualify(sup)
577
+ cls = qualify(sub)
578
+ tested = []
579
+ until fqsup.nil? || cls.nil? || tested.include?(cls)
580
+ return true if cls == fqsup
581
+ tested.push cls
582
+ cls = qualify_superclass(cls)
583
+ end
584
+ false
585
+ end
586
+
587
+ # Check if the host class includes the specified module, ignoring
588
+ # type parameters used.
589
+ #
590
+ # @param host_ns [String] The class namesapce (no type parameters)
591
+ # @param module_ns [String] The module namespace (no type parameters)
592
+ #
593
+ # @return [Boolean]
594
+ def type_include?(host_ns, module_ns)
595
+ store.get_includes(host_ns).map { |inc_tag| ComplexType.parse(inc_tag).name }.include?(module_ns)
596
+ end
597
+
598
+ private
599
+
600
+ # A hash of source maps with filename keys.
601
+ #
602
+ # @return [Hash{String => SourceMap}]
603
+ attr_reader :source_map_hash
604
+
605
+ # @return [ApiMap::Store]
606
+ def store
607
+ @store ||= Store.new
608
+ end
609
+
610
+ # @return [Solargraph::ApiMap::Cache]
611
+ attr_reader :cache
612
+
613
+ # @param rooted_tag [String] A fully qualified namespace, with
614
+ # generic parameter values if applicable
615
+ # @param scope [Symbol] :class or :instance
616
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
617
+ # @param deep [Boolean]
618
+ # @param skip [Set<String>]
619
+ # @param no_core [Boolean] Skip core classes if true
620
+ # @return [Array<Pin::Base>]
621
+ def inner_get_methods rooted_tag, scope, visibility, deep, skip, no_core = false
622
+ rooted_type = ComplexType.parse(rooted_tag).force_rooted
623
+ fqns = rooted_type.namespace
624
+ fqns_generic_params = rooted_type.all_params
625
+ namespace_pin = store.get_path_pins(fqns).select { |p| p.is_a?(Pin::Namespace) }.first
626
+ return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module)$/
627
+ reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
628
+ return [] if skip.include?(reqstr)
629
+ skip.add reqstr
630
+ result = []
631
+ if deep && scope == :instance
632
+ store.get_prepends(fqns).reverse.each do |im|
633
+ fqim = qualify(im, fqns)
634
+ result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
635
+ end
636
+ end
637
+ # Store#get_methods doesn't know about full tags, just
638
+ # namespaces; resolving the generics in the method pins is this
639
+ # class' responsibility
640
+ methods = store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
641
+ result.concat methods
642
+ if deep
643
+ if scope == :instance
644
+ store.get_includes(fqns).reverse.each do |include_tag|
645
+ rooted_include_tag = qualify(include_tag, rooted_tag)
646
+ # Ensure the types returned by the included methods are
647
+ # relative to the generics passed to the include. e.g.,
648
+ # Foo<String> might include Enumerable<String>
649
+ #
650
+ # @todo perform the same translation in the other areas
651
+ # here after adding a spec and handling things correctly
652
+ # in ApiMap::Store and RbsMap::Conversions
653
+ resolved_include_type = ComplexType.parse(rooted_include_tag).force_rooted.resolve_generics(namespace_pin, rooted_type)
654
+ methods = inner_get_methods(resolved_include_type.tag, scope, visibility, deep, skip, true)
655
+ result.concat methods
656
+ end
657
+ fqsc = qualify_superclass(fqns)
658
+ unless fqsc.nil?
659
+ result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
660
+ end
661
+ else
662
+ store.get_extends(fqns).reverse.each do |em|
663
+ fqem = qualify(em, fqns)
664
+ result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
665
+ end
666
+ fqsc = qualify_superclass(fqns)
667
+ unless fqsc.nil?
668
+ result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
669
+ end
670
+ unless no_core || fqns.empty?
671
+ type = get_namespace_type(fqns)
672
+ result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
673
+ result.concat inner_get_methods('Module', :instance, visibility, deep, skip, no_core)
674
+ end
675
+ end
676
+ store.domains(fqns).each do |d|
677
+ dt = ComplexType.try_parse(d)
678
+ result.concat inner_get_methods(dt.namespace, dt.scope, visibility, deep, skip)
679
+ end
680
+ end
681
+ result
682
+ end
683
+
684
+ # @param fqns [String]
685
+ # @param visibility [Array<Symbol>]
686
+ # @param skip [Set<String>]
687
+ # @return [Array<Pin::Base>]
688
+ def inner_get_constants fqns, visibility, skip
689
+ return [] if fqns.nil? || skip.include?(fqns)
690
+ skip.add fqns
691
+ result = []
692
+ store.get_prepends(fqns).each do |is|
693
+ result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
694
+ end
695
+ result.concat store.get_constants(fqns, visibility)
696
+ .sort { |a, b| a.name <=> b.name }
697
+ store.get_includes(fqns).each do |is|
698
+ result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
699
+ end
700
+ fqsc = qualify_superclass(fqns)
701
+ unless %w[Object BasicObject].include?(fqsc)
702
+ result.concat inner_get_constants(fqsc, [:public], skip)
703
+ end
704
+ result
705
+ end
706
+
707
+ # @return [Hash]
708
+ def path_macros
709
+ @path_macros ||= {}
710
+ end
711
+
712
+ # @param namespace [String]
713
+ # @param context [String]
714
+ # @return [String, nil]
715
+ def qualify_lower namespace, context
716
+ qualify namespace, context.split('::')[0..-2].join('::')
717
+ end
718
+
719
+ # @param fqsub [String]
720
+ # @return [String, nil]
721
+ def qualify_superclass fqsub
722
+ sup = store.get_superclass(fqsub)
723
+ return nil if sup.nil?
724
+ parts = fqsub.split('::')
725
+ last = parts.pop
726
+ parts.pop if last == sup
727
+ qualify(sup, parts.join('::'))
728
+ end
729
+
730
+ # @param name [String] Namespace to fully qualify
731
+ # @param root [String] The context to search
732
+ # @param skip [Set<String>] Contexts already searched
733
+ # @return [String, nil] Fully qualified ("rooted") namespace
734
+ def inner_qualify name, root, skip
735
+ return name if name == ComplexType::GENERIC_TAG_NAME
736
+ return nil if name.nil?
737
+ return nil if skip.include?(root)
738
+ skip.add root
739
+ possibles = []
740
+ if name == ''
741
+ if root == ''
742
+ return ''
743
+ else
744
+ return inner_qualify(root, '', skip)
745
+ end
746
+ else
747
+ return name if root == '' && store.namespace_exists?(name)
748
+ roots = root.to_s.split('::')
749
+ while roots.length > 0
750
+ fqns = roots.join('::') + '::' + name
751
+ return fqns if store.namespace_exists?(fqns)
752
+ incs = store.get_includes(roots.join('::'))
753
+ incs.each do |inc|
754
+ foundinc = inner_qualify(name, inc, skip)
755
+ possibles.push foundinc unless foundinc.nil?
756
+ end
757
+ roots.pop
758
+ end
759
+ if possibles.empty?
760
+ incs = store.get_includes('')
761
+ incs.each do |inc|
762
+ foundinc = inner_qualify(name, inc, skip)
763
+ possibles.push foundinc unless foundinc.nil?
764
+ end
765
+ end
766
+ return name if store.namespace_exists?(name)
767
+ return possibles.last
768
+ end
769
+ end
770
+
771
+ # Get the namespace's type (Class or Module).
772
+ #
773
+ # @param fqns [String] A fully qualified namespace
774
+ # @return [Symbol, nil] :class, :module, or nil
775
+ def get_namespace_type fqns
776
+ return nil if fqns.nil?
777
+ # @type [Pin::Namespace, nil]
778
+ pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
779
+ return nil if pin.nil?
780
+ pin.type
781
+ end
782
+
783
+ # Sort an array of pins to put nil or undefined variables last.
784
+ #
785
+ # @param pins [Enumerable<Pin::BaseVariable>]
786
+ # @return [Enumerable<Pin::BaseVariable>]
787
+ def prefer_non_nil_variables pins
788
+ result = []
789
+ nil_pins = []
790
+ pins.each do |pin|
791
+ if pin.variable? && pin.nil_assignment?
792
+ nil_pins.push pin
793
+ else
794
+ result.push pin
795
+ end
796
+ end
797
+ result + nil_pins
798
+ end
799
+
800
+ # @param pins [Enumerable<Pin::Base>]
801
+ # @param visibility [Enumerable<Symbol>]
802
+ # @return [Array<Pin::Base>]
803
+ def resolve_method_aliases pins, visibility = [:public, :private, :protected]
804
+ pins.map do |pin|
805
+ resolved = resolve_method_alias(pin)
806
+ next pin if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
807
+ resolved
808
+ end.compact
809
+ end
810
+
811
+ # @param pin [Pin::MethodAlias, Pin::Base]
812
+ # @return [Pin::Method]
813
+ def resolve_method_alias pin
814
+ return pin unless pin.is_a?(Pin::MethodAlias)
815
+ return nil if @method_alias_stack.include?(pin.path)
816
+ @method_alias_stack.push pin.path
817
+ origin = get_method_stack(pin.full_context.tag, pin.original, scope: pin.scope).first
818
+ @method_alias_stack.pop
819
+ return nil if origin.nil?
820
+ args = {
821
+ location: pin.location,
822
+ closure: pin.closure,
823
+ name: pin.name,
824
+ comments: origin.comments,
825
+ scope: origin.scope,
826
+ # context: pin.context,
827
+ visibility: origin.visibility,
828
+ signatures: origin.signatures,
829
+ attribute: origin.attribute?,
830
+ generics: origin.generics,
831
+ return_type: origin.return_type,
832
+ }
833
+ Pin::Method.new **args
834
+ end
835
+
836
+ include Logging
837
+
838
+ private
839
+
840
+ # @param namespace_pin [Pin::Namespace]
841
+ # @param rooted_type [ComplexType]
842
+ # @param pins [Enumerable<Pin::Base>]
843
+ # @return [Array<Pin::Base>]
844
+ def erase_generics(namespace_pin, rooted_type, pins)
845
+ return pins unless should_erase_generics_when_done?(namespace_pin, rooted_type)
846
+
847
+ logger.debug("Erasing generics on namespace_pin=#{namespace_pin} / rooted_type=#{rooted_type}")
848
+ pins.map do |method_pin|
849
+ method_pin.erase_generics(namespace_pin.generics)
850
+ end
851
+ end
852
+
853
+ # @param namespace_pin [Pin::Namespace]
854
+ # @param rooted_type [ComplexType]
855
+ def should_erase_generics_when_done?(namespace_pin, rooted_type)
856
+ has_generics?(namespace_pin) && !can_resolve_generics?(namespace_pin, rooted_type)
857
+ end
858
+
859
+ # @param namespace_pin [Pin::Namespace]
860
+ def has_generics?(namespace_pin)
861
+ namespace_pin && !namespace_pin.generics.empty?
862
+ end
863
+
864
+ # @param namespace_pin [Pin::Namespace]
865
+ # @param rooted_type [ComplexType]
866
+ def can_resolve_generics?(namespace_pin, rooted_type)
867
+ has_generics?(namespace_pin) && !rooted_type.all_params.empty?
868
+ end
869
+ end
870
+ end