solargraph 0.58.3 → 0.60.0

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 (238) 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 +18 -28
  6. data/.github/workflows/typecheck.yml +6 -3
  7. data/.rubocop.yml +39 -6
  8. data/.rubocop_todo.yml +53 -966
  9. data/CHANGELOG.md +35 -0
  10. data/Gemfile +6 -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 +42 -20
  17. data/lib/solargraph/api_map/source_to_yard.rb +22 -9
  18. data/lib/solargraph/api_map/store.rb +61 -36
  19. data/lib/solargraph/api_map.rb +192 -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 +225 -63
  24. data/lib/solargraph/complex_type.rb +177 -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 +39 -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 +222 -72
  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 +64 -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 +156 -84
  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 +67 -23
  138. data/lib/solargraph/pin/closure.rb +7 -10
  139. data/lib/solargraph/pin/common.rb +36 -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 +205 -111
  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 +278 -191
  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/rbs_translator.rb +206 -0
  174. data/lib/solargraph/server_methods.rb +1 -1
  175. data/lib/solargraph/shell.rb +330 -71
  176. data/lib/solargraph/source/chain/array.rb +3 -12
  177. data/lib/solargraph/source/chain/block_symbol.rb +13 -13
  178. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  179. data/lib/solargraph/source/chain/call.rb +91 -119
  180. data/lib/solargraph/source/chain/class_variable.rb +1 -1
  181. data/lib/solargraph/source/chain/constant.rb +5 -1
  182. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  183. data/lib/solargraph/source/chain/hash.rb +8 -5
  184. data/lib/solargraph/source/chain/head.rb +19 -19
  185. data/lib/solargraph/source/chain/if.rb +12 -10
  186. data/lib/solargraph/source/chain/instance_variable.rb +24 -1
  187. data/lib/solargraph/source/chain/link.rb +12 -22
  188. data/lib/solargraph/source/chain/literal.rb +22 -15
  189. data/lib/solargraph/source/chain/or.rb +10 -4
  190. data/lib/solargraph/source/chain/q_call.rb +2 -0
  191. data/lib/solargraph/source/chain/variable.rb +3 -1
  192. data/lib/solargraph/source/chain/z_super.rb +1 -3
  193. data/lib/solargraph/source/chain.rb +51 -38
  194. data/lib/solargraph/source/change.rb +12 -5
  195. data/lib/solargraph/source/cursor.rb +33 -18
  196. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  197. data/lib/solargraph/source/source_chainer.rb +56 -32
  198. data/lib/solargraph/source/updater.rb +5 -1
  199. data/lib/solargraph/source.rb +59 -35
  200. data/lib/solargraph/source_map/clip.rb +54 -30
  201. data/lib/solargraph/source_map/data.rb +4 -1
  202. data/lib/solargraph/source_map/mapper.rb +28 -131
  203. data/lib/solargraph/source_map.rb +35 -9
  204. data/lib/solargraph/type_checker/problem.rb +3 -1
  205. data/lib/solargraph/type_checker/rules.rb +81 -8
  206. data/lib/solargraph/type_checker.rb +196 -122
  207. data/lib/solargraph/version.rb +19 -1
  208. data/lib/solargraph/workspace/config.rb +14 -11
  209. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  210. data/lib/solargraph/workspace/require_paths.rb +1 -0
  211. data/lib/solargraph/workspace.rb +50 -28
  212. data/lib/solargraph/yard_map/cache.rb +25 -25
  213. data/lib/solargraph/yard_map/directives/attribute_directive.rb +65 -0
  214. data/lib/solargraph/yard_map/directives/domain_directive.rb +30 -0
  215. data/lib/solargraph/yard_map/directives/method_directive.rb +51 -0
  216. data/lib/solargraph/yard_map/directives/override_directive.rb +30 -0
  217. data/lib/solargraph/yard_map/directives/parse_directive.rb +53 -0
  218. data/lib/solargraph/yard_map/directives/visibility_directive.rb +70 -0
  219. data/lib/solargraph/yard_map/directives.rb +35 -0
  220. data/lib/solargraph/yard_map/helpers.rb +8 -3
  221. data/lib/solargraph/yard_map/macro.rb +113 -0
  222. data/lib/solargraph/yard_map/mapper/to_constant.rb +28 -28
  223. data/lib/solargraph/yard_map/mapper/to_method.rb +13 -7
  224. data/lib/solargraph/yard_map/mapper/to_namespace.rb +2 -1
  225. data/lib/solargraph/yard_map/mapper.rb +31 -8
  226. data/lib/solargraph/yard_map.rb +19 -18
  227. data/lib/solargraph/yard_tags.rb +2 -2
  228. data/lib/solargraph/yardoc.rb +7 -4
  229. data/lib/solargraph.rb +34 -10
  230. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  231. data/rbs/shims/ast/0/node.rbs +1 -1
  232. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  233. data/solargraph.gemspec +38 -35
  234. metadata +64 -42
  235. data/lib/solargraph/type_checker/checks.rb +0 -124
  236. data/lib/solargraph/type_checker/param_def.rb +0 -37
  237. data/lib/solargraph/yard_map/to_method.rb +0 -89
  238. data/rbs/fills/tuple/tuple.rbs +0 -149
@@ -3,10 +3,15 @@
3
3
  require 'benchmark'
4
4
  require 'thor'
5
5
  require 'yard'
6
+ require 'yaml'
7
+ require 'sord'
8
+ require 'tmpdir'
9
+
6
10
 
7
11
  module Solargraph
8
12
  class Shell < Thor
9
13
  include Solargraph::ServerMethods
14
+ include ApiMap::SourceToYard
10
15
 
11
16
  # Tell Thor to ensure the process exits with status 1 if any error happens.
12
17
  def self.exit_on_failure?
@@ -15,7 +20,7 @@ module Solargraph
15
20
 
16
21
  map %w[--version -v] => :version
17
22
 
18
- desc "--version, -v", "Print the version"
23
+ desc '--version, -v', 'Print the version'
19
24
  # @return [void]
20
25
  def version
21
26
  puts Solargraph::VERSION
@@ -30,15 +35,15 @@ module Solargraph
30
35
  port = options[:port]
31
36
  port = available_port if port.zero?
32
37
  Backport.run do
33
- Signal.trap("INT") do
38
+ Signal.trap('INT') do
34
39
  Backport.stop
35
40
  end
36
- Signal.trap("TERM") do
41
+ Signal.trap('TERM') do
37
42
  Backport.stop
38
43
  end
39
44
  # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
40
45
  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}"
46
+ $stderr.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
42
47
  end
43
48
  end
44
49
 
@@ -47,15 +52,15 @@ module Solargraph
47
52
  def stdio
48
53
  require 'backport'
49
54
  Backport.run do
50
- Signal.trap("INT") do
55
+ Signal.trap('INT') do
51
56
  Backport.stop
52
57
  end
53
- Signal.trap("TERM") do
58
+ Signal.trap('TERM') do
54
59
  Backport.stop
55
60
  end
56
61
  # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
57
62
  Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
58
- STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
63
+ $stderr.puts "Solargraph is listening on stdio PID=#{Process.pid}"
59
64
  end
60
65
  end
61
66
 
@@ -63,11 +68,11 @@ module Solargraph
63
68
  option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
64
69
  # @param directory [String]
65
70
  # @return [void]
66
- def config(directory = '.')
71
+ def config directory = '.'
67
72
  matches = []
68
73
  if options[:extensions]
69
74
  Gem::Specification.each do |g|
70
- if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
75
+ if g.name.match(/^solargraph-[A-Za-z0-9_-]*?-ext/)
71
76
  require g.name
72
77
  matches.push g.name
73
78
  end
@@ -83,7 +88,7 @@ module Solargraph
83
88
  File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
84
89
  file.puts conf.to_yaml
85
90
  end
86
- STDOUT.puts "Configuration file initialized."
91
+ $stdout.puts 'Configuration file initialized.'
87
92
  end
88
93
 
89
94
  desc 'clear', 'Delete all cached documentation'
@@ -92,7 +97,7 @@ module Solargraph
92
97
  )
93
98
  # @return [void]
94
99
  def clear
95
- puts "Deleting all cached documentation (gems, core and stdlib)"
100
+ puts 'Deleting all cached documentation (gems, core and stdlib)'
96
101
  Solargraph::PinCache.clear
97
102
  end
98
103
  map 'clear-cache' => :clear
@@ -111,16 +116,16 @@ module Solargraph
111
116
  PinCache.serialize_yard_gem(gemspec, pins)
112
117
  end
113
118
 
114
- workspace = Solargraph::Workspace.new(Dir.pwd)
115
- rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
119
+ workspace = Solargraph::Workspace.new(Dir.pwd) if File.exist?('rbs_collection.yaml')
120
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace&.rbs_collection_path, workspace&.rbs_collection_config_path)
116
121
  if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
117
- # cache pins even if result is zero, so we don't retry building pins
118
- pins = rbs_map.pins || []
119
- PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
122
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, rbs_map.pins)
120
123
  end
124
+ rescue Gem::MissingSpecError
125
+ warn "Gem '#{gem}' not found"
121
126
  end
122
127
 
123
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
128
+ desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
124
129
  long_desc %(
125
130
  Specify one or more gem names to clear. 'core' or 'stdlib' may
126
131
  also be specified to clear cached system documentation.
@@ -146,23 +151,75 @@ module Solargraph
146
151
  end
147
152
  end
148
153
 
149
- desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
154
+ desc 'gems [GEM[=VERSION]...] [STDLIB...] [core]', 'Cache documentation for
155
+ installed libraries'
156
+ long_desc %( This command will cache the
157
+ generated type documentation for the specified libraries. While
158
+ Solargraph will generate this on the fly when needed, it takes
159
+ time. This command will generate it in advance, which can be
160
+ useful for CI scenarios.
161
+
162
+ With no arguments, it will cache all libraries in the current
163
+ workspace. If a gem or standard library name is specified, it
164
+ will cache that library's type documentation.
165
+
166
+ An equals sign after a gem will allow a specific gem version
167
+ to be cached.
168
+
169
+ The 'core' argument can be used to cache the type
170
+ documentation for the core Ruby libraries.
171
+
172
+ If the library is already cached, it will be rebuilt if the
173
+ --rebuild option is set.
174
+
175
+ Cached documentation is stored in #{PinCache.base_dir}, which
176
+ can be stored between CI runs.
177
+ )
150
178
  option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
151
179
  # @param names [Array<String>]
152
180
  # @return [void]
153
181
  def gems *names
154
- api_map = ApiMap.load('.')
182
+ # print time with ms
183
+ workspace = Solargraph::Workspace.new('.')
184
+
155
185
  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."
186
+ Gem::Specification.to_a.each { |spec| do_cache spec, rebuild: options[:rebuild] }
187
+ $stderr.puts "Documentation cached for all #{Gem::Specification.count} gems."
158
188
  else
189
+ warn("Caching these gems: #{names}")
159
190
  names.each do |name|
160
- spec = Gem::Specification.find_by_name(*name.split('='))
161
- do_cache spec, api_map
191
+ if name == 'core'
192
+ # @sg-ignore cache_core and core? are dynamically defined
193
+ PinCache.cache_core(out: $stdout) # if !PinCache.core? || options[:rebuild]
194
+ next
195
+ end
196
+
197
+ gemspec = workspace.find_gem(*name.split('='))
198
+ if gemspec.nil?
199
+ warn "Gem '#{name}' not found"
200
+ else
201
+ if options[:rebuild] || !PinCache.has_yard?(gemspec)
202
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
203
+ PinCache.serialize_yard_gem(gemspec, pins)
204
+ end
205
+
206
+ workspace = Solargraph::Workspace.new(Dir.pwd)
207
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
208
+ if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
209
+ # cache pins even if result is zero, so we don't retry building pins
210
+ pins = rbs_map.pins || []
211
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
212
+ end
213
+ end
162
214
  rescue Gem::MissingSpecError
163
215
  warn "Gem '#{name}' not found"
216
+ rescue Gem::Requirement::BadRequirementError => e
217
+ warn "Gem '#{name}' failed while loading"
218
+ warn e.message
219
+ # @sg-ignore Need to add nil check here
220
+ warn e.backtrace.join("\n")
164
221
  end
165
- STDERR.puts "Documentation cached for #{names.count} gems."
222
+ warn "Documentation cached for #{names.count} gems."
166
223
  end
167
224
  end
168
225
 
@@ -179,7 +236,7 @@ module Solargraph
179
236
 
180
237
  Type checking levels are normal, typed, strict, and strong.
181
238
  )
182
- option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
239
+ option :level, type: :string, aliases: %i[mode m l], desc: 'Type checking level', default: 'normal'
183
240
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
184
241
  # @return [void]
185
242
  def typecheck *files
@@ -187,7 +244,10 @@ module Solargraph
187
244
  workspace = Solargraph::Workspace.new(directory)
188
245
  level = options[:level].to_sym
189
246
  rules = workspace.rules(level)
190
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
247
+ api_map =
248
+ Solargraph::ApiMap.load_with_cache(directory, $stdout,
249
+ loose_unions:
250
+ !rules.require_all_unique_types_support_call?)
191
251
  probcount = 0
192
252
  if files.empty?
193
253
  files = api_map.source_maps.map(&:filename)
@@ -195,23 +255,28 @@ module Solargraph
195
255
  files.map! { |file| File.realpath(file) }
196
256
  end
197
257
  filecount = 0
198
-
199
- time = Benchmark.measure {
258
+ time = Benchmark.measure do
200
259
  files.each do |file|
201
- checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym, workspace: workspace)
260
+ checker = TypeChecker.new(file, api_map: api_map, rules: rules, level: options[:level].to_sym,
261
+ workspace: workspace)
202
262
  problems = checker.problems
203
263
  next if problems.empty?
204
264
  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")
265
+ puts problems.map { |prob|
266
+ "#{prob.location.filename}:#{prob.location.range.start.line + 1}: #{prob.message}"
267
+ }.join("\n")
206
268
  filecount += 1
207
269
  probcount += problems.length
208
270
  end
209
- # "
210
- }
271
+ end
211
272
  puts "Typecheck finished in #{time.real} seconds."
212
- puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
273
+ puts "#{probcount} problem#{if probcount != 1
274
+ 's'
275
+ end} found#{if files.length != 1
276
+ " in #{filecount} of #{files.length} files"
277
+ end}."
213
278
  # "
214
- exit 1 if probcount > 0
279
+ exit 1 if probcount.positive?
215
280
  end
216
281
 
217
282
  desc 'scan', 'Test the workspace for problems'
@@ -228,21 +293,27 @@ module Solargraph
228
293
  directory = File.realpath(options[:directory])
229
294
  # @type [Solargraph::ApiMap, nil]
230
295
  api_map = nil
231
- time = Benchmark.measure {
296
+ time = Benchmark.measure do
232
297
  api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
298
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
233
299
  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
300
+ puts pin_description(pin) if options[:verbose]
301
+ pin.typify api_map
302
+ pin.probe api_map
303
+ rescue StandardError => e
304
+ # @todo to add nil check here
305
+ # @todo should warn on nil dereference below
306
+ warn "Error testing #{pin_description(pin)} #{if pin.location
307
+ "at #{pin.location.filename}:#{pin.location.range.start.line + 1}"
308
+ end}"
309
+ warn "[#{e.class}]: #{e.message}"
310
+ # @todo Need to add nil check here
311
+ # @todo flow sensitive typing should be able to handle redefinition
312
+ warn e.backtrace.join("\n")
313
+ exit 1
244
314
  end
245
- }
315
+ end
316
+ # @sg-ignore Need to add nil check here
246
317
  puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
247
318
  end
248
319
 
@@ -256,12 +327,15 @@ module Solargraph
256
327
  puts "#{workspace.filenames.length} files total."
257
328
  end
258
329
 
259
- desc 'pin [PATH]', 'Describe a pin', hide: true
330
+ desc 'pin [PATH]', 'Describe a pin'
260
331
  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
332
+ option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations',
333
+ default: false
262
334
  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
335
+ option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference',
336
+ default: false
337
+ option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses',
338
+ default: false
265
339
  # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
266
340
  # @return [void]
267
341
  def pin path
@@ -286,10 +360,11 @@ module Solargraph
286
360
  pin = pins.first
287
361
  case pin
288
362
  when nil
289
- $stderr.puts "Pin not found for path '#{path}'"
363
+ warn "Pin not found for path '#{path}'"
290
364
  exit 1
291
365
  when Pin::Namespace
292
366
  if options[:references]
367
+ # @sg-ignore Need to add nil check here
293
368
  superclass_tag = api_map.qualify_superclass(pin.return_type.tag)
294
369
  superclass_pin = api_map.get_path_pins(superclass_tag).first if superclass_tag
295
370
  references[:superclass] = superclass_pin if superclass_pin
@@ -313,36 +388,198 @@ module Solargraph
313
388
  end
314
389
  end
315
390
 
391
+ desc 'profile [FILE]', 'Profile go-to-definition performance using vernier'
392
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
393
+ option :output_dir, type: :string, aliases: :o, desc: 'The output directory for profiles', default: './tmp/profiles'
394
+ option :line, type: :numeric, aliases: :l, desc: 'Line number (0-based)', default: 4
395
+ option :column, type: :numeric, aliases: :c, desc: 'Column number', default: 10
396
+ option :memory, type: :boolean, aliases: :m, desc: 'Include memory usage counter', default: true
397
+ # @param file [String, nil]
398
+ # @return [void]
399
+ def profile file = nil
400
+ begin
401
+ require 'vernier'
402
+ rescue LoadError
403
+ $stderr.puts 'vernier gem not found. Please install this dependency:'
404
+ $stderr.puts
405
+ $stderr.puts " gem 'vernier', '>1.0', '<2'"
406
+
407
+ return
408
+ end
409
+
410
+ hooks = []
411
+ hooks << :memory_usage if options[:memory]
412
+
413
+ directory = File.realpath(options[:directory])
414
+ FileUtils.mkdir_p(options[:output_dir])
415
+
416
+ host = Solargraph::LanguageServer::Host.new
417
+ host.client_capabilities.merge!({ 'window' => { 'workDoneProgress' => true } })
418
+ # @param method [String] The message method
419
+ # @param params [Hash] The method parameters
420
+ # @return [void]
421
+ def host.send_notification method, params
422
+ puts "Notification: #{method} - #{params}"
423
+ end
424
+
425
+ parse_path = File.join(options[:output_dir], 'parse_benchmark.json.gz')
426
+ catalog_path = File.join(options[:output_dir], 'catalog_benchmark.json.gz')
427
+ definition_path = File.join(options[:output_dir], 'definition_benchmark.json.gz')
428
+
429
+ prepare_time = catalog_time = definition_time = nil
430
+
431
+ # Trap CTRL-C so the in-progress Vernier.profile block can unwind through
432
+ # its ensure clause and still write its output file. A second CTRL-C
433
+ # restores default handling for a hard exit.
434
+ interrupted = false
435
+ previous_int_trap = Signal.trap('INT') do
436
+ if interrupted
437
+ Signal.trap('INT', 'DEFAULT')
438
+ Process.kill('INT', Process.pid)
439
+ else
440
+ interrupted = true
441
+ puts "\nInterrupted. Finishing current profile (CTRL-C again to force exit)..."
442
+ raise Interrupt
443
+ end
444
+ end
445
+
446
+ begin
447
+ puts 'Parsing and mapping source files...'
448
+ prepare_start = Time.now
449
+ Vernier.profile(out: parse_path, hooks: hooks) do
450
+ puts 'Mapping libraries'
451
+ host.prepare(directory)
452
+ sleep 0.2 until host.libraries.all?(&:mapped?)
453
+ end
454
+ prepare_time = Time.now - prepare_start
455
+
456
+ puts 'Building the catalog...'
457
+ catalog_start = Time.now
458
+ Vernier.profile(out: catalog_path, hooks: hooks) do
459
+ host.catalog
460
+ end
461
+ catalog_time = Time.now - catalog_start
462
+
463
+ # Determine test file
464
+ if file
465
+ test_file = File.join(directory, file)
466
+ else
467
+ test_file = File.join(directory, 'lib', 'other.rb')
468
+ unless File.exist?(test_file)
469
+ # Fallback to any Ruby file in the workspace
470
+ workspace = Solargraph::Workspace.new(directory)
471
+ test_file = workspace.filenames.find { |f| f.end_with?('.rb') }
472
+ unless test_file
473
+ puts 'No Ruby files found in workspace'
474
+ return
475
+ end
476
+ end
477
+ end
478
+
479
+ file_uri = Solargraph::LanguageServer::UriHelpers.file_to_uri(File.absolute_path(test_file))
480
+
481
+ puts "Profiling go-to-definition for #{test_file}"
482
+ puts "Position: line #{options[:line]}, column #{options[:column]}"
483
+
484
+ definition_start = Time.now
485
+ Vernier.profile(out: definition_path, hooks: hooks) do
486
+ message = Solargraph::LanguageServer::Message::TextDocument::Definition.new(
487
+ host, {
488
+ 'params' => {
489
+ 'textDocument' => { 'uri' => file_uri },
490
+ 'position' => { 'line' => options[:line], 'character' => options[:column] }
491
+ }
492
+ }
493
+ )
494
+ puts 'Processing go-to-definition request...'
495
+ result = message.process
496
+
497
+ puts "Result: #{result.inspect}"
498
+ end
499
+ definition_time = Time.now - definition_start
500
+ rescue Interrupt
501
+ puts "\nProfile run interrupted; partial profile(s) have been written."
502
+ ensure
503
+ Signal.trap('INT', previous_int_trap || 'DEFAULT')
504
+
505
+ puts "\n=== Timing Results ==="
506
+ puts "Parsing & mapping: #{(prepare_time * 1000).round(2)}ms" if prepare_time
507
+ puts "Catalog building: #{(catalog_time * 1000).round(2)}ms" if catalog_time
508
+ puts "Go-to-definition: #{(definition_time * 1000).round(2)}ms" if definition_time
509
+ if prepare_time && catalog_time && definition_time
510
+ total_time = prepare_time + catalog_time + definition_time
511
+ puts "Total time: #{(total_time * 1000).round(2)}ms"
512
+ end
513
+
514
+ saved = [parse_path, catalog_path, definition_path].select { |p| File.exist?(p) }
515
+ unless saved.empty?
516
+ puts "\nProfiles saved to:"
517
+ saved.each { |p| puts " - #{File.expand_path(p)}" }
518
+
519
+ puts "\nUpload the JSON files to https://vernier.prof/ to view the profiles."
520
+ puts 'Or use https://rubygems.org/gems/profile-viewer to view them locally.'
521
+ end
522
+ end
523
+ end
524
+
525
+ desc 'rbs', 'Generate RBS definitions'
526
+ option :filename, type: :string, alias: :f, desc: 'Generated file name', default: 'sig.rbs'
527
+ option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true
528
+ def rbs
529
+ api_map = Solargraph::ApiMap.load('.')
530
+ pins = api_map.source_maps.flat_map(&:pins)
531
+ store = Solargraph::ApiMap::Store.new(pins)
532
+ if options[:inference]
533
+ puts 'Inferring untyped methods...'
534
+ store.method_pins.each do |pin|
535
+ next unless pin.return_type.undefined?
536
+ type = pin.typify(api_map)
537
+ type = pin.probe(api_map) if type.undefined?
538
+ pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s))
539
+ pin.instance_variable_set(:@return_type, type)
540
+ end
541
+ end
542
+ puts 'Generating yardocs...'
543
+ rake_yard(store)
544
+ work_dir = Dir.pwd
545
+ Dir.mktmpdir do |tmpdir|
546
+ Dir.chdir tmpdir do
547
+ yardoc = File.join(tmpdir, '.yardoc')
548
+ YARD::Registry.save(false, yardoc)
549
+ YARD::Registry.load(yardoc)
550
+ rel_dir = File.join('sig', options[:filename])
551
+ puts "Writing #{rel_dir}..."
552
+ target = File.join(work_dir, rel_dir)
553
+ FileUtils.mkdir_p(File.join(work_dir, 'sig'))
554
+ `sord #{target} --rbs --no-regenerate`
555
+ end
556
+ end
557
+ puts 'Done.'
558
+ end
559
+
316
560
  private
317
561
 
318
562
  # @param pin [Solargraph::Pin::Base]
319
563
  # @return [String]
320
564
  def pin_description pin
321
565
  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
566
+ if pin.closure
567
+ # @sg-ignore Need to add nil check here
568
+ "#{pin.closure.path} | #{pin.name}"
569
+ else
570
+ "#{pin.context.namespace} | #{pin.name}"
571
+ end
572
+ else
573
+ pin.path
574
+ end
575
+ # @sg-ignore Need to add nil check here
330
576
  desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
331
577
  desc
332
578
  end
333
579
 
334
- # @param gemspec [Gem::Specification]
335
- # @param api_map [ApiMap]
336
- # @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]
580
+ # @param type [ComplexType, ComplexType::UniqueType]
344
581
  # @return [void]
345
- def print_type(type)
582
+ def print_type type
346
583
  if options[:rbs]
347
584
  puts type.to_rbs
348
585
  else
@@ -352,12 +589,34 @@ module Solargraph
352
589
 
353
590
  # @param pin [Solargraph::Pin::Base]
354
591
  # @return [void]
355
- def print_pin(pin)
592
+ def print_pin pin
356
593
  if options[:rbs]
357
594
  puts pin.to_rbs
358
595
  else
359
596
  puts pin.inspect
360
597
  end
361
598
  end
599
+
600
+ # @param gemspec [Gem::Specification, nil]
601
+ # @param rebuild [Boolean]
602
+ # @return [void]
603
+ def do_cache gemspec, rebuild: false
604
+ if gemspec.nil?
605
+ warn "Gem '#{gemspec&.name}' not found"
606
+ else
607
+ if rebuild || !PinCache.has_yard?(gemspec)
608
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
609
+ PinCache.serialize_yard_gem(gemspec, pins)
610
+ end
611
+
612
+ workspace = Solargraph::Workspace.new(Dir.pwd)
613
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
614
+ if rebuild || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
615
+ # cache pins even if result is zero, so we don't retry building pins
616
+ pins = rbs_map.pins || []
617
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
618
+ end
619
+ end
620
+ end
362
621
  end
363
622
  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