solargraph 0.47.2 → 0.53.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/plugins.yml +40 -0
  4. data/.github/workflows/rspec.yml +4 -8
  5. data/.github/workflows/typecheck.yml +34 -0
  6. data/.yardopts +2 -2
  7. data/CHANGELOG.md +137 -3
  8. data/LICENSE +1 -1
  9. data/README.md +19 -16
  10. data/SPONSORS.md +2 -9
  11. data/lib/solargraph/api_map/cache.rb +60 -20
  12. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  13. data/lib/solargraph/api_map/store.rb +60 -12
  14. data/lib/solargraph/api_map.rb +171 -99
  15. data/lib/solargraph/bench.rb +3 -2
  16. data/lib/solargraph/cache.rb +77 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +61 -12
  18. data/lib/solargraph/complex_type/unique_type.rb +193 -16
  19. data/lib/solargraph/complex_type.rb +113 -10
  20. data/lib/solargraph/convention/rakefile.rb +17 -0
  21. data/lib/solargraph/convention.rb +2 -3
  22. data/lib/solargraph/converters/dd.rb +5 -0
  23. data/lib/solargraph/converters/dl.rb +3 -0
  24. data/lib/solargraph/converters/dt.rb +3 -0
  25. data/lib/solargraph/diagnostics/rubocop.rb +23 -8
  26. data/lib/solargraph/diagnostics/rubocop_helpers.rb +4 -1
  27. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  28. data/lib/solargraph/diagnostics.rb +2 -2
  29. data/lib/solargraph/doc_map.rb +171 -0
  30. data/lib/solargraph/gem_pins.rb +64 -0
  31. data/lib/solargraph/language_server/host/cataloger.rb +2 -1
  32. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  33. data/lib/solargraph/language_server/host/dispatch.rb +15 -5
  34. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  35. data/lib/solargraph/language_server/host/sources.rb +7 -4
  36. data/lib/solargraph/language_server/host.rb +50 -26
  37. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  38. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  39. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  40. data/lib/solargraph/language_server/message/initialize.rb +13 -0
  41. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  42. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +4 -1
  43. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -4
  44. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  46. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  47. data/lib/solargraph/language_server/message/text_document.rb +1 -1
  48. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  49. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  50. data/lib/solargraph/language_server/message.rb +1 -0
  51. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  52. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  53. data/lib/solargraph/library.rb +124 -37
  54. data/lib/solargraph/location.rb +1 -0
  55. data/lib/solargraph/page.rb +6 -0
  56. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  57. data/lib/solargraph/parser/node_methods.rb +47 -7
  58. data/lib/solargraph/parser/node_processor/base.rb +9 -0
  59. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -5
  60. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  61. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +57 -41
  62. data/lib/solargraph/parser/parser_gem/node_methods.rb +499 -0
  63. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  64. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +53 -0
  65. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  66. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  67. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +14 -4
  68. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  69. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +7 -20
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  71. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  72. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  77. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
  78. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +2 -2
  79. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +1 -1
  80. data/lib/solargraph/parser/parser_gem/node_processors.rb +54 -0
  81. data/lib/solargraph/parser/parser_gem.rb +12 -0
  82. data/lib/solargraph/parser/region.rb +1 -1
  83. data/lib/solargraph/parser/snippet.rb +2 -0
  84. data/lib/solargraph/parser.rb +9 -10
  85. data/lib/solargraph/pin/base.rb +69 -11
  86. data/lib/solargraph/pin/base_variable.rb +8 -4
  87. data/lib/solargraph/pin/block.rb +21 -28
  88. data/lib/solargraph/pin/closure.rb +17 -2
  89. data/lib/solargraph/pin/common.rb +7 -3
  90. data/lib/solargraph/pin/conversions.rb +34 -8
  91. data/lib/solargraph/pin/delegated_method.rb +97 -0
  92. data/lib/solargraph/pin/documenting.rb +25 -34
  93. data/lib/solargraph/pin/instance_variable.rb +4 -0
  94. data/lib/solargraph/pin/local_variable.rb +13 -1
  95. data/lib/solargraph/pin/method.rb +270 -16
  96. data/lib/solargraph/pin/namespace.rb +17 -1
  97. data/lib/solargraph/pin/parameter.rb +52 -17
  98. data/lib/solargraph/pin/reference/override.rb +2 -2
  99. data/lib/solargraph/pin/reference.rb +8 -0
  100. data/lib/solargraph/pin/search.rb +4 -4
  101. data/lib/solargraph/pin/signature.rb +143 -0
  102. data/lib/solargraph/pin.rb +2 -1
  103. data/lib/solargraph/range.rb +4 -6
  104. data/lib/solargraph/rbs_map/conversions.rb +601 -0
  105. data/lib/solargraph/rbs_map/core_fills.rb +47 -0
  106. data/lib/solargraph/rbs_map/core_map.rb +28 -0
  107. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
  108. data/lib/solargraph/rbs_map.rb +84 -0
  109. data/lib/solargraph/shell.rb +69 -48
  110. data/lib/solargraph/source/chain/array.rb +32 -0
  111. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  112. data/lib/solargraph/source/chain/call.rb +125 -61
  113. data/lib/solargraph/source/chain/constant.rb +15 -1
  114. data/lib/solargraph/source/chain/if.rb +23 -0
  115. data/lib/solargraph/source/chain/link.rb +8 -2
  116. data/lib/solargraph/source/chain/or.rb +1 -1
  117. data/lib/solargraph/source/chain/z_super.rb +3 -3
  118. data/lib/solargraph/source/chain.rb +44 -14
  119. data/lib/solargraph/source/change.rb +3 -0
  120. data/lib/solargraph/source/cursor.rb +2 -0
  121. data/lib/solargraph/source/source_chainer.rb +8 -5
  122. data/lib/solargraph/source.rb +18 -19
  123. data/lib/solargraph/source_map/clip.rb +30 -23
  124. data/lib/solargraph/source_map/mapper.rb +20 -5
  125. data/lib/solargraph/source_map.rb +28 -13
  126. data/lib/solargraph/type_checker/checks.rb +10 -2
  127. data/lib/solargraph/type_checker.rb +201 -98
  128. data/lib/solargraph/version.rb +1 -1
  129. data/lib/solargraph/views/environment.erb +2 -2
  130. data/lib/solargraph/workspace/config.rb +14 -11
  131. data/lib/solargraph/workspace.rb +28 -17
  132. data/lib/solargraph/yard_map/cache.rb +6 -0
  133. data/lib/solargraph/yard_map/helpers.rb +1 -1
  134. data/lib/solargraph/yard_map/mapper/to_method.rb +18 -5
  135. data/lib/solargraph/yard_map/mapper.rb +1 -1
  136. data/lib/solargraph/yard_map/to_method.rb +11 -4
  137. data/lib/solargraph/yard_map.rb +1 -443
  138. data/lib/solargraph/yard_tags.rb +20 -0
  139. data/lib/solargraph/yardoc.rb +52 -0
  140. data/lib/solargraph.rb +8 -6
  141. data/solargraph.gemspec +19 -8
  142. metadata +162 -98
  143. data/.travis.yml +0 -19
  144. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  145. data/lib/solargraph/compat.rb +0 -37
  146. data/lib/solargraph/convention/rspec.rb +0 -30
  147. data/lib/solargraph/documentor.rb +0 -76
  148. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  149. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  150. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  151. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  152. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
  153. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
  154. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  155. data/lib/solargraph/parser/legacy.rb +0 -12
  156. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
  157. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  158. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  159. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  160. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  161. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
  162. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  163. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
  164. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  165. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  166. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  167. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  168. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  169. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  170. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  171. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  172. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  173. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
  174. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  175. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
  176. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
  177. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  178. data/lib/solargraph/parser/rubyvm.rb +0 -40
  179. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  180. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  181. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  182. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  183. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  184. data/lib/yard-solargraph.rb +0 -33
  185. data/yardoc/2.2.2.tar.gz +0 -0
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'open3'
4
- require 'rubygems'
5
4
  require 'json'
6
5
 
7
6
  module Solargraph
@@ -26,9 +25,11 @@ module Solargraph
26
25
 
27
26
  # @param directory [String]
28
27
  # @param config [Config, nil]
29
- def initialize directory = '', config = nil
28
+ # @param server [Hash]
29
+ def initialize directory = '', config = nil, server = {}
30
30
  @directory = directory
31
31
  @config = config
32
+ @server = server
32
33
  load_sources
33
34
  @gemnames = []
34
35
  @require_paths = generate_require_paths
@@ -46,24 +47,21 @@ module Solargraph
46
47
  #
47
48
  # @param source [Solargraph::Source]
48
49
  # @return [Boolean] True if the source was added to the workspace
49
- def merge source
50
- unless directory == '*' || source_hash.key?(source.filename)
50
+ def merge *sources
51
+ unless directory == '*' || sources.all? { |source| source_hash.key?(source.filename) }
51
52
  # Reload the config to determine if a new source should be included
52
53
  @config = Solargraph::Workspace::Config.new(directory)
53
- return false unless config.calculated.include?(source.filename)
54
54
  end
55
- source_hash[source.filename] = source
56
- true
57
- end
58
55
 
59
- # Determine whether a file would be merged into the workspace.
60
- #
61
- # @param filename [String]
62
- # @return [Boolean]
63
- def would_merge? filename
64
- return true if directory == '*' || source_hash.include?(filename)
65
- @config = Solargraph::Workspace::Config.new(directory)
66
- config.calculated.include?(filename)
56
+ includes_any = false
57
+ sources.each do |source|
58
+ if directory == "*" || config.calculated.include?(source.filename)
59
+ source_hash[source.filename] = source
60
+ includes_any = true
61
+ end
62
+ end
63
+
64
+ includes_any
67
65
  end
68
66
 
69
67
  # Remove a source from the workspace. The source will not be removed if
@@ -107,7 +105,8 @@ module Solargraph
107
105
  # @return [Boolean]
108
106
  def would_require? path
109
107
  require_paths.each do |rp|
110
- return true if File.exist?(File.join(rp, "#{path}.rb"))
108
+ full = File.join rp, path
109
+ return true if File.exist?(full) or File.exist?(full << ".rb")
111
110
  end
112
111
  false
113
112
  end
@@ -137,8 +136,19 @@ module Solargraph
137
136
  source_hash[updater.filename] = source_hash[updater.filename].synchronize(updater)
138
137
  end
139
138
 
139
+ # @return [String]
140
+ def command_path
141
+ server['commandPath'] || 'solargraph'
142
+ end
143
+
140
144
  private
141
145
 
146
+ # The language server configuration (or an empty hash if the workspace was
147
+ # not initialized from a server).
148
+ #
149
+ # @return [Hash]
150
+ attr_reader :server
151
+
142
152
  # @return [Hash{String => Solargraph::Source}]
143
153
  def source_hash
144
154
  @source_hash ||= {}
@@ -202,6 +212,7 @@ module Solargraph
202
212
  config.require_paths.map{|p| File.join(directory, p)}
203
213
  end
204
214
 
215
+ # @return [void]
205
216
  def require_plugins
206
217
  config.plugins.each do |plugin|
207
218
  begin
@@ -4,13 +4,19 @@ module Solargraph
4
4
  class YardMap
5
5
  class Cache
6
6
  def initialize
7
+ # @type [Hash{String => Array<Solargraph::Pin::Base>}]
7
8
  @path_pins = {}
8
9
  end
9
10
 
11
+ # @param path [String]
12
+ # @param pins [Array<Solargraph::Pin::Base>]
13
+ # @return [Array<Solargraph::Pin::Base>]
10
14
  def set_path_pins path, pins
11
15
  @path_pins[path] = pins
12
16
  end
13
17
 
18
+ # @param path [String]
19
+ # @return [Array<Solargraph::Pin::Base>]
14
20
  def get_path_pins path
15
21
  @path_pins[path]
16
22
  end
@@ -4,7 +4,7 @@ module Solargraph
4
4
  module_function
5
5
 
6
6
  # @param code_object [YARD::CodeObjects::Base]
7
- # @param spec [Gem::Specification]
7
+ # @param spec [Gem::Specification, nil]
8
8
  # @return [Solargraph::Location, nil]
9
9
  def object_location code_object, spec
10
10
  return nil if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil?
@@ -6,6 +6,13 @@ module Solargraph
6
6
  module ToMethod
7
7
  extend YardMap::Helpers
8
8
 
9
+ # @param code_object [YARD::CodeObjects::Base]
10
+ # @param name [String, nil]
11
+ # @param scope [Symbol, nil]
12
+ # @param visibility [Symbol, nil]
13
+ # @param closure [Solargraph::Pin::Namespace, nil]
14
+ # @param spec [Gem::Specification, nil]
15
+ # @return [Solargraph::Pin::Method]
9
16
  def self.make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil
10
17
  closure ||= Solargraph::Pin::Namespace.new(
11
18
  name: code_object.namespace.to_s,
@@ -13,24 +20,30 @@ module Solargraph
13
20
  )
14
21
  location = object_location(code_object, spec)
15
22
  comments = code_object.docstring ? code_object.docstring.all.to_s : ''
16
- Pin::Method.new(
23
+ pin = Pin::Method.new(
17
24
  location: location,
18
25
  closure: closure,
19
26
  name: name || code_object.name.to_s,
20
27
  comments: comments,
21
28
  scope: scope || code_object.scope,
22
29
  visibility: visibility || code_object.visibility,
23
- parameters: get_parameters(code_object, location, comments),
30
+ # @todo Might need to convert overloads to signatures
31
+ parameters: [],
24
32
  explicit: code_object.is_explicit?
25
33
  )
34
+ pin.parameters.concat get_parameters(code_object, location, comments, pin)
35
+ pin
26
36
  end
27
37
 
28
38
  class << self
29
39
  private
30
40
 
31
41
  # @param code_object [YARD::CodeObjects::Base]
42
+ # @param location [Location],
43
+ # @param comments [String]
44
+ # @param pin [Pin::Base]
32
45
  # @return [Array<Solargraph::Pin::Parameter>]
33
- def get_parameters code_object, location, comments
46
+ def get_parameters code_object, location, comments, pin
34
47
  return [] unless code_object.is_a?(YARD::CodeObjects::MethodObject)
35
48
  # HACK: Skip `nil` and `self` parameters that are sometimes emitted
36
49
  # for methods defined in C
@@ -38,7 +51,7 @@ module Solargraph
38
51
  code_object.parameters.select { |a| a[0] && a[0] != 'self' }.map do |a|
39
52
  Solargraph::Pin::Parameter.new(
40
53
  location: location,
41
- closure: self,
54
+ closure: pin,
42
55
  comments: comments,
43
56
  name: arg_name(a),
44
57
  presence: nil,
@@ -48,7 +61,7 @@ module Solargraph
48
61
  end
49
62
  end
50
63
 
51
- # @param a [Array]
64
+ # @param a [Array<String>]
52
65
  # @return [String]
53
66
  def arg_name a
54
67
  a[0].gsub(/[^a-z0-9_]/i, '')
@@ -8,7 +8,7 @@ module Solargraph
8
8
  autoload :ToConstant, 'solargraph/yard_map/mapper/to_constant'
9
9
 
10
10
  # @param code_objects [Array<YARD::CodeObjects::Base>]
11
- # @param spec [Gem::Specification]
11
+ # @param spec [Gem::Specification, nil]
12
12
  def initialize code_objects, spec = nil
13
13
  @code_objects = code_objects
14
14
  @spec = spec
@@ -7,6 +7,8 @@ module Solargraph
7
7
  module_function
8
8
 
9
9
  # @param code_object [YARD::CodeObjects::Base]
10
+ # @param location [Solargraph::Location]
11
+ # @param comments [String]
10
12
  # @return [Array<Solargraph::Pin::Parameter>]
11
13
  def get_parameters code_object, location, comments
12
14
  return [] unless code_object.is_a?(YARD::CodeObjects::MethodObject)
@@ -26,7 +28,7 @@ module Solargraph
26
28
  end
27
29
  end
28
30
 
29
- # @param a [Array]
31
+ # @param a [Array<String>]
30
32
  # @return [String]
31
33
  def arg_name a
32
34
  a[0].gsub(/[^a-z0-9_]/i, '')
@@ -52,10 +54,15 @@ module Solargraph
52
54
  end
53
55
  private_constant :InnerMethods
54
56
 
55
- # include YardMixin
56
- # extend YardMixin
57
- extend Helpers
57
+ include Helpers
58
58
 
59
+ # @param code_object [YARD::CodeObjects::Base]
60
+ # @param name [String, nil]
61
+ # @param scope [Symbol, nil]
62
+ # @param visibility [Symbol, nil]
63
+ # @param closure [Solargraph::Pin::Base, nil]
64
+ # @param spec [Solargraph::Pin::Base, nil]
65
+ # @return [Solargraph::Pin::Method]
59
66
  def make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil
60
67
  closure ||= Solargraph::Pin::Namespace.new(
61
68
  name: code_object.namespace.to_s,
@@ -1,9 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yard'
4
- require 'yard-solargraph'
5
- require 'rubygems/package'
6
- require 'set'
4
+ require 'solargraph/yard_tags'
7
5
 
8
6
  module Solargraph
9
7
  # The YardMap provides access to YARD documentation for the Ruby core, the
@@ -13,448 +11,8 @@ module Solargraph
13
11
  class NoYardocError < StandardError; end
14
12
 
15
13
  autoload :Cache, 'solargraph/yard_map/cache'
16
- autoload :CoreDocs, 'solargraph/yard_map/core_docs'
17
- autoload :CoreGen, 'solargraph/yard_map/core_gen'
18
14
  autoload :Mapper, 'solargraph/yard_map/mapper'
19
- autoload :RdocToYard, 'solargraph/yard_map/rdoc_to_yard'
20
- autoload :CoreFills, 'solargraph/yard_map/core_fills'
21
- autoload :StdlibFills, 'solargraph/yard_map/stdlib_fills'
22
15
  autoload :Helpers, 'solargraph/yard_map/helpers'
23
16
  autoload :ToMethod, 'solargraph/yard_map/to_method'
24
-
25
- include ApiMap::BundlerMethods
26
-
27
- CoreDocs.require_minimum
28
-
29
- def stdlib_paths
30
- @@stdlib_paths ||= begin
31
- result = {}
32
- YARD::Registry.load! CoreDocs.yardoc_stdlib_file
33
- YARD::Registry.all.each do |co|
34
- next if co.file.nil?
35
- path = co.file.sub(/^(ext|lib)\//, '').sub(/\.(rb|c)$/, '')
36
- base = path.split('/').first
37
- result[base] ||= []
38
- result[base].push co
39
- end
40
- result
41
- end
42
- end
43
-
44
- # @return [Boolean]
45
- attr_writer :with_dependencies
46
-
47
- # @param required [Array<String>, Set<String>]
48
- # @param directory [String]
49
- # @param source_gems [Array<String>, Set<String>]
50
- # @param with_dependencies [Boolean]
51
- def initialize(required: [], directory: '', source_gems: [], with_dependencies: true)
52
- @with_dependencies = with_dependencies
53
- change required.to_set, directory, source_gems.to_set
54
- end
55
-
56
- # @return [Array<Solargraph::Pin::Base>]
57
- def pins
58
- @pins ||= []
59
- end
60
-
61
- def with_dependencies?
62
- @with_dependencies ||= true unless @with_dependencies == false
63
- @with_dependencies
64
- end
65
-
66
- # @param new_requires [Set<String>] Required paths to use for loading gems
67
- # @param new_directory [String] The workspace directory
68
- # @param new_source_gems [Set<String>] Gems under local development (i.e., part of the workspace)
69
- # @return [Boolean]
70
- def change new_requires, new_directory, new_source_gems
71
- return false if new_requires == base_required && new_directory == @directory && new_source_gems == @source_gems
72
- @gem_paths = {}
73
- base_required.replace new_requires
74
- required.replace new_requires
75
- # HACK: Hardcoded YAML handling
76
- required.add 'psych' if new_requires.include?('yaml')
77
- @source_gems = new_source_gems
78
- @directory = new_directory
79
- process_requires
80
- @rebindable_method_names = nil
81
- @pin_class_hash = nil
82
- @pin_select_cache = {}
83
- true
84
- end
85
-
86
- # @return [Set<String>]
87
- def rebindable_method_names
88
- @rebindable_method_names ||= pins_by_class(Pin::Method)
89
- .select { |pin| pin.comments && pin.comments.include?('@yieldself') }
90
- .map(&:name)
91
- .concat(['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec', 'define_method'])
92
- .to_set
93
- end
94
-
95
- # @return [Array<String>]
96
- def yardocs
97
- @yardocs ||= []
98
- end
99
-
100
- # @return [Set<String>]
101
- def required
102
- @required ||= Set.new
103
- end
104
-
105
- # @return [Array<String>]
106
- def unresolved_requires
107
- @unresolved_requires ||= []
108
- end
109
-
110
- # @return [Array<String>]
111
- def missing_docs
112
- @missing_docs ||= []
113
- end
114
-
115
- # @param y [String]
116
- # @return [YARD::Registry]
117
- def load_yardoc y
118
- if y.is_a?(Array)
119
- YARD::Registry.load y, true
120
- else
121
- YARD::Registry.load! y
122
- end
123
- rescue StandardError => e
124
- Solargraph::Logging.logger.warn "Error loading yardoc '#{y}' #{e.class} #{e.message}"
125
- yardocs.delete y
126
- nil
127
- end
128
-
129
- # @return [Array<Solargraph::Pin::Base>]
130
- def core_pins
131
- # Using a class variable to reduce loads
132
- @@core_pins ||= load_core_pins
133
- end
134
-
135
- # @param path [String]
136
- # @return [Pin::Base]
137
- def path_pin path
138
- pins.select { |p| p.path == path }.first
139
- end
140
-
141
- # Get the location of a file referenced by a require path.
142
- #
143
- # @param path [String]
144
- # @return [Location]
145
- def require_reference path
146
- # @type [Gem::Specification]
147
- spec = spec_for_require(path)
148
- spec.full_require_paths.each do |rp|
149
- file = File.join(rp, "#{path}.rb")
150
- next unless File.file?(file)
151
- return Solargraph::Location.new(file, Solargraph::Range.from_to(0, 0, 0, 0))
152
- end
153
- nil
154
- rescue Gem::LoadError
155
- nil
156
- end
157
-
158
- def stdlib_pins
159
- @stdlib_pins ||= []
160
- end
161
-
162
- def base_required
163
- @base_required ||= Set.new
164
- end
165
-
166
- def directory
167
- @directory ||= ''
168
- end
169
-
170
- private
171
-
172
- # @return [YardMap::Cache]
173
- def cache
174
- @cache ||= YardMap::Cache.new
175
- end
176
-
177
- # @return [Hash]
178
- def pin_class_hash
179
- @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
180
- end
181
-
182
- # @return [Array<Pin::Base>]
183
- def pins_by_class klass
184
- @pin_select_cache[klass] ||= pin_class_hash.select { |key, _| key <= klass }.values.flatten
185
- end
186
-
187
- # @param ns [YARD::CodeObjects::NamespaceObject]
188
- # @return [Array<YARD::CodeObjects::Base>]
189
- def recurse_namespace_object ns
190
- result = []
191
- ns.children.each do |c|
192
- result.push c
193
- result.concat recurse_namespace_object(c) if c.respond_to?(:children)
194
- end
195
- result
196
- end
197
-
198
- # @return [void]
199
- def process_requires
200
- @gemset = process_gemsets
201
- required.merge @gemset.keys if required.include?('bundler/require')
202
- pins.replace core_pins
203
- unresolved_requires.clear
204
- missing_docs.clear
205
- stdlib_pins.clear
206
- environ = Convention.for_global(self)
207
- done = []
208
- already_errored = []
209
- (required + environ.requires).each do |r|
210
- next if r.nil? || r.empty? || done.include?(r)
211
- done.push r
212
- cached = cache.get_path_pins(r)
213
- unless cached.nil?
214
- pins.concat cached
215
- next
216
- end
217
- result = []
218
- begin
219
- spec = spec_for_require(r)
220
- if @source_gems.include?(spec.name)
221
- next
222
- end
223
- next if @gem_paths.key?(spec.name)
224
- yd = yardoc_file_for_spec(spec)
225
- # YARD detects gems for certain libraries that do not have a yardoc
226
- # but exist in the stdlib. `fileutils` is an example. Treat those
227
- # cases as errors and check the stdlib yardoc.
228
- if yd.nil?
229
- process_error(r, result, already_errored, nil)
230
- next
231
- end
232
- @gem_paths[spec.name] = spec.full_gem_path
233
- unless yardocs.include?(yd)
234
- yardocs.unshift yd
235
- result.concat process_yardoc yd, spec
236
- result.concat add_gem_dependencies(spec) if with_dependencies?
237
- end
238
- rescue Gem::LoadError, NoYardocError
239
- process_error(r, result, already_errored)
240
- end
241
- result.delete_if(&:nil?)
242
- unless result.empty?
243
- cache.set_path_pins r, result
244
- pins.concat result
245
- end
246
- end
247
- if required.include?('yaml') && required.include?('psych')
248
- # HACK: Hardcoded YAML handling
249
- # @todo Why can't this be handled with an override or a virtual pin?
250
- pin = path_pin('YAML')
251
- pin.instance_variable_set(:@return_type, ComplexType.parse('Module<Psych>')) unless pin.nil?
252
- end
253
- pins.concat environ.pins
254
- end
255
-
256
- def process_error(req, result, already_errored, yd = 1)
257
- base = req.split('/').first
258
- return if already_errored.include?(base)
259
- already_errored.push base
260
- stdtmp = load_stdlib_pins(base)
261
- if yd.nil?
262
- missing_docs.push req
263
- elsif stdtmp.empty?
264
- unresolved_requires.push req
265
- else
266
- stdlib_pins.concat stdtmp
267
- result.concat stdtmp
268
- end
269
- end
270
-
271
- def process_gemsets
272
- return {} if directory.empty? || !File.file?(File.join(directory, 'Gemfile'))
273
- require_from_bundle(directory)
274
- end
275
-
276
- # @param spec [Gem::Specification]
277
- # @return [void]
278
- def add_gem_dependencies spec
279
- result = []
280
- (spec.dependencies - spec.development_dependencies).each do |dep|
281
- begin
282
- next if @source_gems.include?(dep.name) || @gem_paths.key?(dep.name)
283
- depspec = Gem::Specification.find_by_name(dep.name)
284
- next if depspec.nil?
285
- @gem_paths[depspec.name] = depspec.full_gem_path
286
- gy = yardoc_file_for_spec(depspec)
287
- if gy.nil?
288
- missing_docs.push dep.name
289
- else
290
- next if yardocs.include?(gy)
291
- yardocs.unshift gy
292
- result.concat process_yardoc gy, depspec
293
- result.concat add_gem_dependencies(depspec)
294
- end
295
- rescue Gem::LoadError
296
- # This error probably indicates a bug in an installed gem
297
- Solargraph::Logging.logger.warn "Failed to resolve #{dep.name} gem dependency for #{spec.name}"
298
- end
299
- end
300
- result
301
- end
302
-
303
- # @param y [String, nil]
304
- # @param spec [Gem::Specification, nil]
305
- # @return [Array<Pin::Base>]
306
- def process_yardoc y, spec = nil
307
- return [] if y.nil?
308
- if spec
309
- ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
310
- if File.file?(ser)
311
- Solargraph.logger.info "Loading #{spec.name} #{spec.version} from cache"
312
- file = File.open(ser, 'rb')
313
- dump = file.read
314
- file.close
315
- begin
316
- result = Marshal.load(dump)
317
- return result unless result.nil? || result.empty?
318
- Solargraph.logger.warn "Empty cache for #{spec.name} #{spec.version}. Reloading"
319
- File.unlink ser
320
- rescue StandardError => e
321
- Solargraph.logger.warn "Error loading pin cache: [#{e.class}] #{e.message}"
322
- File.unlink ser
323
- end
324
- end
325
- end
326
- size = Dir.glob(File.join(y, '**', '*'))
327
- .map{ |f| File.size(f) }
328
- .inject(:+)
329
- if !size.nil? && size > 20_000_000
330
- Solargraph::Logging.logger.warn "Yardoc at #{y} is too large to process (#{size} bytes)"
331
- return []
332
- end
333
- Solargraph.logger.info "Loading #{spec.name} #{spec.version} from #{y}"
334
- load_yardoc y
335
- result = Mapper.new(YARD::Registry.all, spec).map
336
- raise NoYardocError, "Yardoc at #{y} is empty" if result.empty?
337
- if spec
338
- ser = File.join(CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}.ser")
339
- file = File.open(ser, 'wb')
340
- file.write Marshal.dump(result)
341
- file.close
342
- end
343
- result
344
- end
345
-
346
- # @param spec [Gem::Specification]
347
- # @return [String]
348
- def yardoc_file_for_spec spec
349
- cache_dir = File.join(Solargraph::YardMap::CoreDocs.cache_dir, 'gems', "#{spec.name}-#{spec.version}", 'yardoc')
350
- if File.exist?(cache_dir)
351
- Solargraph.logger.info "Using cached documentation for #{spec.name} at #{cache_dir}"
352
- cache_dir
353
- else
354
- YARD::Registry.yardoc_file_for_gem(spec.name, "= #{spec.version}")
355
- end
356
- end
357
-
358
- # @param path [String]
359
- # @return [Gem::Specification]
360
- def spec_for_require path
361
- name = path.split('/').first
362
- spec = Gem::Specification.find_by_name(name, @gemset[name])
363
-
364
- # Avoid loading the spec again if it's going to be skipped anyway
365
- return spec if @source_gems.include?(spec.name)
366
- # Avoid loading the spec again if it's already the correct version
367
- if @gemset[spec.name] && @gemset[spec.name] != spec.version
368
- begin
369
- return Gem::Specification.find_by_name(spec.name, "= #{@gemset[spec.name]}")
370
- rescue Gem::LoadError
371
- Solargraph.logger.warn "Unable to load #{spec.name} #{@gemset[spec.name]} specified by workspace, using #{spec.version} instead"
372
- end
373
- end
374
- spec
375
- end
376
-
377
- def load_core_pins
378
- yd = CoreDocs.yardoc_file
379
- ser = File.join(File.dirname(yd), 'core.ser')
380
- result = if File.file?(ser)
381
- file = File.open(ser, 'rb')
382
- dump = file.read
383
- file.close
384
- begin
385
- Marshal.load(dump)
386
- rescue StandardError => e
387
- Solargraph.logger.warn "Error loading core pin cache: [#{e.class}] #{e.message}"
388
- File.unlink ser
389
- read_core_and_save_cache(yd, ser)
390
- end
391
- else
392
- read_core_and_save_cache(yd, ser)
393
- end
394
- ApiMap::Store.new(result + CoreFills::ALL).pins.reject { |pin| pin.is_a?(Pin::Reference::Override) }
395
- end
396
-
397
- def read_core_and_save_cache yd, ser
398
- result = []
399
- load_yardoc yd
400
- result.concat Mapper.new(YARD::Registry.all).map
401
- # HACK: Assume core methods with a single `args` parameter accept restarg
402
- result.select { |pin| pin.is_a?(Solargraph::Pin::Method )}.each do |pin|
403
- if pin.parameters.length == 1 && pin.parameters.first.name == 'args' && pin.parameters.first.decl == :arg
404
- # @todo Smelly instance variable access
405
- pin.parameters.first.instance_variable_set(:@decl, :restarg)
406
- end
407
- end
408
- # HACK: Set missing parameters on `==` methods, e.g., `Symbol#==`
409
- result.select { |pin| pin.name == '==' && pin.parameters.empty? }.each do |pin|
410
- pin.parameters.push Pin::Parameter.new(decl: :arg, name: 'obj2')
411
- end
412
- dump = Marshal.dump(result)
413
- file = File.open(ser, 'wb')
414
- file.write dump
415
- file.close
416
- result
417
- end
418
-
419
- def load_stdlib_pins base
420
- ser = File.join(File.dirname(CoreDocs.yardoc_stdlib_file), "#{base}.ser")
421
- result = if File.file?(ser)
422
- Solargraph.logger.info "Loading #{base} stdlib from cache"
423
- file = File.open(ser, 'rb')
424
- dump = file.read
425
- file.close
426
- begin
427
- Marshal.load(dump)
428
- rescue StandardError => e
429
- Solargraph.logger.warn "Error loading #{base} stdlib pin cache: [#{e.class}] #{e.message}"
430
- File.unlink ser
431
- read_stdlib_and_save_cache(base, ser)
432
- end
433
- else
434
- read_stdlib_and_save_cache(base, ser)
435
- end
436
- fills = StdlibFills.get(base)
437
- unless fills.empty?
438
- result = ApiMap::Store.new(result + fills).pins.reject { |pin| pin.is_a?(Pin::Reference::Override) }
439
- end
440
- result
441
- end
442
-
443
- def read_stdlib_and_save_cache base, ser
444
- result = []
445
- if stdlib_paths[base]
446
- Solargraph.logger.info "Loading #{base} stdlib from yardoc"
447
- result.concat Mapper.new(stdlib_paths[base]).map
448
- unless result.empty?
449
- dump = Marshal.dump(result)
450
- file = File.open(ser, 'wb')
451
- file.write dump
452
- file.close
453
- end
454
- end
455
- result
456
- end
457
17
  end
458
18
  end
459
-
460
- Solargraph::YardMap::CoreDocs.require_minimum