solargraph 0.44.2 → 0.46.0

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 (246) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -0
  3. data/.gitignore +9 -9
  4. data/.rspec +2 -2
  5. data/.travis.yml +19 -19
  6. data/CHANGELOG.md +1115 -1088
  7. data/Gemfile +0 -0
  8. data/LICENSE +0 -0
  9. data/README.md +128 -120
  10. data/Rakefile +0 -0
  11. data/SPONSORS.md +18 -15
  12. data/bin/solargraph +0 -0
  13. data/lib/solargraph/api_map/bundler_methods.rb +22 -22
  14. data/lib/solargraph/api_map/cache.rb +70 -70
  15. data/lib/solargraph/api_map/source_to_yard.rb +81 -81
  16. data/lib/solargraph/api_map/store.rb +256 -256
  17. data/lib/solargraph/api_map.rb +686 -681
  18. data/lib/solargraph/bench.rb +27 -27
  19. data/lib/solargraph/compat.rb +37 -37
  20. data/lib/solargraph/complex_type/type_methods.rb +130 -130
  21. data/lib/solargraph/complex_type/unique_type.rb +75 -75
  22. data/lib/solargraph/complex_type.rb +221 -221
  23. data/lib/solargraph/convention/base.rb +33 -33
  24. data/lib/solargraph/convention/gemfile.rb +15 -15
  25. data/lib/solargraph/convention/gemspec.rb +22 -22
  26. data/lib/solargraph/convention/rspec.rb +30 -21
  27. data/lib/solargraph/convention.rb +47 -47
  28. data/lib/solargraph/converters/dd.rb +12 -12
  29. data/lib/solargraph/converters/dl.rb +12 -12
  30. data/lib/solargraph/converters/dt.rb +12 -12
  31. data/lib/solargraph/converters/misc.rb +1 -1
  32. data/lib/solargraph/diagnostics/base.rb +29 -29
  33. data/lib/solargraph/diagnostics/require_not_found.rb +53 -37
  34. data/lib/solargraph/diagnostics/rubocop.rb +98 -98
  35. data/lib/solargraph/diagnostics/rubocop_helpers.rb +63 -63
  36. data/lib/solargraph/diagnostics/severities.rb +15 -15
  37. data/lib/solargraph/diagnostics/type_check.rb +54 -54
  38. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  39. data/lib/solargraph/diagnostics.rb +55 -55
  40. data/lib/solargraph/documentor.rb +76 -76
  41. data/lib/solargraph/environ.rb +45 -45
  42. data/lib/solargraph/language_server/completion_item_kinds.rb +35 -35
  43. data/lib/solargraph/language_server/error_codes.rb +20 -20
  44. data/lib/solargraph/language_server/host/cataloger.rb +56 -56
  45. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  46. data/lib/solargraph/language_server/host/dispatch.rb +111 -111
  47. data/lib/solargraph/language_server/host/message_worker.rb +59 -59
  48. data/lib/solargraph/language_server/host/sources.rb +156 -156
  49. data/lib/solargraph/language_server/host.rb +865 -865
  50. data/lib/solargraph/language_server/message/base.rb +89 -89
  51. data/lib/solargraph/language_server/message/cancel_request.rb +13 -13
  52. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  53. data/lib/solargraph/language_server/message/client.rb +11 -11
  54. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -58
  55. data/lib/solargraph/language_server/message/completion_item.rb +11 -11
  56. data/lib/solargraph/language_server/message/exit_notification.rb +13 -13
  57. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +100 -100
  58. data/lib/solargraph/language_server/message/extended/document.rb +20 -20
  59. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  60. data/lib/solargraph/language_server/message/extended/download_core.rb +23 -23
  61. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/extended.rb +21 -21
  64. data/lib/solargraph/language_server/message/initialize.rb +162 -162
  65. data/lib/solargraph/language_server/message/initialized.rb +27 -27
  66. data/lib/solargraph/language_server/message/method_not_found.rb +16 -16
  67. data/lib/solargraph/language_server/message/method_not_implemented.rb +14 -14
  68. data/lib/solargraph/language_server/message/shutdown.rb +13 -13
  69. data/lib/solargraph/language_server/message/text_document/base.rb +19 -19
  70. data/lib/solargraph/language_server/message/text_document/code_action.rb +17 -17
  71. data/lib/solargraph/language_server/message/text_document/completion.rb +59 -59
  72. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -38
  73. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -15
  74. data/lib/solargraph/language_server/message/text_document/did_close.rb +15 -15
  75. data/lib/solargraph/language_server/message/text_document/did_open.rb +15 -15
  76. data/lib/solargraph/language_server/message/text_document/did_save.rb +17 -17
  77. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  78. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +23 -23
  79. data/lib/solargraph/language_server/message/text_document/folding_range.rb +26 -26
  80. data/lib/solargraph/language_server/message/text_document/formatting.rb +126 -126
  81. data/lib/solargraph/language_server/message/text_document/hover.rb +54 -44
  82. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +34 -34
  83. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  84. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  85. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  86. data/lib/solargraph/language_server/message/text_document/signature_help.rb +29 -29
  87. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  88. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +30 -30
  89. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +33 -33
  90. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +24 -24
  91. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  92. data/lib/solargraph/language_server/message/workspace.rb +14 -14
  93. data/lib/solargraph/language_server/message.rb +93 -93
  94. data/lib/solargraph/language_server/message_types.rb +14 -14
  95. data/lib/solargraph/language_server/request.rb +24 -24
  96. data/lib/solargraph/language_server/symbol_kinds.rb +36 -36
  97. data/lib/solargraph/language_server/transport/adapter.rb +53 -53
  98. data/lib/solargraph/language_server/transport/data_reader.rb +72 -72
  99. data/lib/solargraph/language_server/transport.rb +13 -13
  100. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  101. data/lib/solargraph/language_server.rb +19 -19
  102. data/lib/solargraph/library.rb +546 -546
  103. data/lib/solargraph/location.rb +37 -37
  104. data/lib/solargraph/logging.rb +27 -27
  105. data/lib/solargraph/page.rb +83 -83
  106. data/lib/solargraph/parser/comment_ripper.rb +52 -52
  107. data/lib/solargraph/parser/legacy/class_methods.rb +135 -140
  108. data/lib/solargraph/parser/legacy/flawed_builder.rb +16 -16
  109. data/lib/solargraph/parser/legacy/node_chainer.rb +148 -148
  110. data/lib/solargraph/parser/legacy/node_methods.rb +325 -325
  111. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +23 -23
  112. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +35 -35
  113. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +15 -15
  114. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +42 -42
  115. data/lib/solargraph/parser/legacy/node_processors/casgn_node.rb +25 -25
  116. data/lib/solargraph/parser/legacy/node_processors/cvasgn_node.rb +23 -23
  117. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +63 -63
  118. data/lib/solargraph/parser/legacy/node_processors/defs_node.rb +36 -36
  119. data/lib/solargraph/parser/legacy/node_processors/gvasgn_node.rb +23 -23
  120. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +38 -38
  121. data/lib/solargraph/parser/legacy/node_processors/lvasgn_node.rb +28 -28
  122. data/lib/solargraph/parser/legacy/node_processors/namespace_node.rb +39 -39
  123. data/lib/solargraph/parser/legacy/node_processors/orasgn_node.rb +16 -16
  124. data/lib/solargraph/parser/legacy/node_processors/resbody_node.rb +36 -36
  125. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +21 -21
  126. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +257 -257
  127. data/lib/solargraph/parser/legacy/node_processors/sym_node.rb +18 -18
  128. data/lib/solargraph/parser/legacy/node_processors.rb +54 -54
  129. data/lib/solargraph/parser/legacy.rb +12 -12
  130. data/lib/solargraph/parser/node_methods.rb +43 -43
  131. data/lib/solargraph/parser/node_processor/base.rb +77 -77
  132. data/lib/solargraph/parser/node_processor.rb +43 -43
  133. data/lib/solargraph/parser/region.rb +66 -66
  134. data/lib/solargraph/parser/rubyvm/class_methods.rb +144 -155
  135. data/lib/solargraph/parser/rubyvm/node_chainer.rb +160 -160
  136. data/lib/solargraph/parser/rubyvm/node_methods.rb +315 -315
  137. data/lib/solargraph/parser/rubyvm/node_processors/alias_node.rb +23 -23
  138. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +85 -85
  139. data/lib/solargraph/parser/rubyvm/node_processors/begin_node.rb +15 -15
  140. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +42 -42
  141. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +22 -22
  142. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +23 -23
  143. data/lib/solargraph/parser/rubyvm/node_processors/def_node.rb +63 -64
  144. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +57 -57
  145. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +23 -23
  146. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +38 -38
  147. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +39 -39
  148. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +20 -20
  149. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +27 -27
  150. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +39 -39
  151. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +26 -26
  152. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +15 -15
  153. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +45 -45
  154. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +21 -21
  155. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +15 -15
  156. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +277 -277
  157. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +18 -18
  158. data/lib/solargraph/parser/rubyvm/node_processors.rb +63 -62
  159. data/lib/solargraph/parser/rubyvm.rb +40 -40
  160. data/lib/solargraph/parser/snippet.rb +13 -13
  161. data/lib/solargraph/parser.rb +26 -26
  162. data/lib/solargraph/pin/base.rb +296 -296
  163. data/lib/solargraph/pin/base_variable.rb +84 -84
  164. data/lib/solargraph/pin/block.rb +72 -72
  165. data/lib/solargraph/pin/class_variable.rb +8 -8
  166. data/lib/solargraph/pin/closure.rb +37 -37
  167. data/lib/solargraph/pin/common.rb +70 -70
  168. data/lib/solargraph/pin/constant.rb +43 -43
  169. data/lib/solargraph/pin/conversions.rb +96 -96
  170. data/lib/solargraph/pin/documenting.rb +105 -105
  171. data/lib/solargraph/pin/duck_method.rb +16 -16
  172. data/lib/solargraph/pin/global_variable.rb +8 -8
  173. data/lib/solargraph/pin/instance_variable.rb +30 -30
  174. data/lib/solargraph/pin/keyword.rb +15 -15
  175. data/lib/solargraph/pin/keyword_param.rb +8 -8
  176. data/lib/solargraph/pin/local_variable.rb +55 -55
  177. data/lib/solargraph/pin/method.rb +245 -245
  178. data/lib/solargraph/pin/method_alias.rb +31 -31
  179. data/lib/solargraph/pin/namespace.rb +91 -91
  180. data/lib/solargraph/pin/parameter.rb +201 -206
  181. data/lib/solargraph/pin/proxy_type.rb +29 -29
  182. data/lib/solargraph/pin/reference/extend.rb +10 -10
  183. data/lib/solargraph/pin/reference/include.rb +10 -10
  184. data/lib/solargraph/pin/reference/override.rb +29 -29
  185. data/lib/solargraph/pin/reference/prepend.rb +10 -10
  186. data/lib/solargraph/pin/reference/require.rb +14 -14
  187. data/lib/solargraph/pin/reference/superclass.rb +10 -10
  188. data/lib/solargraph/pin/reference.rb +14 -14
  189. data/lib/solargraph/pin/search.rb +56 -0
  190. data/lib/solargraph/pin/singleton.rb +11 -11
  191. data/lib/solargraph/pin/symbol.rb +47 -47
  192. data/lib/solargraph/pin.rb +37 -36
  193. data/lib/solargraph/position.rb +100 -100
  194. data/lib/solargraph/range.rb +95 -95
  195. data/lib/solargraph/server_methods.rb +16 -16
  196. data/lib/solargraph/shell.rb +226 -226
  197. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  198. data/lib/solargraph/source/chain/call.rb +204 -204
  199. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  200. data/lib/solargraph/source/chain/constant.rb +75 -75
  201. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  202. data/lib/solargraph/source/chain/hash.rb +28 -28
  203. data/lib/solargraph/source/chain/head.rb +19 -19
  204. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  205. data/lib/solargraph/source/chain/link.rb +71 -71
  206. data/lib/solargraph/source/chain/literal.rb +23 -23
  207. data/lib/solargraph/source/chain/or.rb +23 -23
  208. data/lib/solargraph/source/chain/q_call.rb +11 -11
  209. data/lib/solargraph/source/chain/variable.rb +13 -13
  210. data/lib/solargraph/source/chain/z_super.rb +30 -30
  211. data/lib/solargraph/source/chain.rb +164 -164
  212. data/lib/solargraph/source/change.rb +79 -79
  213. data/lib/solargraph/source/cursor.rb +164 -164
  214. data/lib/solargraph/source/source_chainer.rb +191 -191
  215. data/lib/solargraph/source/updater.rb +54 -54
  216. data/lib/solargraph/source.rb +522 -522
  217. data/lib/solargraph/source_map/clip.rb +224 -224
  218. data/lib/solargraph/source_map/completion.rb +23 -23
  219. data/lib/solargraph/source_map/mapper.rb +212 -212
  220. data/lib/solargraph/source_map.rb +180 -189
  221. data/lib/solargraph/type_checker/checks.rb +99 -99
  222. data/lib/solargraph/type_checker/param_def.rb +35 -35
  223. data/lib/solargraph/type_checker/problem.rb +32 -32
  224. data/lib/solargraph/type_checker/rules.rb +57 -57
  225. data/lib/solargraph/type_checker.rb +543 -510
  226. data/lib/solargraph/version.rb +5 -5
  227. data/lib/solargraph/views/environment.erb +58 -58
  228. data/lib/solargraph/workspace/config.rb +231 -231
  229. data/lib/solargraph/workspace.rb +215 -214
  230. data/lib/solargraph/yard_map/cache.rb +19 -19
  231. data/lib/solargraph/yard_map/core_docs.rb +170 -170
  232. data/lib/solargraph/yard_map/core_fills.rb +208 -203
  233. data/lib/solargraph/yard_map/core_gen.rb +76 -76
  234. data/lib/solargraph/yard_map/helpers.rb +16 -16
  235. data/lib/solargraph/yard_map/mapper/to_constant.rb +25 -25
  236. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -78
  237. data/lib/solargraph/yard_map/mapper/to_namespace.rb +27 -27
  238. data/lib/solargraph/yard_map/mapper.rb +77 -77
  239. data/lib/solargraph/yard_map/rdoc_to_yard.rb +140 -140
  240. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -43
  241. data/lib/solargraph/yard_map/to_method.rb +79 -79
  242. data/lib/solargraph/yard_map.rb +460 -443
  243. data/lib/solargraph.rb +69 -69
  244. data/lib/yard-solargraph.rb +33 -33
  245. data/solargraph.gemspec +0 -0
  246. metadata +14 -12
@@ -1,681 +1,686 @@
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
- # @param pins [Array<Solargraph::Pin::Base>]
25
- def initialize pins: []
26
- @source_map_hash = {}
27
- @cache = Cache.new
28
- @method_alias_stack = []
29
- index pins
30
- end
31
-
32
- # @param pins [Array<Pin::Base>]
33
- # @return [self]
34
- def index pins
35
- # @todo This implementation is incomplete. It should probably create a
36
- # Bench.
37
- @source_map_hash = {}
38
- implicit.clear
39
- cache.clear
40
- @store = Store.new(yard_map.pins + pins)
41
- self
42
- end
43
-
44
- # Map a single source.
45
- #
46
- # @param source [Source]
47
- # @return [self]
48
- def map source
49
- map = Solargraph::SourceMap.map(source)
50
- catalog Bench.new(source_maps: [map])
51
- self
52
- end
53
-
54
- # Catalog a bench.
55
- #
56
- # @param bench [Bench]
57
- def catalog bench
58
- implicit.clear
59
- @cache.clear
60
- @source_map_hash = bench.source_maps.map { |s| [s.filename, s] }.to_h
61
- pins = bench.source_maps.map(&:pins).flatten
62
- external_requires = bench.external_requires
63
- source_map_hash.each_value do |map|
64
- implicit.merge map.environ
65
- end
66
- external_requires.merge implicit.requires
67
- external_requires.merge bench.workspace.config.required
68
- yard_map.change(external_requires, bench.workspace.directory, bench.workspace.source_gems)
69
- @store = Store.new(yard_map.pins + implicit.pins + pins)
70
- @unresolved_requires = yard_map.unresolved_requires
71
- @rebindable_method_names = nil
72
- store.block_pins.each { |blk| blk.rebind(self) }
73
- self
74
- end
75
-
76
- # @param name [String]
77
- # @return [YARD::Tags::MacroDirective, nil]
78
- def named_macro name
79
- store.named_macros[name]
80
- end
81
-
82
- def required
83
- @required ||= Set.new
84
- end
85
-
86
- # @return [Environ]
87
- def implicit
88
- @implicit ||= Environ.new
89
- end
90
-
91
- # @param filename [String]
92
- # @param position [Position, Array(Integer, Integer)]
93
- # @return [Source::Cursor]
94
- def cursor_at filename, position
95
- position = Position.normalize(position)
96
- raise FileNotFoundError, "File not found: #{filename}" unless source_map_hash.key?(filename)
97
- source_map_hash[filename].cursor_at(position)
98
- end
99
-
100
- # Get a clip by filename and position.
101
- #
102
- # @param filename [String]
103
- # @param position [Position, Array(Integer, Integer)]
104
- # @return [SourceMap::Clip]
105
- def clip_at filename, position
106
- position = Position.normalize(position)
107
- SourceMap::Clip.new(self, cursor_at(filename, position))
108
- end
109
-
110
- # Create an ApiMap with a workspace in the specified directory.
111
- #
112
- # @param directory [String]
113
- # @return [ApiMap]
114
- def self.load directory
115
- api_map = new
116
- workspace = Solargraph::Workspace.new(directory)
117
- # api_map.catalog Bench.new(workspace: workspace)
118
- library = Library.new(workspace)
119
- library.map!
120
- api_map.catalog library.bench
121
- api_map
122
- end
123
-
124
- # @return [Array<Solargraph::Pin::Base>]
125
- def pins
126
- store.pins
127
- end
128
-
129
- def rebindable_method_names
130
- @rebindable_method_names ||= begin
131
- result = yard_map.rebindable_method_names
132
- source_maps.each do |map|
133
- result.merge map.rebindable_method_names
134
- end
135
- result
136
- end
137
- end
138
-
139
- # An array of pins based on Ruby keywords (`if`, `end`, etc.).
140
- #
141
- # @return [Enumerable<Solargraph::Pin::Keyword>]
142
- def keyword_pins
143
- store.pins_by_class(Pin::Keyword)
144
- end
145
-
146
- # An array of namespace names defined in the ApiMap.
147
- #
148
- # @return [Set<String>]
149
- def namespaces
150
- store.namespaces
151
- end
152
-
153
- # True if the namespace exists.
154
- #
155
- # @param name [String] The namespace to match
156
- # @param context [String] The context to search
157
- # @return [Boolean]
158
- def namespace_exists? name, context = ''
159
- !qualify(name, context).nil?
160
- end
161
-
162
- # Get suggestions for constants in the specified namespace. The result
163
- # may contain both constant and namespace pins.
164
- #
165
- # @param namespace [String] The namespace
166
- # @param contexts [Array<String>] The contexts
167
- # @return [Array<Solargraph::Pin::Base>]
168
- def get_constants namespace, *contexts
169
- namespace ||= ''
170
- contexts.push '' if contexts.empty?
171
- cached = cache.get_constants(namespace, contexts)
172
- return cached.clone unless cached.nil?
173
- skip = Set.new
174
- result = []
175
- contexts.each do |context|
176
- fqns = qualify(namespace, context)
177
- visibility = [:public]
178
- visibility.push :private if fqns == context
179
- result.concat inner_get_constants(fqns, visibility, skip)
180
- end
181
- cache.set_constants(namespace, contexts, result)
182
- result
183
- end
184
-
185
- # Get a fully qualified namespace name. This method will start the search
186
- # in the specified context until it finds a match for the name.
187
- #
188
- # @param namespace [String, nil] The namespace to match
189
- # @param context [String] The context to search
190
- # @return [String]
191
- def qualify namespace, context = ''
192
- return namespace if ['self', nil].include?(namespace)
193
- cached = cache.get_qualified_namespace(namespace, context)
194
- return cached.clone unless cached.nil?
195
- result = if namespace.start_with?('::')
196
- inner_qualify(namespace[2..-1], '', Set.new)
197
- else
198
- inner_qualify(namespace, context, Set.new)
199
- end
200
- cache.set_qualified_namespace(namespace, context, result)
201
- result
202
- end
203
-
204
- # Get an array of instance variable pins defined in specified namespace
205
- # and scope.
206
- #
207
- # @param namespace [String] A fully qualified namespace
208
- # @param scope [Symbol] :instance or :class
209
- # @return [Array<Solargraph::Pin::InstanceVariable>]
210
- def get_instance_variable_pins(namespace, scope = :instance)
211
- result = []
212
- used = [namespace]
213
- result.concat store.get_instance_variables(namespace, scope)
214
- sc = qualify_lower(store.get_superclass(namespace), namespace)
215
- until sc.nil? || used.include?(sc)
216
- used.push sc
217
- result.concat store.get_instance_variables(sc, scope)
218
- sc = qualify_lower(store.get_superclass(sc), sc)
219
- end
220
- result
221
- end
222
-
223
- # Get an array of class variable pins for a namespace.
224
- #
225
- # @param namespace [String] A fully qualified namespace
226
- # @return [Array<Solargraph::Pin::ClassVariable>]
227
- def get_class_variable_pins(namespace)
228
- prefer_non_nil_variables(store.get_class_variables(namespace))
229
- end
230
-
231
- # @return [Array<Solargraph::Pin::Base>]
232
- def get_symbols
233
- store.get_symbols
234
- end
235
-
236
- # @return [Array<Solargraph::Pin::GlobalVariable>]
237
- def get_global_variable_pins
238
- store.pins_by_class(Pin::GlobalVariable)
239
- end
240
-
241
- # Get an array of methods available in a particular context.
242
- #
243
- # @param fqns [String] The fully qualified namespace to search for methods
244
- # @param scope [Symbol] :class or :instance
245
- # @param visibility [Array<Symbol>] :public, :protected, and/or :private
246
- # @param deep [Boolean] True to include superclasses, mixins, etc.
247
- # @return [Array<Solargraph::Pin::Method>]
248
- def get_methods fqns, scope: :instance, visibility: [:public], deep: true
249
- cached = cache.get_methods(fqns, scope, visibility, deep)
250
- return cached.clone unless cached.nil?
251
- result = []
252
- skip = Set.new
253
- if fqns == ''
254
- # @todo Implement domains
255
- implicit.domains.each do |domain|
256
- type = ComplexType.try_parse(domain)
257
- next if type.undefined?
258
- result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
259
- end
260
- result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
261
- result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
262
- result.concat inner_get_methods('Kernel', :instance, visibility, deep, skip)
263
- else
264
- result.concat inner_get_methods(fqns, scope, visibility, deep, skip)
265
- result.concat inner_get_methods('Kernel', :instance, [:public], deep, skip) if visibility.include?(:private)
266
- end
267
- resolved = resolve_method_aliases(result, visibility)
268
- cache.set_methods(fqns, scope, visibility, deep, resolved)
269
- resolved
270
- end
271
-
272
- # Get an array of method pins for a complex type.
273
- #
274
- # The type's namespace and the context should be fully qualified. If the
275
- # context matches the namespace type or is a subclass of the type,
276
- # protected methods are included in the results. If protected methods are
277
- # included and internal is true, private methods are also included.
278
- #
279
- # @example
280
- # api_map = Solargraph::ApiMap.new
281
- # type = Solargraph::ComplexType.parse('String')
282
- # api_map.get_complex_type_methods(type)
283
- #
284
- # @param type [Solargraph::ComplexType] The complex type of the namespace
285
- # @param context [String] The context from which the type is referenced
286
- # @param internal [Boolean] True to include private methods
287
- # @return [Array<Solargraph::Pin::Base>]
288
- def get_complex_type_methods type, context = '', internal = false
289
- # This method does not qualify the complex type's namespace because
290
- # it can cause conflicts between similar names, e.g., `Foo` vs.
291
- # `Other::Foo`. It still takes a context argument to determine whether
292
- # protected and private methods are visible.
293
- return [] if type.undefined? || type.void?
294
- result = []
295
- if type.duck_type?
296
- type.select(&:duck_type?).each do |t|
297
- result.push Pin::DuckMethod.new(name: t.tag[1..-1])
298
- end
299
- result.concat get_methods('Object')
300
- else
301
- unless type.nil? || type.name == 'void'
302
- visibility = [:public]
303
- if type.namespace == context || super_and_sub?(type.namespace, context)
304
- visibility.push :protected
305
- visibility.push :private if internal
306
- end
307
- result.concat get_methods(type.namespace, scope: type.scope, visibility: visibility)
308
- end
309
- end
310
- result
311
- end
312
-
313
- # Get a stack of method pins for a method name in a namespace. The order
314
- # of the pins corresponds to the ancestry chain, with highest precedence
315
- # first.
316
- #
317
- # @example
318
- # api_map.get_method_stack('Subclass', 'method_name')
319
- # #=> [ <Subclass#method_name pin>, <Superclass#method_name pin> ]
320
- #
321
- # @param fqns [String]
322
- # @param name [String]
323
- # @param scope [Symbol] :instance or :class
324
- # @return [Array<Solargraph::Pin::Method>]
325
- def get_method_stack fqns, name, scope: :instance
326
- get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select { |p| p.name == name }
327
- end
328
-
329
- # Get an array of all suggestions that match the specified path.
330
- #
331
- # @deprecated Use #get_path_pins instead.
332
- #
333
- # @param path [String] The path to find
334
- # @return [Array<Solargraph::Pin::Base>]
335
- def get_path_suggestions path
336
- return [] if path.nil?
337
- resolve_method_aliases store.get_path_pins(path)
338
- end
339
-
340
- # Get an array of pins that match the specified path.
341
- #
342
- # @param path [String]
343
- # @return [Array<Pin::Base>]
344
- def get_path_pins path
345
- get_path_suggestions(path)
346
- end
347
-
348
- # Get a list of documented paths that match the query.
349
- #
350
- # @example
351
- # api_map.query('str') # Results will include `String` and `Struct`
352
- #
353
- # @param query [String] The text to match
354
- # @return [Array<String>]
355
- def search query
356
- rake_yard(store)
357
- found = []
358
- code_object_paths.each do |k|
359
- if (found.empty? || (query.include?('.') || query.include?('#')) || !(k.include?('.') || k.include?('#'))) &&
360
- k.downcase.include?(query.downcase)
361
- found.push k
362
- end
363
- end
364
- found
365
- end
366
-
367
- # Get YARD documentation for the specified path.
368
- #
369
- # @example
370
- # api_map.document('String#split')
371
- #
372
- # @param path [String] The path to find
373
- # @return [Array<YARD::CodeObjects::Base>]
374
- def document path
375
- rake_yard(store)
376
- docs = []
377
- docs.push code_object_at(path) unless code_object_at(path).nil?
378
- docs
379
- end
380
-
381
- # Get an array of all symbols in the workspace that match the query.
382
- #
383
- # @param query [String]
384
- # @return [Array<Pin::Base>]
385
- def query_symbols query
386
- result = []
387
- source_map_hash.each_value { |s| result.concat s.query_symbols(query) }
388
- result
389
- end
390
-
391
- # @param location [Solargraph::Location]
392
- # @return [Array<Solargraph::Pin::Base>]
393
- def locate_pins location
394
- return [] if location.nil? || !source_map_hash.key?(location.filename)
395
- resolve_method_aliases source_map_hash[location.filename].locate_pins(location)
396
- end
397
-
398
- # @raise [FileNotFoundError] if the cursor's file is not in the ApiMap
399
- # @param cursor [Source::Cursor]
400
- # @return [SourceMap::Clip]
401
- def clip cursor
402
- raise FileNotFoundError, "ApiMap did not catalog #{cursor.filename}" unless source_map_hash.key?(cursor.filename)
403
- SourceMap::Clip.new(self, cursor)
404
- end
405
-
406
- # Get an array of document symbols from a file.
407
- #
408
- # @param filename [String]
409
- # @return [Array<Pin::Symbol>]
410
- def document_symbols filename
411
- return [] unless source_map_hash.key?(filename) # @todo Raise error?
412
- resolve_method_aliases source_map_hash[filename].document_symbols
413
- end
414
-
415
- # @return [Array<SourceMap>]
416
- def source_maps
417
- source_map_hash.values
418
- end
419
-
420
- # Get a source map by filename.
421
- #
422
- # @param filename [String]
423
- # @return [SourceMap]
424
- def source_map filename
425
- raise FileNotFoundError, "Source map for `#{filename}` not found" unless source_map_hash.key?(filename)
426
- source_map_hash[filename]
427
- end
428
-
429
- # True if the specified file was included in a bundle, i.e., it's either
430
- # included in a workspace or open in a library.
431
- #
432
- # @param filename [String]
433
- def bundled? filename
434
- source_map_hash.keys.include?(filename)
435
- end
436
-
437
- # Check if a class is a superclass of another class.
438
- #
439
- # @param sup [String] The superclass
440
- # @param sub [String] The subclass
441
- # @return [Boolean]
442
- def super_and_sub?(sup, sub)
443
- fqsup = qualify(sup)
444
- cls = qualify(sub)
445
- until fqsup.nil? || cls.nil?
446
- return true if cls == fqsup
447
- cls = qualify_superclass(cls)
448
- end
449
- false
450
- end
451
-
452
- # @return [YardMap]
453
- def yard_map
454
- @yard_map ||= YardMap.new
455
- end
456
-
457
- # Check if the host class includes the specified module.
458
- #
459
- # @param host [String] The class
460
- # @param mod [String] The module
461
- # @return [Boolean]
462
- def type_include?(host, mod)
463
- store.get_includes(host).include?(mod)
464
- end
465
-
466
- private
467
-
468
- # A hash of source maps with filename keys.
469
- #
470
- # @return [Hash{String => SourceMap}]
471
- attr_reader :source_map_hash
472
-
473
- # @return [ApiMap::Store]
474
- def store
475
- @store ||= Store.new
476
- end
477
-
478
- # @return [Solargraph::ApiMap::Cache]
479
- attr_reader :cache
480
-
481
- # @param fqns [String] A fully qualified namespace
482
- # @param scope [Symbol] :class or :instance
483
- # @param visibility [Array<Symbol>] :public, :protected, and/or :private
484
- # @param deep [Boolean]
485
- # @param skip [Set<String>]
486
- # @param no_core [Boolean] Skip core classes if true
487
- # @return [Array<Pin::Base>]
488
- def inner_get_methods fqns, scope, visibility, deep, skip, no_core = false
489
- return [] if no_core && fqns =~ /^(Object|BasicObject|Class|Module|Kernel)$/
490
- reqstr = "#{fqns}|#{scope}|#{visibility.sort}|#{deep}"
491
- return [] if skip.include?(reqstr)
492
- skip.add reqstr
493
- result = []
494
- if deep && scope == :instance
495
- store.get_prepends(fqns).reverse.each do |im|
496
- fqim = qualify(im, fqns)
497
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
498
- end
499
- end
500
- result.concat store.get_methods(fqns, scope: scope, visibility: visibility).sort{ |a, b| a.name <=> b.name }
501
- if deep
502
- if scope == :instance
503
- store.get_includes(fqns).reverse.each do |im|
504
- fqim = qualify(im, fqns)
505
- result.concat inner_get_methods(fqim, scope, visibility, deep, skip, true) unless fqim.nil?
506
- end
507
- fqsc = qualify_superclass(fqns)
508
- unless fqsc.nil?
509
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, no_core) unless fqsc.nil?
510
- end
511
- else
512
- store.get_extends(fqns).reverse.each do |em|
513
- fqem = qualify(em, fqns)
514
- result.concat inner_get_methods(fqem, :instance, visibility, deep, skip, true) unless fqem.nil?
515
- end
516
- fqsc = qualify_superclass(fqns)
517
- unless fqsc.nil?
518
- result.concat inner_get_methods(fqsc, scope, visibility, true, skip, true) unless fqsc.nil?
519
- end
520
- unless no_core || fqns.empty?
521
- type = get_namespace_type(fqns)
522
- result.concat inner_get_methods('Class', :instance, visibility, deep, skip, no_core) if type == :class
523
- result.concat inner_get_methods('Module', :instance, visibility, deep, skip, no_core)
524
- end
525
- end
526
- store.domains(fqns).each do |d|
527
- dt = ComplexType.try_parse(d)
528
- result.concat inner_get_methods(dt.namespace, dt.scope, [:public], deep, skip)
529
- end
530
- end
531
- result
532
- end
533
-
534
- # @param fqns [String]
535
- # @param visibility [Array<Symbol>]
536
- # @param skip [Set<String>]
537
- # @return [Array<Pin::Base>]
538
- def inner_get_constants fqns, visibility, skip
539
- return [] if fqns.nil? || skip.include?(fqns)
540
- skip.add fqns
541
- result = []
542
- store.get_prepends(fqns).each do |is|
543
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
544
- end
545
- result.concat store.get_constants(fqns, visibility)
546
- .sort { |a, b| a.name <=> b.name }
547
- store.get_includes(fqns).each do |is|
548
- result.concat inner_get_constants(qualify(is, fqns), [:public], skip)
549
- end
550
- fqsc = qualify_superclass(fqns)
551
- unless %w[Object BasicObject].include?(fqsc)
552
- result.concat inner_get_constants(fqsc, [:public], skip)
553
- end
554
- result
555
- end
556
-
557
- # @return [Hash]
558
- def path_macros
559
- @path_macros ||= {}
560
- end
561
-
562
- # @param namespace [String]
563
- # @param context [String]
564
- # @return [String]
565
- def qualify_lower namespace, context
566
- qualify namespace, context.split('::')[0..-2].join('::')
567
- end
568
-
569
- def qualify_superclass fqsub
570
- sup = store.get_superclass(fqsub)
571
- return nil if sup.nil?
572
- parts = fqsub.split('::')
573
- last = parts.pop
574
- parts.pop if last == sup
575
- qualify(sup, parts.join('::'))
576
- end
577
-
578
- # @param name [String]
579
- # @param root [String]
580
- # @param skip [Set<String>]
581
- # @return [String, nil]
582
- def inner_qualify name, root, skip
583
- return nil if name.nil?
584
- return nil if skip.include?(root)
585
- skip.add root
586
- possibles = []
587
- if name == ''
588
- if root == ''
589
- return ''
590
- else
591
- return inner_qualify(root, '', skip)
592
- end
593
- else
594
- return name if root == '' && store.namespace_exists?(name)
595
- roots = root.to_s.split('::')
596
- while roots.length > 0
597
- fqns = roots.join('::') + '::' + name
598
- return fqns if store.namespace_exists?(fqns)
599
- incs = store.get_includes(roots.join('::'))
600
- incs.each do |inc|
601
- foundinc = inner_qualify(name, inc, skip)
602
- possibles.push foundinc unless foundinc.nil?
603
- end
604
- roots.pop
605
- end
606
- if possibles.empty?
607
- incs = store.get_includes('')
608
- incs.each do |inc|
609
- foundinc = inner_qualify(name, inc, skip)
610
- possibles.push foundinc unless foundinc.nil?
611
- end
612
- end
613
- return name if store.namespace_exists?(name)
614
- return possibles.last
615
- end
616
- end
617
-
618
- # Get the namespace's type (Class or Module).
619
- #
620
- # @param fqns [String] A fully qualified namespace
621
- # @return [Symbol, nil] :class, :module, or nil
622
- def get_namespace_type fqns
623
- return nil if fqns.nil?
624
- # @type [Pin::Namespace, nil]
625
- pin = store.get_path_pins(fqns).select{|p| p.is_a?(Pin::Namespace)}.first
626
- return nil if pin.nil?
627
- pin.type
628
- end
629
-
630
- # Sort an array of pins to put nil or undefined variables last.
631
- #
632
- # @param pins [Array<Solargraph::Pin::Base>]
633
- # @return [Array<Solargraph::Pin::Base>]
634
- def prefer_non_nil_variables pins
635
- result = []
636
- nil_pins = []
637
- pins.each do |pin|
638
- if pin.variable? && pin.nil_assignment?
639
- nil_pins.push pin
640
- else
641
- result.push pin
642
- end
643
- end
644
- result + nil_pins
645
- end
646
-
647
- # @param pins [Array<Pin::Base>]
648
- # @param visibility [Array<Symbol>]
649
- # @return [Array<Pin::Base>]
650
- def resolve_method_aliases pins, visibility = [:public, :private, :protected]
651
- result = []
652
- pins.each do |pin|
653
- resolved = resolve_method_alias(pin)
654
- next if resolved.respond_to?(:visibility) && !visibility.include?(resolved.visibility)
655
- result.push resolved
656
- end
657
- result
658
- end
659
-
660
- # @param pin [Pin::MethodAlias, Pin::Base]
661
- # @return [Pin::Method]
662
- def resolve_method_alias pin
663
- return pin if !pin.is_a?(Pin::MethodAlias) || @method_alias_stack.include?(pin.path)
664
- @method_alias_stack.push pin.path
665
- origin = get_method_stack(pin.full_context.namespace, pin.original, scope: pin.scope).first
666
- @method_alias_stack.pop
667
- return pin if origin.nil?
668
- args = {
669
- location: pin.location,
670
- closure: pin.closure,
671
- name: pin.name,
672
- comments: origin.comments,
673
- scope: origin.scope,
674
- visibility: origin.visibility,
675
- parameters: origin.parameters,
676
- attribute: origin.attribute?
677
- }
678
- Pin::Method.new **args
679
- end
680
- end
681
- end
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