solargraph 0.58.2 → 0.59.0.dev.2

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +4 -5
  4. data/.github/workflows/plugins.yml +41 -34
  5. data/.github/workflows/rspec.yml +44 -23
  6. data/.github/workflows/typecheck.yml +2 -2
  7. data/.rubocop.yml +32 -5
  8. data/.rubocop_todo.yml +50 -966
  9. data/Gemfile +3 -1
  10. data/README.md +3 -3
  11. data/Rakefile +26 -23
  12. data/bin/solargraph +2 -1
  13. data/lib/solargraph/api_map/cache.rb +3 -3
  14. data/lib/solargraph/api_map/constants.rb +13 -3
  15. data/lib/solargraph/api_map/index.rb +23 -18
  16. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  17. data/lib/solargraph/api_map/store.rb +33 -28
  18. data/lib/solargraph/api_map.rb +150 -82
  19. data/lib/solargraph/bench.rb +44 -45
  20. data/lib/solargraph/complex_type/conformance.rb +176 -0
  21. data/lib/solargraph/complex_type/type_methods.rb +28 -17
  22. data/lib/solargraph/complex_type/unique_type.rb +218 -57
  23. data/lib/solargraph/complex_type.rb +170 -57
  24. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  25. data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
  26. data/lib/solargraph/convention/data_definition.rb +5 -2
  27. data/lib/solargraph/convention/gemfile.rb +15 -15
  28. data/lib/solargraph/convention/gemspec.rb +23 -23
  29. data/lib/solargraph/convention/rakefile.rb +17 -17
  30. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
  31. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
  32. data/lib/solargraph/convention/struct_definition.rb +8 -4
  33. data/lib/solargraph/convention.rb +78 -78
  34. data/lib/solargraph/converters/dd.rb +19 -17
  35. data/lib/solargraph/converters/dl.rb +17 -15
  36. data/lib/solargraph/converters/dt.rb +17 -15
  37. data/lib/solargraph/converters/misc.rb +3 -1
  38. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  39. data/lib/solargraph/diagnostics/rubocop.rb +11 -10
  40. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  41. data/lib/solargraph/diagnostics/type_check.rb +11 -10
  42. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  43. data/lib/solargraph/doc_map.rb +133 -373
  44. data/lib/solargraph/equality.rb +4 -4
  45. data/lib/solargraph/gem_pins.rb +21 -20
  46. data/lib/solargraph/language_server/error_codes.rb +20 -20
  47. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  48. data/lib/solargraph/language_server/host/dispatch.rb +3 -3
  49. data/lib/solargraph/language_server/host/message_worker.rb +4 -3
  50. data/lib/solargraph/language_server/host/sources.rb +2 -1
  51. data/lib/solargraph/language_server/host.rb +30 -22
  52. data/lib/solargraph/language_server/message/base.rb +97 -97
  53. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  54. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  55. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
  56. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  57. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  58. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  59. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  60. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  61. data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
  62. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
  63. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  64. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
  65. data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
  66. data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
  67. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  68. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  69. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  70. data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
  71. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
  72. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  73. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  74. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  75. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
  76. data/lib/solargraph/language_server/message.rb +94 -94
  77. data/lib/solargraph/language_server/request.rb +29 -27
  78. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  79. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  80. data/lib/solargraph/library.rb +85 -44
  81. data/lib/solargraph/location.rb +17 -14
  82. data/lib/solargraph/logging.rb +24 -4
  83. data/lib/solargraph/page.rb +92 -92
  84. data/lib/solargraph/parser/comment_ripper.rb +19 -4
  85. data/lib/solargraph/parser/flow_sensitive_typing.rb +326 -108
  86. data/lib/solargraph/parser/node_processor/base.rb +34 -4
  87. data/lib/solargraph/parser/node_processor.rb +8 -7
  88. data/lib/solargraph/parser/parser_gem/class_methods.rb +32 -14
  89. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  90. data/lib/solargraph/parser/parser_gem/node_chainer.rb +50 -25
  91. data/lib/solargraph/parser/parser_gem/node_methods.rb +91 -70
  92. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  93. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
  94. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  95. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +12 -12
  96. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
  97. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  98. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  99. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
  100. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  101. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  102. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
  103. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  104. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  105. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  106. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
  107. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
  108. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  109. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  110. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
  111. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  112. data/lib/solargraph/parser/parser_gem.rb +14 -12
  113. data/lib/solargraph/parser/region.rb +9 -3
  114. data/lib/solargraph/parser/snippet.rb +3 -1
  115. data/lib/solargraph/parser.rb +25 -23
  116. data/lib/solargraph/pin/base.rb +126 -80
  117. data/lib/solargraph/pin/base_variable.rb +273 -24
  118. data/lib/solargraph/pin/block.rb +29 -6
  119. data/lib/solargraph/pin/breakable.rb +7 -1
  120. data/lib/solargraph/pin/callable.rb +65 -21
  121. data/lib/solargraph/pin/closure.rb +7 -10
  122. data/lib/solargraph/pin/common.rb +24 -6
  123. data/lib/solargraph/pin/compound_statement.rb +55 -0
  124. data/lib/solargraph/pin/constant.rb +43 -45
  125. data/lib/solargraph/pin/conversions.rb +10 -4
  126. data/lib/solargraph/pin/delegated_method.rb +19 -8
  127. data/lib/solargraph/pin/documenting.rb +4 -2
  128. data/lib/solargraph/pin/instance_variable.rb +5 -1
  129. data/lib/solargraph/pin/keyword.rb +0 -4
  130. data/lib/solargraph/pin/local_variable.rb +15 -59
  131. data/lib/solargraph/pin/method.rb +153 -104
  132. data/lib/solargraph/pin/method_alias.rb +8 -0
  133. data/lib/solargraph/pin/namespace.rb +19 -12
  134. data/lib/solargraph/pin/parameter.rb +100 -36
  135. data/lib/solargraph/pin/proxy_type.rb +4 -1
  136. data/lib/solargraph/pin/reference/override.rb +1 -1
  137. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  138. data/lib/solargraph/pin/reference.rb +19 -0
  139. data/lib/solargraph/pin/search.rb +3 -2
  140. data/lib/solargraph/pin/signature.rb +15 -12
  141. data/lib/solargraph/pin/symbol.rb +2 -1
  142. data/lib/solargraph/pin/until.rb +2 -4
  143. data/lib/solargraph/pin/while.rb +2 -4
  144. data/lib/solargraph/pin.rb +2 -0
  145. data/lib/solargraph/pin_cache.rb +490 -73
  146. data/lib/solargraph/position.rb +14 -10
  147. data/lib/solargraph/range.rb +16 -15
  148. data/lib/solargraph/rbs_map/conversions.rb +343 -214
  149. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  150. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  151. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
  152. data/lib/solargraph/rbs_map.rb +77 -32
  153. data/lib/solargraph/server_methods.rb +16 -16
  154. data/lib/solargraph/shell.rb +128 -73
  155. data/lib/solargraph/source/chain/array.rb +39 -37
  156. data/lib/solargraph/source/chain/call.rb +96 -56
  157. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  158. data/lib/solargraph/source/chain/constant.rb +5 -1
  159. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  160. data/lib/solargraph/source/chain/hash.rb +8 -5
  161. data/lib/solargraph/source/chain/if.rb +12 -10
  162. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  163. data/lib/solargraph/source/chain/link.rb +99 -109
  164. data/lib/solargraph/source/chain/literal.rb +9 -6
  165. data/lib/solargraph/source/chain/or.rb +10 -4
  166. data/lib/solargraph/source/chain/q_call.rb +13 -11
  167. data/lib/solargraph/source/chain/variable.rb +15 -13
  168. data/lib/solargraph/source/chain/z_super.rb +28 -30
  169. data/lib/solargraph/source/chain.rb +49 -38
  170. data/lib/solargraph/source/change.rb +12 -5
  171. data/lib/solargraph/source/cursor.rb +23 -17
  172. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  173. data/lib/solargraph/source/source_chainer.rb +56 -32
  174. data/lib/solargraph/source/updater.rb +5 -1
  175. data/lib/solargraph/source.rb +59 -35
  176. data/lib/solargraph/source_map/clip.rb +48 -29
  177. data/lib/solargraph/source_map/data.rb +4 -1
  178. data/lib/solargraph/source_map/mapper.rb +71 -42
  179. data/lib/solargraph/source_map.rb +21 -9
  180. data/lib/solargraph/type_checker/problem.rb +3 -1
  181. data/lib/solargraph/type_checker/rules.rb +81 -8
  182. data/lib/solargraph/type_checker.rb +195 -120
  183. data/lib/solargraph/version.rb +1 -1
  184. data/lib/solargraph/workspace/config.rb +13 -10
  185. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  186. data/lib/solargraph/workspace/require_paths.rb +1 -0
  187. data/lib/solargraph/workspace.rb +149 -30
  188. data/lib/solargraph/yard_map/helpers.rb +8 -3
  189. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  190. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  191. data/lib/solargraph/yard_map/mapper.rb +13 -8
  192. data/lib/solargraph/yard_tags.rb +20 -20
  193. data/lib/solargraph/yardoc.rb +33 -23
  194. data/lib/solargraph.rb +29 -8
  195. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  196. data/rbs/fills/tuple/tuple.rbs +28 -0
  197. data/rbs/shims/ast/0/node.rbs +1 -1
  198. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  199. data/solargraph.gemspec +36 -34
  200. metadata +38 -33
  201. data/lib/solargraph/type_checker/checks.rb +0 -124
  202. data/lib/solargraph/type_checker/param_def.rb +0 -37
  203. data/lib/solargraph/yard_map/to_method.rb +0 -89
@@ -1,41 +1,37 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- module Diagnostics
5
- class UpdateErrors < Base
6
- def diagnose source, api_map
7
- result = []
8
- combine_ranges(source.code, source.error_ranges).each do |range|
9
- result.push(
10
- range: range.to_hash,
11
- severity: Diagnostics::Severities::ERROR,
12
- source: 'Solargraph',
13
- message: 'Syntax error'
14
- )
15
- end
16
- result
17
- end
18
-
19
- private
20
-
21
- # Combine an array of ranges by their starting lines.
22
- #
23
- # @param code [String]
24
- # @param ranges [Array<Range>]
25
- # @return [Array<Range>]
26
- def combine_ranges code, ranges
27
- result = []
28
- lines = []
29
- ranges.sort{|a, b| a.start.line <=> b.start.line}.each do |rng|
30
- next if rng.nil? || lines.include?(rng.start.line)
31
- lines.push rng.start.line
32
- next if rng.start.line >= code.lines.length
33
- scol = code.lines[rng.start.line].index(/[^\s]/) || 0
34
- ecol = code.lines[rng.start.line].length
35
- result.push Range.from_to(rng.start.line, scol, rng.start.line, ecol)
36
- end
37
- result
38
- end
39
- end
40
- end
41
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Diagnostics
5
+ class UpdateErrors < Base
6
+ def diagnose source, api_map
7
+ combine_ranges(source.code, source.error_ranges).map do |range|
8
+ { range: range.to_hash,
9
+ severity: Diagnostics::Severities::ERROR,
10
+ source: 'Solargraph',
11
+ message: 'Syntax error' }
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ # Combine an array of ranges by their starting lines.
18
+ #
19
+ # @param code [String]
20
+ # @param ranges [Array<Range>]
21
+ # @return [Array<Range>]
22
+ def combine_ranges code, ranges
23
+ result = []
24
+ lines = []
25
+ ranges.sort { |a, b| a.start.line <=> b.start.line }.each do |rng|
26
+ next if rng.nil? || lines.include?(rng.start.line)
27
+ lines.push rng.start.line
28
+ next if rng.start.line >= code.lines.length
29
+ scol = code.lines[rng.start.line].index(/[^\s]/) || 0
30
+ ecol = code.lines[rng.start.line].length
31
+ result.push Range.from_to(rng.start.line, scol, rng.start.line, ecol)
32
+ end
33
+ result
34
+ end
35
+ end
36
+ end
37
+ end
@@ -5,122 +5,86 @@ require 'benchmark'
5
5
  require 'open3'
6
6
 
7
7
  module Solargraph
8
- # A collection of pins generated from required gems.
8
+ # A collection of pins generated from specific 'require' statements
9
+ # in code. Multiple can be created per workspace, to represent the
10
+ # pins available in different files based on their particular
11
+ # 'require' lines.
9
12
  #
10
13
  class DocMap
11
14
  include Logging
12
15
 
13
- # @return [Array<String>]
14
- attr_reader :requires
15
- alias required requires
16
-
17
- # @return [Array<Gem::Specification>]
18
- attr_reader :preferences
19
-
20
- # @return [Array<Pin::Base>]
21
- attr_reader :pins
22
-
23
- # @return [Array<Gem::Specification>]
24
- def uncached_gemspecs
25
- uncached_yard_gemspecs.concat(uncached_rbs_collection_gemspecs)
26
- .sort
27
- .uniq { |gemspec| "#{gemspec.name}:#{gemspec.version}" }
28
- end
29
-
30
- # @return [Array<Gem::Specification>]
31
- attr_reader :uncached_yard_gemspecs
32
-
33
- # @return [Array<Gem::Specification>]
34
- attr_reader :uncached_rbs_collection_gemspecs
35
-
36
- # @return [String, nil]
37
- attr_reader :rbs_collection_path
38
-
39
- # @return [String, nil]
40
- attr_reader :rbs_collection_config_path
41
-
42
- # @return [Workspace, nil]
16
+ # @return [Workspace]
43
17
  attr_reader :workspace
44
18
 
45
19
  # @return [Environ]
46
20
  attr_reader :environ
47
21
 
48
22
  # @param requires [Array<String>]
49
- # @param preferences [Array<Gem::Specification>]
50
23
  # @param workspace [Workspace, nil]
51
- def initialize(requires, preferences, workspace = nil)
52
- @requires = requires.compact
53
- @preferences = preferences.compact
24
+ # @param out [IO, nil] output stream for logging
25
+ def initialize requires, workspace, out: $stderr
26
+ @provided_requires = requires.compact
54
27
  @workspace = workspace
55
- @rbs_collection_path = workspace&.rbs_collection_path
56
- @rbs_collection_config_path = workspace&.rbs_collection_config_path
57
- @environ = Convention.for_global(self)
58
- @requires.concat @environ.requires if @environ
59
- load_serialized_gem_pins
60
- pins.concat @environ.pins
28
+ @out = out
61
29
  end
62
30
 
63
- # @param out [IO]
64
- # @return [void]
65
- def cache_all!(out)
66
- # if we log at debug level:
67
- if logger.info?
68
- gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ')
69
- logger.info "Caching pins for gems: #{gem_desc}" unless uncached_gemspecs.empty?
70
- end
71
- logger.debug { "Caching for YARD: #{uncached_yard_gemspecs.map(&:name)}" }
72
- logger.debug { "Caching for RBS collection: #{uncached_rbs_collection_gemspecs.map(&:name)}" }
73
- load_serialized_gem_pins
74
- uncached_gemspecs.each do |gemspec|
75
- cache(gemspec, out: out)
31
+ # @return [Array<String>]
32
+ def requires
33
+ @requires ||= @provided_requires + (workspace.global_environ&.requires || [])
34
+ end
35
+ alias required requires
36
+
37
+ # @sg-ignore flow sensitive typing needs to understand reassignment
38
+ # @return [Array<Gem::Specification>]
39
+ def uncached_gemspecs
40
+ if @uncached_gemspecs.nil?
41
+ @uncached_gemspecs = []
42
+ pins # force lazy-loaded pin lookup
76
43
  end
77
- load_serialized_gem_pins
78
- @uncached_rbs_collection_gemspecs = []
79
- @uncached_yard_gemspecs = []
44
+ @uncached_gemspecs
80
45
  end
81
46
 
82
- # @param gemspec [Gem::Specification]
83
- # @param out [IO]
84
- # @return [void]
85
- def cache_yard_pins(gemspec, out)
86
- pins = GemPins.build_yard_pins(yard_plugins, gemspec)
87
- PinCache.serialize_yard_gem(gemspec, pins)
88
- logger.info { "Cached #{pins.length} YARD pins for gem #{gemspec.name}:#{gemspec.version}" } unless pins.empty?
47
+ # @return [Array<Pin::Base>]
48
+ def pins
49
+ @pins ||= load_serialized_gem_pins + (workspace.global_environ&.pins || [])
89
50
  end
90
51
 
91
- # @param gemspec [Gem::Specification]
92
- # @param out [IO]
93
52
  # @return [void]
94
- def cache_rbs_collection_pins(gemspec, out)
95
- rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path)
96
- pins = rbs_map.pins
97
- rbs_version_cache_key = rbs_map.cache_key
98
- # cache pins even if result is zero, so we don't retry building pins
99
- pins ||= []
100
- PinCache.serialize_rbs_collection_gem(gemspec, rbs_version_cache_key, pins)
101
- logger.info { "Cached #{pins.length} RBS collection pins for gem #{gemspec.name} #{gemspec.version} with cache_key #{rbs_version_cache_key.inspect}" unless pins.empty? }
53
+ def reset_pins!
54
+ @uncached_gemspecs = nil
55
+ @pins = nil
102
56
  end
103
57
 
104
- # @param gemspec [Gem::Specification]
58
+ # @return [Solargraph::PinCache]
59
+ def pin_cache
60
+ @pin_cache ||= workspace.fresh_pincache
61
+ end
62
+
63
+ def any_uncached?
64
+ uncached_gemspecs.any?
65
+ end
66
+
67
+ # Cache all pins needed for the sources in this doc_map
68
+ # @param out [StringIO, IO, nil] output stream for logging
105
69
  # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
106
- # @param out [IO, nil] output stream for logging
107
70
  # @return [void]
108
- def cache(gemspec, rebuild: false, out: nil)
109
- build_yard = uncached_yard_gemspecs.include?(gemspec) || rebuild
110
- build_rbs_collection = uncached_rbs_collection_gemspecs.include?(gemspec) || rebuild
111
- if build_yard || build_rbs_collection
112
- type = []
113
- type << 'YARD' if build_yard
114
- type << 'RBS collection' if build_rbs_collection
115
- out.puts("Caching #{type.join(' and ')} pins for gem #{gemspec.name}:#{gemspec.version}") if out
71
+ def cache_doc_map_gems! out, rebuild: false
72
+ unless uncached_gemspecs.empty?
73
+ logger.info do
74
+ gem_desc = uncached_gemspecs.map { |gemspec| "#{gemspec.name}:#{gemspec.version}" }.join(', ')
75
+ "Caching pins for gems: #{gem_desc}"
76
+ end
116
77
  end
117
- cache_yard_pins(gemspec, out) if build_yard
118
- cache_rbs_collection_pins(gemspec, out) if build_rbs_collection
119
- end
120
-
121
- # @return [Array<Gem::Specification>]
122
- def gemspecs
123
- @gemspecs ||= required_gems_map.values.compact.flatten
78
+ time = Benchmark.measure do
79
+ uncached_gemspecs.each do |gemspec|
80
+ cache(gemspec, rebuild: rebuild, out: out)
81
+ end
82
+ end
83
+ milliseconds = (time.real * 1000).round
84
+ if (milliseconds > 500) && uncached_gemspecs.any? && out && uncached_gemspecs.any?
85
+ out.puts "Built #{uncached_gemspecs.length} gems in #{milliseconds} ms"
86
+ end
87
+ reset_pins!
124
88
  end
125
89
 
126
90
  # @return [Array<String>]
@@ -128,312 +92,108 @@ module Solargraph
128
92
  @unresolved_requires ||= required_gems_map.select { |_, gemspecs| gemspecs.nil? }.keys
129
93
  end
130
94
 
131
- # @return [Hash{Array(String, String) => Array<Pin::Base>}] Indexed by gemspec name and version
132
- def self.all_yard_gems_in_memory
133
- @yard_gems_in_memory ||= {}
134
- end
135
-
136
- # @return [Hash{String => Hash{Array(String, String) => Array<Pin::Base>}}] stored by RBS collection path
137
- def self.all_rbs_collection_gems_in_memory
138
- @rbs_collection_gems_in_memory ||= {}
139
- end
140
-
141
- # @return [Hash{Array(String, String) => Array<Pin::Base>}] Indexed by gemspec name and version
142
- def yard_pins_in_memory
143
- self.class.all_yard_gems_in_memory
144
- end
145
-
146
- # @return [Hash{Array(String, String) => Array<Pin::Base>}] Indexed by gemspec name and version
147
- def rbs_collection_pins_in_memory
148
- self.class.all_rbs_collection_gems_in_memory[rbs_collection_path] ||= {}
149
- end
150
-
151
- # @return [Hash{Array(String, String) => Array<Pin::Base>}] Indexed by gemspec name and version
152
- def self.all_combined_pins_in_memory
153
- @combined_pins_in_memory ||= {}
95
+ # @return [Array<Gem::Specification>]
96
+ # @param out [IO, nil]
97
+ def dependencies out: $stderr
98
+ @dependencies ||=
99
+ begin
100
+ gem_deps = gemspecs
101
+ .flat_map { |spec| workspace.fetch_dependencies(spec, out: out) }
102
+ .uniq(&:name)
103
+ stdlib_deps = gemspecs
104
+ .flat_map { |spec| workspace.stdlib_dependencies(spec.name) }
105
+ .flat_map { |dep_name| workspace.resolve_require(dep_name) }
106
+ .compact
107
+ existing_gems = gemspecs.map(&:name)
108
+ (gem_deps + stdlib_deps).reject { |gemspec| existing_gems.include? gemspec.name }
109
+ end
154
110
  end
155
111
 
156
- # @todo this should also include an index by the hash of the RBS collection
157
- # @return [Hash{Array(String, String) => Array<Pin::Base>}] Indexed by gemspec name and version
158
- def combined_pins_in_memory
159
- self.class.all_combined_pins_in_memory
112
+ # Cache gem documentation if needed for this doc_map
113
+ #
114
+ # @param gemspec [Gem::Specification]
115
+ # @param rebuild [Boolean] whether to rebuild the pins even if they are cached
116
+ # @param out [StringIO, IO, nil] output stream for logging
117
+ #
118
+ # @return [void]
119
+ def cache gemspec, rebuild: false, out: nil
120
+ pin_cache.cache_gem(gemspec: gemspec,
121
+ rebuild: rebuild,
122
+ out: out)
160
123
  end
161
124
 
162
- # @return [Array<String>]
163
- def yard_plugins
164
- @environ.yard_plugins
165
- end
125
+ private
166
126
 
167
- # @return [Set<Gem::Specification>]
168
- def dependencies
169
- @dependencies ||= (gemspecs.flat_map { |spec| fetch_dependencies(spec) } - gemspecs).to_set
127
+ # @return [Array<Gem::Specification>]
128
+ def gemspecs
129
+ @gemspecs ||= required_gems_map.values.compact.flatten
170
130
  end
171
131
 
172
- private
173
-
174
- # @return [void]
175
- def load_serialized_gem_pins
176
- @pins = []
177
- @uncached_yard_gemspecs = []
178
- @uncached_rbs_collection_gemspecs = []
132
+ # @param out [IO, nil]
133
+ # @return [Array<Pin::Base>]
134
+ def load_serialized_gem_pins out: @out
135
+ serialized_pins = []
179
136
  with_gemspecs, without_gemspecs = required_gems_map.partition { |_, v| v }
180
- # @sg-ignore Need support for RBS duck interfaces like _ToHash
181
137
  # @type [Array<String>]
182
- paths = Hash[without_gemspecs].keys
183
- # @sg-ignore Need support for RBS duck interfaces like _ToHash
138
+ missing_paths = without_gemspecs.to_h.keys
184
139
  # @type [Array<Gem::Specification>]
185
- gemspecs = Hash[with_gemspecs].values.flatten.compact + dependencies.to_a
186
-
187
- paths.each do |path|
188
- rbs_pins = deserialize_stdlib_rbs_map path
140
+ gemspecs = with_gemspecs.to_h.values.flatten.compact + dependencies(out: out).to_a
141
+
142
+ # if we are type checking a gem project, we should not include
143
+ # pins from rbs or yard from that gem here - we use our own
144
+ # parser for those pins
145
+
146
+ # @param gemspec [Gem::Specification, Bundler::LazySpecification, Bundler::StubSpecification]
147
+ gemspecs.reject! do |gemspec|
148
+ gemspec.respond_to?(:source) &&
149
+ gemspec.source.instance_of?(Bundler::Source::Gemspec) &&
150
+ gemspec.source.respond_to?(:path) &&
151
+ gemspec.source.path == Pathname.new('.')
152
+ end
153
+
154
+ missing_paths.each do |path|
155
+ # this will load from disk if needed; no need to manage
156
+ # uncached_gemspecs to trigger that later
157
+ stdlib_name_guess = path.split('/').first
158
+
159
+ # try to resolve the stdlib name
160
+ # @type [Array<String>]
161
+ deps = workspace.stdlib_dependencies(stdlib_name_guess) || []
162
+ [stdlib_name_guess, *deps].compact.each do |potential_stdlib_name|
163
+ # @sg-ignore Need to support splatting in literal array
164
+ rbs_pins = pin_cache.cache_stdlib_rbs_map potential_stdlib_name
165
+ serialized_pins.concat rbs_pins if rbs_pins
166
+ end
189
167
  end
190
168
 
191
- logger.debug { "DocMap#load_serialized_gem_pins: Combining pins..." }
169
+ serialized_pins.length
192
170
  time = Benchmark.measure do
193
171
  gemspecs.each do |gemspec|
194
- pins = deserialize_combined_pin_cache gemspec
195
- @pins.concat pins if pins
172
+ # only deserializes already-cached gems
173
+ gemspec_pins = pin_cache.deserialize_combined_pin_cache gemspec
174
+ if gemspec_pins
175
+ serialized_pins.concat gemspec_pins
176
+ else
177
+ uncached_gemspecs << gemspec
178
+ end
196
179
  end
197
180
  end
198
- logger.info { "DocMap#load_serialized_gem_pins: Loaded and processed serialized pins together in #{time.real} seconds" }
199
- @uncached_yard_gemspecs.uniq!
200
- @uncached_rbs_collection_gemspecs.uniq!
201
- nil
181
+ serialized_pins.length
182
+ milliseconds = (time.real * 1000).round
183
+ if (milliseconds > 500) && out && gemspecs.any?
184
+ out.puts "Deserialized #{serialized_pins.length} gem pins from #{PinCache.base_dir} in #{milliseconds} ms"
185
+ end
186
+ uncached_gemspecs.uniq! { |gemspec| "#{gemspec.name}:#{gemspec.version}" }
187
+ serialized_pins
202
188
  end
203
189
 
204
190
  # @return [Hash{String => Array<Gem::Specification>}]
205
191
  def required_gems_map
206
- @required_gems_map ||= requires.to_h { |path| [path, resolve_path_to_gemspecs(path)] }
207
- end
208
-
209
- # @return [Hash{String => Gem::Specification}]
210
- def preference_map
211
- @preference_map ||= preferences.to_h { |gemspec| [gemspec.name, gemspec] }
212
- end
213
-
214
- # @param gemspec [Gem::Specification]
215
- # @return [Array<Pin::Base>, nil]
216
- def deserialize_yard_pin_cache gemspec
217
- if yard_pins_in_memory.key?([gemspec.name, gemspec.version])
218
- return yard_pins_in_memory[[gemspec.name, gemspec.version]]
219
- end
220
-
221
- cached = PinCache.deserialize_yard_gem(gemspec)
222
- if cached
223
- logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" }
224
- yard_pins_in_memory[[gemspec.name, gemspec.version]] = cached
225
- cached
226
- else
227
- logger.debug "No YARD pin cache for #{gemspec.name}:#{gemspec.version}"
228
- @uncached_yard_gemspecs.push gemspec
229
- nil
230
- end
231
- end
232
-
233
- # @param gemspec [Gem::Specification]
234
- # @return [void]
235
- def deserialize_combined_pin_cache(gemspec)
236
- unless combined_pins_in_memory[[gemspec.name, gemspec.version]].nil?
237
- return combined_pins_in_memory[[gemspec.name, gemspec.version]]
238
- end
239
-
240
- rbs_map = RbsMap.from_gemspec(gemspec, rbs_collection_path, rbs_collection_config_path)
241
- rbs_version_cache_key = rbs_map.cache_key
242
-
243
- cached = PinCache.deserialize_combined_gem(gemspec, rbs_version_cache_key)
244
- if cached
245
- logger.info { "Loaded #{cached.length} cached YARD pins from #{gemspec.name}:#{gemspec.version}" }
246
- combined_pins_in_memory[[gemspec.name, gemspec.version]] = cached
247
- return combined_pins_in_memory[[gemspec.name, gemspec.version]]
248
- end
249
-
250
- rbs_collection_pins = deserialize_rbs_collection_cache gemspec, rbs_version_cache_key
251
-
252
- yard_pins = deserialize_yard_pin_cache gemspec
253
-
254
- if !rbs_collection_pins.nil? && !yard_pins.nil?
255
- logger.debug { "Combining pins for #{gemspec.name}:#{gemspec.version}" }
256
- combined_pins = GemPins.combine(yard_pins, rbs_collection_pins)
257
- PinCache.serialize_combined_gem(gemspec, rbs_version_cache_key, combined_pins)
258
- combined_pins_in_memory[[gemspec.name, gemspec.version]] = combined_pins
259
- logger.info { "Generated #{combined_pins_in_memory[[gemspec.name, gemspec.version]].length} combined pins for #{gemspec.name} #{gemspec.version}" }
260
- return combined_pins
261
- end
262
-
263
- if !yard_pins.nil?
264
- logger.debug { "Using only YARD pins for #{gemspec.name}:#{gemspec.version}" }
265
- combined_pins_in_memory[[gemspec.name, gemspec.version]] = yard_pins
266
- return combined_pins_in_memory[[gemspec.name, gemspec.version]]
267
- elsif !rbs_collection_pins.nil?
268
- logger.debug { "Using only RBS collection pins for #{gemspec.name}:#{gemspec.version}" }
269
- combined_pins_in_memory[[gemspec.name, gemspec.version]] = rbs_collection_pins
270
- return combined_pins_in_memory[[gemspec.name, gemspec.version]]
271
- else
272
- logger.debug { "Pins not yet cached for #{gemspec.name}:#{gemspec.version}" }
273
- return nil
274
- end
275
- end
276
-
277
- # @param path [String] require path that might be in the RBS stdlib collection
278
- # @return [void]
279
- def deserialize_stdlib_rbs_map path
280
- map = RbsMap::StdlibMap.load(path)
281
- if map.resolved?
282
- logger.debug { "Loading stdlib pins for #{path}" }
283
- @pins.concat map.pins
284
- logger.debug { "Loaded #{map.pins.length} stdlib pins for #{path}" }
285
- map.pins
286
- else
287
- # @todo Temporarily ignoring unresolved `require 'set'`
288
- logger.debug { "Require path #{path} could not be resolved in RBS" } unless path == 'set'
289
- nil
290
- end
291
- end
292
-
293
- # @param gemspec [Gem::Specification]
294
- # @param rbs_version_cache_key [String]
295
- # @return [Array<Pin::Base>, nil]
296
- def deserialize_rbs_collection_cache gemspec, rbs_version_cache_key
297
- return if rbs_collection_pins_in_memory.key?([gemspec, rbs_version_cache_key])
298
- cached = PinCache.deserialize_rbs_collection_gem(gemspec, rbs_version_cache_key)
299
- if cached
300
- logger.info { "Loaded #{cached.length} pins from RBS collection cache for #{gemspec.name}:#{gemspec.version}" } unless cached.empty?
301
- rbs_collection_pins_in_memory[[gemspec, rbs_version_cache_key]] = cached
302
- cached
303
- else
304
- logger.debug "No RBS collection pin cache for #{gemspec.name} #{gemspec.version}"
305
- @uncached_rbs_collection_gemspecs.push gemspec
306
- nil
307
- end
308
- end
309
-
310
- # @param path [String]
311
- # @return [::Array<Gem::Specification>, nil]
312
- def resolve_path_to_gemspecs path
313
- return nil if path.empty?
314
- return gemspecs_required_from_bundler if path == 'bundler/require'
315
-
316
- # @type [Gem::Specification, nil]
317
- gemspec = Gem::Specification.find_by_path(path)
318
- if gemspec.nil?
319
- gem_name_guess = path.split('/').first
320
- begin
321
- # this can happen when the gem is included via a local path in
322
- # a Gemfile; Gem doesn't try to index the paths in that case.
323
- #
324
- # See if we can make a good guess:
325
- potential_gemspec = Gem::Specification.find_by_name(gem_name_guess)
326
- file = "lib/#{path}.rb"
327
- gemspec = potential_gemspec if potential_gemspec.files.any? { |gemspec_file| file == gemspec_file }
328
- rescue Gem::MissingSpecError
329
- logger.debug { "Require path #{path} could not be resolved to a gem via find_by_path or guess of #{gem_name_guess}" }
330
- []
331
- end
332
- end
333
- return nil if gemspec.nil?
334
- [gemspec_or_preference(gemspec)]
192
+ @required_gems_map ||= requires.to_h { |path| [path, workspace.resolve_require(path)] }
335
193
  end
336
194
 
337
- # @param gemspec [Gem::Specification]
338
- # @return [Gem::Specification]
339
- def gemspec_or_preference gemspec
340
- # :nocov: dormant feature
341
- return gemspec unless preference_map.key?(gemspec.name)
342
- return gemspec if gemspec.version == preference_map[gemspec.name].version
343
-
344
- change_gemspec_version gemspec, preference_map[gemspec.name].version
345
- # :nocov:
346
- end
347
-
348
- # @param gemspec [Gem::Specification]
349
- # @param version [Gem::Version]
350
- # @return [Gem::Specification]
351
- def change_gemspec_version gemspec, version
352
- Gem::Specification.find_by_name(gemspec.name, "= #{version}")
353
- rescue Gem::MissingSpecError
354
- Solargraph.logger.info "Gem #{gemspec.name} version #{version} not found. Using #{gemspec.version} instead"
355
- gemspec
356
- end
357
-
358
- # @param gemspec [Gem::Specification]
359
- # @return [Array<Gem::Specification>]
360
- def fetch_dependencies gemspec
361
- # @param spec [Gem::Dependency]
362
- # @param deps [Set<Gem::Specification>]
363
- only_runtime_dependencies(gemspec).each_with_object(Set.new) do |spec, deps|
364
- Solargraph.logger.info "Adding #{spec.name} dependency for #{gemspec.name}"
365
- dep = Gem.loaded_specs[spec.name]
366
- # @todo is next line necessary?
367
- # @sg-ignore Unresolved call to requirement on Gem::Dependency
368
- dep ||= Gem::Specification.find_by_name(spec.name, spec.requirement)
369
- deps.merge fetch_dependencies(dep) if deps.add?(dep)
370
- rescue Gem::MissingSpecError
371
- # @sg-ignore Unresolved call to requirement on Gem::Dependency
372
- Solargraph.logger.warn "Gem dependency #{spec.name} #{spec.requirement} for #{gemspec.name} not found in RubyGems."
373
- end.to_a
374
- end
375
-
376
- # @param gemspec [Gem::Specification]
377
- # @return [Array<Gem::Dependency>]
378
- def only_runtime_dependencies gemspec
379
- gemspec.dependencies - gemspec.development_dependencies
380
- end
381
-
382
-
383
195
  def inspect
384
196
  self.class.inspect
385
197
  end
386
-
387
- # @return [Array<Gem::Specification>, nil]
388
- def gemspecs_required_from_bundler
389
- # @todo Handle projects with custom Bundler/Gemfile setups
390
- return unless workspace.gemfile?
391
-
392
- if workspace.gemfile? && Bundler.definition&.lockfile&.to_s&.start_with?(workspace.directory)
393
- # Find only the gems bundler is now using
394
- Bundler.definition.locked_gems.specs.flat_map do |lazy_spec|
395
- logger.info "Handling #{lazy_spec.name}:#{lazy_spec.version}"
396
- [Gem::Specification.find_by_name(lazy_spec.name, lazy_spec.version)]
397
- rescue Gem::MissingSpecError => e
398
- logger.info("Could not find #{lazy_spec.name}:#{lazy_spec.version} with find_by_name, falling back to guess")
399
- # can happen in local filesystem references
400
- specs = resolve_path_to_gemspecs lazy_spec.name
401
- logger.warn "Gem #{lazy_spec.name} #{lazy_spec.version} from bundle not found: #{e}" if specs.nil?
402
- next specs
403
- end.compact
404
- else
405
- logger.info 'Fetching gemspecs required from Bundler (bundler/require)'
406
- gemspecs_required_from_external_bundle
407
- end
408
- end
409
-
410
- # @return [Array<Gem::Specification>, nil]
411
- def gemspecs_required_from_external_bundle
412
- logger.info 'Fetching gemspecs required from external bundle'
413
- return [] unless workspace&.directory
414
-
415
- Solargraph.with_clean_env do
416
- cmd = [
417
- 'ruby', '-e',
418
- "require 'bundler'; require 'json'; Dir.chdir('#{workspace&.directory}') { puts Bundler.definition.locked_gems.specs.map { |spec| [spec.name, spec.version] }.to_h.to_json }"
419
- ]
420
- o, e, s = Open3.capture3(*cmd)
421
- if s.success?
422
- Solargraph.logger.debug "External bundle: #{o}"
423
- hash = o && !o.empty? ? JSON.parse(o.split("\n").last) : {}
424
- hash.flat_map do |name, version|
425
- Gem::Specification.find_by_name(name, version)
426
- rescue Gem::MissingSpecError => e
427
- logger.info("Could not find #{name}:#{version} with find_by_name, falling back to guess")
428
- # can happen in local filesystem references
429
- specs = resolve_path_to_gemspecs name
430
- logger.warn "Gem #{name} #{version} from bundle not found: #{e}" if specs.nil?
431
- next specs
432
- end.compact
433
- else
434
- Solargraph.logger.warn "Failed to load gems from bundle at #{workspace&.directory}: #{e}"
435
- end
436
- end
437
- end
438
198
  end
439
199
  end
@@ -10,16 +10,16 @@ module Solargraph
10
10
 
11
11
  # @param other [Object]
12
12
  # @return [Boolean]
13
- def eql?(other)
13
+ def eql? other
14
14
  self.class.eql?(other.class) &&
15
- # @sg-ignore https://github.com/castwide/solargraph/pull/1114
15
+ # @sg-ignore flow sensitive typing should support .class == .class
16
16
  equality_fields.eql?(other.equality_fields)
17
17
  end
18
18
 
19
19
  # @param other [Object]
20
20
  # @return [Boolean]
21
- def ==(other)
22
- self.eql?(other)
21
+ def == other
22
+ eql?(other)
23
23
  end
24
24
 
25
25
  def hash