solargraph 0.58.3 → 0.59.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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.github/workflows/linting.yml +12 -5
  4. data/.github/workflows/plugins.yml +54 -34
  5. data/.github/workflows/rspec.yml +15 -28
  6. data/.github/workflows/typecheck.yml +6 -3
  7. data/.rubocop.yml +38 -6
  8. data/.rubocop_todo.yml +53 -966
  9. data/CHANGELOG.md +24 -0
  10. data/Gemfile +3 -1
  11. data/README.md +3 -3
  12. data/Rakefile +26 -23
  13. data/bin/solargraph +2 -1
  14. data/lib/solargraph/api_map/cache.rb +3 -3
  15. data/lib/solargraph/api_map/constants.rb +12 -3
  16. data/lib/solargraph/api_map/index.rb +29 -18
  17. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  18. data/lib/solargraph/api_map/store.rb +40 -30
  19. data/lib/solargraph/api_map.rb +160 -78
  20. data/lib/solargraph/bench.rb +2 -3
  21. data/lib/solargraph/complex_type/conformance.rb +176 -0
  22. data/lib/solargraph/complex_type/type_methods.rb +31 -18
  23. data/lib/solargraph/complex_type/unique_type.rb +221 -63
  24. data/lib/solargraph/complex_type.rb +173 -59
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -111
  26. data/lib/solargraph/convention/base.rb +50 -50
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +1 -1
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +7 -5
  29. data/lib/solargraph/convention/data_definition.rb +5 -2
  30. data/lib/solargraph/convention/gemfile.rb +1 -1
  31. data/lib/solargraph/convention/gemspec.rb +1 -1
  32. data/lib/solargraph/convention/rakefile.rb +1 -1
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +2 -1
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +4 -3
  35. data/lib/solargraph/convention/struct_definition.rb +8 -4
  36. data/lib/solargraph/convention.rb +2 -2
  37. data/lib/solargraph/converters/dd.rb +2 -0
  38. data/lib/solargraph/converters/dl.rb +2 -0
  39. data/lib/solargraph/converters/dt.rb +2 -0
  40. data/lib/solargraph/converters/misc.rb +2 -0
  41. data/lib/solargraph/diagnostics/require_not_found.rb +1 -0
  42. data/lib/solargraph/diagnostics/rubocop.rb +11 -10
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  44. data/lib/solargraph/diagnostics/type_check.rb +11 -10
  45. data/lib/solargraph/diagnostics/update_errors.rb +4 -8
  46. data/lib/solargraph/diagnostics.rb +55 -55
  47. data/lib/solargraph/doc_map.rb +38 -39
  48. data/lib/solargraph/environ.rb +52 -52
  49. data/lib/solargraph/equality.rb +4 -4
  50. data/lib/solargraph/gem_pins.rb +4 -15
  51. data/lib/solargraph/language_server/error_codes.rb +10 -10
  52. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  53. data/lib/solargraph/language_server/host/dispatch.rb +3 -3
  54. data/lib/solargraph/language_server/host/message_worker.rb +4 -3
  55. data/lib/solargraph/language_server/host/sources.rb +2 -1
  56. data/lib/solargraph/language_server/host.rb +35 -28
  57. data/lib/solargraph/language_server/message/base.rb +1 -1
  58. data/lib/solargraph/language_server/message/client/register_capability.rb +1 -3
  59. data/lib/solargraph/language_server/message/completion_item/resolve.rb +6 -8
  60. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +12 -18
  61. data/lib/solargraph/language_server/message/extended/document.rb +1 -0
  62. data/lib/solargraph/language_server/message/extended/document_gems.rb +7 -7
  63. data/lib/solargraph/language_server/message/extended/download_core.rb +2 -1
  64. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  65. data/lib/solargraph/language_server/message/extended/search.rb +1 -1
  66. data/lib/solargraph/language_server/message/initialize.rb +20 -14
  67. data/lib/solargraph/language_server/message/initialized.rb +28 -28
  68. data/lib/solargraph/language_server/message/text_document/completion.rb +10 -8
  69. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -32
  70. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +17 -10
  71. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -19
  72. data/lib/solargraph/language_server/message/text_document/formatting.rb +8 -6
  73. data/lib/solargraph/language_server/message/text_document/hover.rb +5 -5
  74. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +13 -6
  75. data/lib/solargraph/language_server/message/text_document/references.rb +17 -10
  76. data/lib/solargraph/language_server/message/text_document/rename.rb +20 -13
  77. data/lib/solargraph/language_server/message/text_document/signature_help.rb +3 -2
  78. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -17
  79. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  80. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +34 -28
  81. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +38 -30
  82. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +23 -17
  83. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -17
  84. data/lib/solargraph/language_server/message.rb +1 -1
  85. data/lib/solargraph/language_server/progress.rb +143 -143
  86. data/lib/solargraph/language_server/request.rb +4 -2
  87. data/lib/solargraph/language_server/transport/adapter.rb +68 -68
  88. data/lib/solargraph/language_server/transport/data_reader.rb +11 -13
  89. data/lib/solargraph/language_server/uri_helpers.rb +2 -2
  90. data/lib/solargraph/language_server.rb +20 -20
  91. data/lib/solargraph/library.rb +57 -38
  92. data/lib/solargraph/location.rb +17 -14
  93. data/lib/solargraph/logging.rb +22 -4
  94. data/lib/solargraph/page.rb +1 -1
  95. data/lib/solargraph/parser/comment_ripper.rb +19 -4
  96. data/lib/solargraph/parser/flow_sensitive_typing.rb +324 -108
  97. data/lib/solargraph/parser/node_processor/base.rb +34 -4
  98. data/lib/solargraph/parser/node_processor.rb +8 -7
  99. data/lib/solargraph/parser/parser_gem/class_methods.rb +30 -14
  100. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -1
  101. data/lib/solargraph/parser/parser_gem/node_chainer.rb +51 -25
  102. data/lib/solargraph/parser/parser_gem/node_methods.rb +181 -73
  103. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +24 -24
  104. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +4 -4
  105. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +13 -11
  106. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +9 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +11 -12
  108. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +36 -36
  109. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +24 -24
  110. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +10 -3
  111. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +9 -8
  112. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +24 -24
  113. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +36 -6
  114. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +5 -3
  115. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  116. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +3 -1
  117. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +40 -40
  118. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +3 -3
  119. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  120. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -1
  121. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  122. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -5
  123. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +124 -113
  124. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +20 -20
  125. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +1 -1
  126. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  127. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +6 -2
  128. data/lib/solargraph/parser/parser_gem/node_processors.rb +4 -0
  129. data/lib/solargraph/parser/parser_gem.rb +2 -0
  130. data/lib/solargraph/parser/region.rb +9 -3
  131. data/lib/solargraph/parser/snippet.rb +3 -1
  132. data/lib/solargraph/parser.rb +2 -0
  133. data/lib/solargraph/pin/base.rb +126 -82
  134. data/lib/solargraph/pin/base_variable.rb +273 -24
  135. data/lib/solargraph/pin/block.rb +29 -6
  136. data/lib/solargraph/pin/breakable.rb +7 -1
  137. data/lib/solargraph/pin/callable.rb +65 -21
  138. data/lib/solargraph/pin/closure.rb +7 -10
  139. data/lib/solargraph/pin/common.rb +24 -6
  140. data/lib/solargraph/pin/compound_statement.rb +55 -0
  141. data/lib/solargraph/pin/constant.rb +3 -5
  142. data/lib/solargraph/pin/conversions.rb +10 -4
  143. data/lib/solargraph/pin/delegated_method.rb +19 -8
  144. data/lib/solargraph/pin/documenting.rb +4 -2
  145. data/lib/solargraph/pin/instance_variable.rb +5 -1
  146. data/lib/solargraph/pin/keyword.rb +0 -4
  147. data/lib/solargraph/pin/local_variable.rb +15 -59
  148. data/lib/solargraph/pin/method.rb +158 -104
  149. data/lib/solargraph/pin/method_alias.rb +8 -0
  150. data/lib/solargraph/pin/namespace.rb +19 -12
  151. data/lib/solargraph/pin/parameter.rb +102 -36
  152. data/lib/solargraph/pin/proxy_type.rb +4 -1
  153. data/lib/solargraph/pin/reference/override.rb +1 -1
  154. data/lib/solargraph/pin/reference/require.rb +14 -14
  155. data/lib/solargraph/pin/reference/superclass.rb +2 -0
  156. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  157. data/lib/solargraph/pin/reference.rb +20 -0
  158. data/lib/solargraph/pin/search.rb +8 -7
  159. data/lib/solargraph/pin/signature.rb +15 -12
  160. data/lib/solargraph/pin/singleton.rb +11 -11
  161. data/lib/solargraph/pin/symbol.rb +2 -1
  162. data/lib/solargraph/pin/until.rb +2 -4
  163. data/lib/solargraph/pin/while.rb +2 -4
  164. data/lib/solargraph/pin.rb +2 -0
  165. data/lib/solargraph/pin_cache.rb +22 -19
  166. data/lib/solargraph/position.rb +17 -10
  167. data/lib/solargraph/range.rb +16 -15
  168. data/lib/solargraph/rbs_map/conversions.rb +367 -231
  169. data/lib/solargraph/rbs_map/core_fills.rb +18 -11
  170. data/lib/solargraph/rbs_map/core_map.rb +24 -17
  171. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -5
  172. data/lib/solargraph/rbs_map.rb +76 -32
  173. data/lib/solargraph/server_methods.rb +1 -1
  174. data/lib/solargraph/shell.rb +258 -66
  175. data/lib/solargraph/source/chain/array.rb +3 -12
  176. data/lib/solargraph/source/chain/block_symbol.rb +13 -13
  177. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  178. data/lib/solargraph/source/chain/call.rb +96 -56
  179. data/lib/solargraph/source/chain/class_variable.rb +1 -1
  180. data/lib/solargraph/source/chain/constant.rb +5 -1
  181. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  182. data/lib/solargraph/source/chain/hash.rb +8 -5
  183. data/lib/solargraph/source/chain/head.rb +19 -19
  184. data/lib/solargraph/source/chain/if.rb +12 -10
  185. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  186. data/lib/solargraph/source/chain/link.rb +12 -22
  187. data/lib/solargraph/source/chain/literal.rb +22 -15
  188. data/lib/solargraph/source/chain/or.rb +10 -4
  189. data/lib/solargraph/source/chain/q_call.rb +2 -0
  190. data/lib/solargraph/source/chain/variable.rb +3 -1
  191. data/lib/solargraph/source/chain/z_super.rb +1 -3
  192. data/lib/solargraph/source/chain.rb +51 -38
  193. data/lib/solargraph/source/change.rb +12 -5
  194. data/lib/solargraph/source/cursor.rb +33 -18
  195. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  196. data/lib/solargraph/source/source_chainer.rb +56 -32
  197. data/lib/solargraph/source/updater.rb +5 -1
  198. data/lib/solargraph/source.rb +59 -35
  199. data/lib/solargraph/source_map/clip.rb +54 -30
  200. data/lib/solargraph/source_map/data.rb +4 -1
  201. data/lib/solargraph/source_map/mapper.rb +69 -42
  202. data/lib/solargraph/source_map.rb +21 -9
  203. data/lib/solargraph/type_checker/problem.rb +3 -1
  204. data/lib/solargraph/type_checker/rules.rb +81 -8
  205. data/lib/solargraph/type_checker.rb +196 -122
  206. data/lib/solargraph/version.rb +1 -1
  207. data/lib/solargraph/workspace/config.rb +14 -11
  208. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  209. data/lib/solargraph/workspace/require_paths.rb +1 -0
  210. data/lib/solargraph/workspace.rb +50 -28
  211. data/lib/solargraph/yard_map/cache.rb +25 -25
  212. data/lib/solargraph/yard_map/helpers.rb +8 -3
  213. data/lib/solargraph/yard_map/mapper/to_constant.rb +28 -28
  214. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  215. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  216. data/lib/solargraph/yard_map/mapper.rb +13 -8
  217. data/lib/solargraph/yard_map.rb +17 -18
  218. data/lib/solargraph/yard_tags.rb +2 -2
  219. data/lib/solargraph/yardoc.rb +7 -4
  220. data/lib/solargraph.rb +33 -10
  221. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  222. data/rbs/shims/ast/0/node.rbs +1 -1
  223. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  224. data/solargraph.gemspec +37 -35
  225. metadata +41 -42
  226. data/lib/solargraph/type_checker/checks.rb +0 -124
  227. data/lib/solargraph/type_checker/param_def.rb +0 -37
  228. data/lib/solargraph/yard_map/to_method.rb +0 -89
  229. data/rbs/fills/tuple/tuple.rbs +0 -149
@@ -3,6 +3,7 @@
3
3
  require 'benchmark'
4
4
  require 'thor'
5
5
  require 'yard'
6
+ require 'yaml'
6
7
 
7
8
  module Solargraph
8
9
  class Shell < Thor
@@ -15,7 +16,7 @@ module Solargraph
15
16
 
16
17
  map %w[--version -v] => :version
17
18
 
18
- desc "--version, -v", "Print the version"
19
+ desc '--version, -v', 'Print the version'
19
20
  # @return [void]
20
21
  def version
21
22
  puts Solargraph::VERSION
@@ -30,15 +31,15 @@ module Solargraph
30
31
  port = options[:port]
31
32
  port = available_port if port.zero?
32
33
  Backport.run do
33
- Signal.trap("INT") do
34
+ Signal.trap('INT') do
34
35
  Backport.stop
35
36
  end
36
- Signal.trap("TERM") do
37
+ Signal.trap('TERM') do
37
38
  Backport.stop
38
39
  end
39
40
  # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
40
41
  Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter
41
- STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
42
+ $stderr.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
42
43
  end
43
44
  end
44
45
 
@@ -47,15 +48,15 @@ module Solargraph
47
48
  def stdio
48
49
  require 'backport'
49
50
  Backport.run do
50
- Signal.trap("INT") do
51
+ Signal.trap('INT') do
51
52
  Backport.stop
52
53
  end
53
- Signal.trap("TERM") do
54
+ Signal.trap('TERM') do
54
55
  Backport.stop
55
56
  end
56
57
  # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
57
58
  Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
58
- STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
59
+ $stderr.puts "Solargraph is listening on stdio PID=#{Process.pid}"
59
60
  end
60
61
  end
61
62
 
@@ -63,11 +64,11 @@ module Solargraph
63
64
  option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
64
65
  # @param directory [String]
65
66
  # @return [void]
66
- def config(directory = '.')
67
+ def config directory = '.'
67
68
  matches = []
68
69
  if options[:extensions]
69
70
  Gem::Specification.each do |g|
70
- if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
71
+ if g.name.match(/^solargraph-[A-Za-z0-9_-]*?-ext/)
71
72
  require g.name
72
73
  matches.push g.name
73
74
  end
@@ -83,7 +84,7 @@ module Solargraph
83
84
  File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
84
85
  file.puts conf.to_yaml
85
86
  end
86
- STDOUT.puts "Configuration file initialized."
87
+ $stdout.puts 'Configuration file initialized.'
87
88
  end
88
89
 
89
90
  desc 'clear', 'Delete all cached documentation'
@@ -92,7 +93,7 @@ module Solargraph
92
93
  )
93
94
  # @return [void]
94
95
  def clear
95
- puts "Deleting all cached documentation (gems, core and stdlib)"
96
+ puts 'Deleting all cached documentation (gems, core and stdlib)'
96
97
  Solargraph::PinCache.clear
97
98
  end
98
99
  map 'clear-cache' => :clear
@@ -118,9 +119,11 @@ module Solargraph
118
119
  pins = rbs_map.pins || []
119
120
  PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
120
121
  end
122
+ rescue Gem::MissingSpecError
123
+ warn "Gem '#{gem}' not found"
121
124
  end
122
125
 
123
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
126
+ desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
124
127
  long_desc %(
125
128
  Specify one or more gem names to clear. 'core' or 'stdlib' may
126
129
  also be specified to clear cached system documentation.
@@ -146,23 +149,75 @@ module Solargraph
146
149
  end
147
150
  end
148
151
 
149
- desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
152
+ desc 'gems [GEM[=VERSION]...] [STDLIB...] [core]', 'Cache documentation for
153
+ installed libraries'
154
+ long_desc %( This command will cache the
155
+ generated type documentation for the specified libraries. While
156
+ Solargraph will generate this on the fly when needed, it takes
157
+ time. This command will generate it in advance, which can be
158
+ useful for CI scenarios.
159
+
160
+ With no arguments, it will cache all libraries in the current
161
+ workspace. If a gem or standard library name is specified, it
162
+ will cache that library's type documentation.
163
+
164
+ An equals sign after a gem will allow a specific gem version
165
+ to be cached.
166
+
167
+ The 'core' argument can be used to cache the type
168
+ documentation for the core Ruby libraries.
169
+
170
+ If the library is already cached, it will be rebuilt if the
171
+ --rebuild option is set.
172
+
173
+ Cached documentation is stored in #{PinCache.base_dir}, which
174
+ can be stored between CI runs.
175
+ )
150
176
  option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
151
177
  # @param names [Array<String>]
152
178
  # @return [void]
153
179
  def gems *names
154
- api_map = ApiMap.load('.')
180
+ # print time with ms
181
+ workspace = Solargraph::Workspace.new('.')
182
+
155
183
  if names.empty?
156
- Gem::Specification.to_a.each { |spec| do_cache spec, api_map }
157
- STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems."
184
+ Gem::Specification.to_a.each { |spec| do_cache spec, rebuild: options[:rebuild] }
185
+ $stderr.puts "Documentation cached for all #{Gem::Specification.count} gems."
158
186
  else
187
+ warn("Caching these gems: #{names}")
159
188
  names.each do |name|
160
- spec = Gem::Specification.find_by_name(*name.split('='))
161
- do_cache spec, api_map
189
+ if name == 'core'
190
+ # @sg-ignore cache_core and core? are dynamically defined
191
+ PinCache.cache_core(out: $stdout) # if !PinCache.core? || options[:rebuild]
192
+ next
193
+ end
194
+
195
+ gemspec = workspace.find_gem(*name.split('='))
196
+ if gemspec.nil?
197
+ warn "Gem '#{name}' not found"
198
+ else
199
+ if options[:rebuild] || !PinCache.has_yard?(gemspec)
200
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
201
+ PinCache.serialize_yard_gem(gemspec, pins)
202
+ end
203
+
204
+ workspace = Solargraph::Workspace.new(Dir.pwd)
205
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
206
+ if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
207
+ # cache pins even if result is zero, so we don't retry building pins
208
+ pins = rbs_map.pins || []
209
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
210
+ end
211
+ end
162
212
  rescue Gem::MissingSpecError
163
213
  warn "Gem '#{name}' not found"
214
+ rescue Gem::Requirement::BadRequirementError => e
215
+ warn "Gem '#{name}' failed while loading"
216
+ warn e.message
217
+ # @sg-ignore Need to add nil check here
218
+ warn e.backtrace.join("\n")
164
219
  end
165
- STDERR.puts "Documentation cached for #{names.count} gems."
220
+ warn "Documentation cached for #{names.count} gems."
166
221
  end
167
222
  end
168
223
 
@@ -179,7 +234,7 @@ module Solargraph
179
234
 
180
235
  Type checking levels are normal, typed, strict, and strong.
181
236
  )
182
- option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
237
+ option :level, type: :string, aliases: %i[mode m l], desc: 'Type checking level', default: 'normal'
183
238
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
184
239
  # @return [void]
185
240
  def typecheck *files
@@ -187,7 +242,10 @@ module Solargraph
187
242
  workspace = Solargraph::Workspace.new(directory)
188
243
  level = options[:level].to_sym
189
244
  rules = workspace.rules(level)
190
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
245
+ api_map =
246
+ Solargraph::ApiMap.load_with_cache(directory, $stdout,
247
+ loose_unions:
248
+ !rules.require_all_unique_types_support_call?)
191
249
  probcount = 0
192
250
  if files.empty?
193
251
  files = api_map.source_maps.map(&:filename)
@@ -195,23 +253,28 @@ module Solargraph
195
253
  files.map! { |file| File.realpath(file) }
196
254
  end
197
255
  filecount = 0
198
-
199
- time = Benchmark.measure {
256
+ time = Benchmark.measure do
200
257
  files.each do |file|
201
- checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym, workspace: workspace)
258
+ checker = TypeChecker.new(file, api_map: api_map, rules: rules, level: options[:level].to_sym,
259
+ workspace: workspace)
202
260
  problems = checker.problems
203
261
  next if problems.empty?
204
262
  problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
205
- puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
263
+ puts problems.map { |prob|
264
+ "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}"
265
+ }.join("\n")
206
266
  filecount += 1
207
267
  probcount += problems.length
208
268
  end
209
- # "
210
- }
269
+ end
211
270
  puts "Typecheck finished in #{time.real} seconds."
212
- puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
271
+ puts "#{probcount} problem#{if probcount != 1
272
+ 's'
273
+ end} found#{if files.length != 1
274
+ " in #{filecount} of #{files.length} files"
275
+ end}."
213
276
  # "
214
- exit 1 if probcount > 0
277
+ exit 1 if probcount.positive?
215
278
  end
216
279
 
217
280
  desc 'scan', 'Test the workspace for problems'
@@ -228,21 +291,27 @@ module Solargraph
228
291
  directory = File.realpath(options[:directory])
229
292
  # @type [Solargraph::ApiMap, nil]
230
293
  api_map = nil
231
- time = Benchmark.measure {
294
+ time = Benchmark.measure do
232
295
  api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
296
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
233
297
  api_map.pins.each do |pin|
234
- begin
235
- puts pin_description(pin) if options[:verbose]
236
- pin.typify api_map
237
- pin.probe api_map
238
- rescue StandardError => e
239
- STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
240
- STDERR.puts "[#{e.class}]: #{e.message}"
241
- STDERR.puts e.backtrace.join("\n")
242
- exit 1
243
- end
298
+ puts pin_description(pin) if options[:verbose]
299
+ pin.typify api_map
300
+ pin.probe api_map
301
+ rescue StandardError => e
302
+ # @todo to add nil check here
303
+ # @todo should warn on nil dereference below
304
+ warn "Error testing #{pin_description(pin)} #{if pin.location
305
+ "at #{pin.location.filename}:#{pin.location.range.start.line + 1}"
306
+ end}"
307
+ warn "[#{e.class}]: #{e.message}"
308
+ # @todo Need to add nil check here
309
+ # @todo flow sensitive typing should be able to handle redefinition
310
+ warn e.backtrace.join("\n")
311
+ exit 1
244
312
  end
245
- }
313
+ end
314
+ # @sg-ignore Need to add nil check here
246
315
  puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
247
316
  end
248
317
 
@@ -256,12 +325,15 @@ module Solargraph
256
325
  puts "#{workspace.filenames.length} files total."
257
326
  end
258
327
 
259
- desc 'pin [PATH]', 'Describe a pin', hide: true
328
+ desc 'pin [PATH]', 'Describe a pin'
260
329
  option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false
261
- option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations', default: false
330
+ option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations',
331
+ default: false
262
332
  option :references, type: :boolean, desc: 'Show references', default: false
263
- option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', default: false
264
- option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses', default: false
333
+ option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference',
334
+ default: false
335
+ option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses',
336
+ default: false
265
337
  # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
266
338
  # @return [void]
267
339
  def pin path
@@ -286,10 +358,11 @@ module Solargraph
286
358
  pin = pins.first
287
359
  case pin
288
360
  when nil
289
- $stderr.puts "Pin not found for path '#{path}'"
361
+ warn "Pin not found for path '#{path}'"
290
362
  exit 1
291
363
  when Pin::Namespace
292
364
  if options[:references]
365
+ # @sg-ignore Need to add nil check here
293
366
  superclass_tag = api_map.qualify_superclass(pin.return_type.tag)
294
367
  superclass_pin = api_map.get_path_pins(superclass_tag).first if superclass_tag
295
368
  references[:superclass] = superclass_pin if superclass_pin
@@ -313,36 +386,133 @@ module Solargraph
313
386
  end
314
387
  end
315
388
 
389
+ desc 'profile [FILE]', 'Profile go-to-definition performance using vernier'
390
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
391
+ option :output_dir, type: :string, aliases: :o, desc: 'The output directory for profiles', default: './tmp/profiles'
392
+ option :line, type: :numeric, aliases: :l, desc: 'Line number (0-based)', default: 4
393
+ option :column, type: :numeric, aliases: :c, desc: 'Column number', default: 10
394
+ option :memory, type: :boolean, aliases: :m, desc: 'Include memory usage counter', default: true
395
+ # @param file [String, nil]
396
+ # @return [void]
397
+ def profile file = nil
398
+ begin
399
+ require 'vernier'
400
+ rescue LoadError
401
+ $stderr.puts 'vernier gem not found. Please install this dependency:'
402
+ $stderr.puts
403
+ $stderr.puts " gem 'vernier', '>1.0', '<2'"
404
+
405
+ return
406
+ end
407
+
408
+ hooks = []
409
+ hooks << :memory_usage if options[:memory]
410
+
411
+ directory = File.realpath(options[:directory])
412
+ FileUtils.mkdir_p(options[:output_dir])
413
+
414
+ host = Solargraph::LanguageServer::Host.new
415
+ host.client_capabilities.merge!({ 'window' => { 'workDoneProgress' => true } })
416
+ # @param method [String] The message method
417
+ # @param params [Hash] The method parameters
418
+ # @return [void]
419
+ def host.send_notification method, params
420
+ puts "Notification: #{method} - #{params}"
421
+ end
422
+
423
+ puts 'Parsing and mapping source files...'
424
+ prepare_start = Time.now
425
+ Vernier.profile(out: "#{options[:output_dir]}/parse_benchmark.json.gz", hooks: hooks) do
426
+ puts 'Mapping libraries'
427
+ host.prepare(directory)
428
+ sleep 0.2 until host.libraries.all?(&:mapped?)
429
+ end
430
+ prepare_time = Time.now - prepare_start
431
+
432
+ puts 'Building the catalog...'
433
+ catalog_start = Time.now
434
+ Vernier.profile(out: "#{options[:output_dir]}/catalog_benchmark.json.gz", hooks: hooks) do
435
+ host.catalog
436
+ end
437
+ catalog_time = Time.now - catalog_start
438
+
439
+ # Determine test file
440
+ if file
441
+ test_file = File.join(directory, file)
442
+ else
443
+ test_file = File.join(directory, 'lib', 'other.rb')
444
+ unless File.exist?(test_file)
445
+ # Fallback to any Ruby file in the workspace
446
+ workspace = Solargraph::Workspace.new(directory)
447
+ test_file = workspace.filenames.find { |f| f.end_with?('.rb') }
448
+ unless test_file
449
+ warn 'No Ruby files found in workspace'
450
+ return
451
+ end
452
+ end
453
+ end
454
+
455
+ file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path(test_file))
456
+
457
+ puts "Profiling go-to-definition for #{test_file}"
458
+ puts "Position: line #{options[:line]}, column #{options[:column]}"
459
+
460
+ definition_start = Time.now
461
+ Vernier.profile(out: "#{options[:output_dir]}/definition_benchmark.json.gz", hooks: hooks) do
462
+ message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(
463
+ host, {
464
+ 'params' => {
465
+ 'textDocument' => { 'uri' => file_uri },
466
+ 'position' => { 'line' => options[:line], 'character' => options[:column] }
467
+ }
468
+ }
469
+ )
470
+ puts 'Processing go-to-definition request...'
471
+ result = message.process
472
+
473
+ puts "Result: #{result.inspect}"
474
+ end
475
+ definition_time = Time.now - definition_start
476
+
477
+ puts "\n=== Timing Results ==="
478
+ puts "Parsing & mapping: #{(prepare_time * 1000).round(2)}ms"
479
+ puts "Catalog building: #{(catalog_time * 1000).round(2)}ms"
480
+ puts "Go-to-definition: #{(definition_time * 1000).round(2)}ms"
481
+ total_time = prepare_time + catalog_time + definition_time
482
+ puts "Total time: #{(total_time * 1000).round(2)}ms"
483
+
484
+ puts "\nProfiles saved to:"
485
+ puts " - #{File.expand_path('parse_benchmark.json.gz', options[:output_dir])}"
486
+ puts " - #{File.expand_path('catalog_benchmark.json.gz', options[:output_dir])}"
487
+ puts " - #{File.expand_path('definition_benchmark.json.gz', options[:output_dir])}"
488
+
489
+ puts "\nUpload the JSON files to https://vernier.prof/ to view the profiles."
490
+ puts 'Or use https://rubygems.org/gems/profile-viewer to view them locally.'
491
+ end
492
+
316
493
  private
317
494
 
318
495
  # @param pin [Solargraph::Pin::Base]
319
496
  # @return [String]
320
497
  def pin_description pin
321
498
  desc = if pin.path.nil? || pin.path.empty?
322
- if pin.closure
323
- "#{pin.closure.path} | #{pin.name}"
324
- else
325
- "#{pin.context.namespace} | #{pin.name}"
326
- end
327
- else
328
- pin.path
329
- end
499
+ if pin.closure
500
+ # @sg-ignore Need to add nil check here
501
+ "#{pin.closure.path} | #{pin.name}"
502
+ else
503
+ "#{pin.context.namespace} | #{pin.name}"
504
+ end
505
+ else
506
+ pin.path
507
+ end
508
+ # @sg-ignore Need to add nil check here
330
509
  desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
331
510
  desc
332
511
  end
333
512
 
334
- # @param gemspec [Gem::Specification]
335
- # @param api_map [ApiMap]
513
+ # @param type [ComplexType, ComplexType::UniqueType]
336
514
  # @return [void]
337
- def do_cache gemspec, api_map
338
- # @todo if the rebuild: option is passed as a positional arg,
339
- # typecheck doesn't complain on the below line
340
- api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout)
341
- end
342
-
343
- # @param type [ComplexType]
344
- # @return [void]
345
- def print_type(type)
515
+ def print_type type
346
516
  if options[:rbs]
347
517
  puts type.to_rbs
348
518
  else
@@ -352,12 +522,34 @@ module Solargraph
352
522
 
353
523
  # @param pin [Solargraph::Pin::Base]
354
524
  # @return [void]
355
- def print_pin(pin)
525
+ def print_pin pin
356
526
  if options[:rbs]
357
527
  puts pin.to_rbs
358
528
  else
359
529
  puts pin.inspect
360
530
  end
361
531
  end
532
+
533
+ # @param gemspec [Gem::Specification, nil]
534
+ # @param rebuild [Boolean]
535
+ # @return [void]
536
+ def do_cache gemspec, rebuild: false
537
+ if gemspec.nil?
538
+ warn "Gem '#{gemspec&.name}' not found"
539
+ else
540
+ if rebuild || !PinCache.has_yard?(gemspec)
541
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
542
+ PinCache.serialize_yard_gem(gemspec, pins)
543
+ end
544
+
545
+ workspace = Solargraph::Workspace.new(Dir.pwd)
546
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
547
+ if rebuild || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
548
+ # cache pins even if result is zero, so we don't retry building pins
549
+ pins = rbs_map.pins || []
550
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
551
+ end
552
+ end
553
+ end
362
554
  end
363
555
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Solargraph
2
4
  class Source
3
5
  class Chain
@@ -17,18 +19,7 @@ module Solargraph
17
19
  # @param name_pin [Pin::Base]
18
20
  # @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
19
21
  def resolve api_map, name_pin, locals
20
- child_types = @children.map do |child|
21
- child.infer(api_map, name_pin, locals).simplify_literals
22
- end
23
- type = if child_types.length == 0 || child_types.any?(&:undefined?)
24
- ComplexType::UniqueType.new('Array', rooted: true)
25
- elsif child_types.uniq.length == 1 && child_types.first.defined?
26
- ComplexType::UniqueType.new('Array', [], child_types.uniq, rooted: true, parameters_type: :list)
27
- elsif child_types.length == 0
28
- ComplexType::UniqueType.new('Array', rooted: true, parameters_type: :list)
29
- else
30
- ComplexType::UniqueType.new('Array', [], child_types, rooted: true, parameters_type: :fixed)
31
- end
22
+ type = ComplexType::UniqueType.new('Array', rooted: true)
32
23
  [Pin::ProxyType.anonymous(type, source: :chain)]
33
24
  end
34
25
  end
@@ -1,13 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class Source
5
- class Chain
6
- class BlockSymbol < Link
7
- def resolve api_map, name_pin, locals
8
- [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
- end
10
- end
11
- end
12
- end
13
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ class BlockSymbol < Link
7
+ def resolve api_map, name_pin, locals
8
+ [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,13 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class Source
5
- class Chain
6
- class BlockVariable < Link
7
- def resolve api_map, name_pin, locals
8
- [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
- end
10
- end
11
- end
12
- end
13
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ class BlockVariable < Link
7
+ def resolve api_map, name_pin, locals
8
+ [Pin::ProxyType.anonymous(ComplexType.try_parse('::Proc'), source: :chain)]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end