solargraph 0.45.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -41
  3. data/.gitignore +9 -9
  4. data/.rspec +2 -2
  5. data/.travis.yml +19 -19
  6. data/CHANGELOG.md +1115 -1109
  7. data/Gemfile +0 -0
  8. data/LICENSE +0 -0
  9. data/README.md +128 -128
  10. data/Rakefile +0 -0
  11. data/SPONSORS.md +18 -17
  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 -30
  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 -135
  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 -144
  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 -63
  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 -203
  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 -529
  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 -208
  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 -445
  243. data/lib/solargraph.rb +69 -69
  244. data/lib/yard-solargraph.rb +33 -33
  245. data/solargraph.gemspec +0 -0
  246. metadata +13 -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, visibility, 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, visibility, 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