solargraph 0.46.0 → 0.47.1

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