solargraph 0.59.0.dev.1 → 0.59.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 (175) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +3 -1
  3. data/.github/workflows/plugins.yml +12 -3
  4. data/.github/workflows/rspec.yml +9 -54
  5. data/.github/workflows/typecheck.yml +2 -1
  6. data/.gitignore +1 -0
  7. data/.rubocop.yml +38 -6
  8. data/.rubocop_todo.yml +40 -931
  9. data/CHANGELOG.md +22 -1
  10. data/Gemfile +3 -1
  11. data/Rakefile +25 -23
  12. data/bin/solargraph +2 -1
  13. data/lib/solargraph/api_map/constants.rb +0 -1
  14. data/lib/solargraph/api_map/index.rb +11 -11
  15. data/lib/solargraph/api_map/source_to_yard.rb +9 -8
  16. data/lib/solargraph/api_map/store.rb +28 -20
  17. data/lib/solargraph/api_map.rb +70 -41
  18. data/lib/solargraph/bench.rb +44 -45
  19. data/lib/solargraph/complex_type/type_methods.rb +14 -16
  20. data/lib/solargraph/complex_type/unique_type.rb +56 -47
  21. data/lib/solargraph/complex_type.rb +70 -62
  22. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  23. data/lib/solargraph/convention/data_definition/data_definition_node.rb +4 -4
  24. data/lib/solargraph/convention/data_definition.rb +1 -1
  25. data/lib/solargraph/convention/gemfile.rb +15 -15
  26. data/lib/solargraph/convention/gemspec.rb +23 -23
  27. data/lib/solargraph/convention/rakefile.rb +17 -17
  28. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +1 -1
  29. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +3 -3
  30. data/lib/solargraph/convention/struct_definition.rb +3 -3
  31. data/lib/solargraph/convention.rb +78 -78
  32. data/lib/solargraph/converters/dd.rb +19 -17
  33. data/lib/solargraph/converters/dl.rb +17 -15
  34. data/lib/solargraph/converters/dt.rb +17 -15
  35. data/lib/solargraph/converters/misc.rb +3 -1
  36. data/lib/solargraph/diagnostics/rubocop.rb +10 -10
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +3 -3
  38. data/lib/solargraph/diagnostics/type_check.rb +10 -10
  39. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  40. data/lib/solargraph/doc_map.rb +370 -132
  41. data/lib/solargraph/equality.rb +3 -3
  42. data/lib/solargraph/gem_pins.rb +19 -18
  43. data/lib/solargraph/language_server/error_codes.rb +20 -20
  44. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  45. data/lib/solargraph/language_server/host/dispatch.rb +2 -3
  46. data/lib/solargraph/language_server/host/message_worker.rb +2 -2
  47. data/lib/solargraph/language_server/host/sources.rb +1 -1
  48. data/lib/solargraph/language_server/host.rb +24 -21
  49. data/lib/solargraph/language_server/message/base.rb +97 -97
  50. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  51. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  52. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +10 -11
  53. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  54. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  55. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  56. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  57. data/lib/solargraph/language_server/message/text_document/completion.rb +8 -8
  58. data/lib/solargraph/language_server/message/text_document/definition.rb +41 -34
  59. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  60. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +29 -21
  61. data/lib/solargraph/language_server/message/text_document/formatting.rb +6 -6
  62. data/lib/solargraph/language_server/message/text_document/hover.rb +3 -5
  63. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  64. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  65. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  66. data/lib/solargraph/language_server/message/text_document/signature_help.rb +2 -2
  67. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -19
  68. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  69. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  70. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  71. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +27 -19
  72. data/lib/solargraph/language_server/message.rb +94 -94
  73. data/lib/solargraph/language_server/request.rb +29 -27
  74. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  75. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  76. data/lib/solargraph/library.rb +68 -95
  77. data/lib/solargraph/location.rb +10 -12
  78. data/lib/solargraph/logging.rb +4 -6
  79. data/lib/solargraph/page.rb +92 -92
  80. data/lib/solargraph/parser/comment_ripper.rb +12 -4
  81. data/lib/solargraph/parser/flow_sensitive_typing.rb +32 -44
  82. data/lib/solargraph/parser/node_processor/base.rb +4 -4
  83. data/lib/solargraph/parser/node_processor.rb +1 -1
  84. data/lib/solargraph/parser/parser_gem/class_methods.rb +4 -6
  85. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  86. data/lib/solargraph/parser/parser_gem/node_chainer.rb +20 -20
  87. data/lib/solargraph/parser/parser_gem/node_methods.rb +66 -65
  88. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +12 -12
  89. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +1 -2
  90. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +3 -3
  91. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  92. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +3 -3
  93. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -1
  94. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +1 -1
  95. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +3 -5
  96. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +118 -112
  97. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  98. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +1 -1
  99. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +1 -1
  100. data/lib/solargraph/parser/parser_gem.rb +14 -12
  101. data/lib/solargraph/parser/snippet.rb +2 -0
  102. data/lib/solargraph/parser.rb +25 -23
  103. data/lib/solargraph/pin/base.rb +76 -64
  104. data/lib/solargraph/pin/base_variable.rb +28 -71
  105. data/lib/solargraph/pin/block.rb +3 -2
  106. data/lib/solargraph/pin/breakable.rb +2 -0
  107. data/lib/solargraph/pin/callable.rb +23 -26
  108. data/lib/solargraph/pin/closure.rb +5 -4
  109. data/lib/solargraph/pin/common.rb +5 -2
  110. data/lib/solargraph/pin/compound_statement.rb +3 -3
  111. data/lib/solargraph/pin/constant.rb +43 -45
  112. data/lib/solargraph/pin/conversions.rb +9 -4
  113. data/lib/solargraph/pin/delegated_method.rb +4 -4
  114. data/lib/solargraph/pin/documenting.rb +3 -2
  115. data/lib/solargraph/pin/local_variable.rb +4 -4
  116. data/lib/solargraph/pin/method.rb +74 -70
  117. data/lib/solargraph/pin/namespace.rb +13 -12
  118. data/lib/solargraph/pin/parameter.rb +28 -27
  119. data/lib/solargraph/pin/proxy_type.rb +2 -0
  120. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  121. data/lib/solargraph/pin/reference.rb +18 -0
  122. data/lib/solargraph/pin/search.rb +2 -2
  123. data/lib/solargraph/pin/signature.rb +9 -14
  124. data/lib/solargraph/pin/symbol.rb +1 -0
  125. data/lib/solargraph/pin/until.rb +1 -3
  126. data/lib/solargraph/pin/while.rb +1 -3
  127. data/lib/solargraph/pin_cache.rb +71 -488
  128. data/lib/solargraph/position.rb +38 -17
  129. data/lib/solargraph/range.rb +10 -9
  130. data/lib/solargraph/rbs_map/conversions.rb +327 -221
  131. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  132. data/lib/solargraph/rbs_map/stdlib_map.rb +0 -1
  133. data/lib/solargraph/rbs_map.rb +5 -15
  134. data/lib/solargraph/server_methods.rb +16 -16
  135. data/lib/solargraph/shell.rb +224 -66
  136. data/lib/solargraph/source/chain/array.rb +39 -37
  137. data/lib/solargraph/source/chain/call.rb +49 -44
  138. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  139. data/lib/solargraph/source/chain/constant.rb +3 -1
  140. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  141. data/lib/solargraph/source/chain/hash.rb +8 -6
  142. data/lib/solargraph/source/chain/if.rb +11 -10
  143. data/lib/solargraph/source/chain/instance_variable.rb +3 -1
  144. data/lib/solargraph/source/chain/link.rb +99 -109
  145. data/lib/solargraph/source/chain/literal.rb +4 -6
  146. data/lib/solargraph/source/chain/or.rb +2 -4
  147. data/lib/solargraph/source/chain/q_call.rb +13 -11
  148. data/lib/solargraph/source/chain/variable.rb +15 -13
  149. data/lib/solargraph/source/chain/z_super.rb +28 -30
  150. data/lib/solargraph/source/chain.rb +26 -16
  151. data/lib/solargraph/source/change.rb +3 -3
  152. data/lib/solargraph/source/cursor.rb +18 -18
  153. data/lib/solargraph/source/encoding_fixes.rb +6 -7
  154. data/lib/solargraph/source/source_chainer.rb +46 -32
  155. data/lib/solargraph/source/updater.rb +1 -1
  156. data/lib/solargraph/source.rb +27 -29
  157. data/lib/solargraph/source_map/clip.rb +38 -30
  158. data/lib/solargraph/source_map/mapper.rb +51 -47
  159. data/lib/solargraph/source_map.rb +8 -4
  160. data/lib/solargraph/type_checker/rules.rb +8 -8
  161. data/lib/solargraph/type_checker.rb +95 -102
  162. data/lib/solargraph/version.rb +1 -1
  163. data/lib/solargraph/workspace/config.rb +11 -10
  164. data/lib/solargraph/workspace/gemspecs.rb +3 -3
  165. data/lib/solargraph/workspace.rb +45 -165
  166. data/lib/solargraph/yard_map/helpers.rb +6 -2
  167. data/lib/solargraph/yard_map/mapper/to_method.rb +8 -6
  168. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
  169. data/lib/solargraph/yard_map/mapper.rb +12 -12
  170. data/lib/solargraph/yard_map.rb +17 -18
  171. data/lib/solargraph/yard_tags.rb +20 -20
  172. data/lib/solargraph/yardoc.rb +26 -33
  173. data/lib/solargraph.rb +7 -5
  174. data/solargraph.gemspec +36 -35
  175. metadata +33 -38
@@ -16,7 +16,7 @@ module Solargraph
16
16
 
17
17
  map %w[--version -v] => :version
18
18
 
19
- desc "--version, -v", "Print the version"
19
+ desc '--version, -v', 'Print the version'
20
20
  # @return [void]
21
21
  def version
22
22
  puts Solargraph::VERSION
@@ -31,15 +31,15 @@ module Solargraph
31
31
  port = options[:port]
32
32
  port = available_port if port.zero?
33
33
  Backport.run do
34
- Signal.trap("INT") do
34
+ Signal.trap('INT') do
35
35
  Backport.stop
36
36
  end
37
- Signal.trap("TERM") do
37
+ Signal.trap('TERM') do
38
38
  Backport.stop
39
39
  end
40
40
  # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
41
41
  Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter
42
- STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
42
+ $stderr.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
43
43
  end
44
44
  end
45
45
 
@@ -48,15 +48,15 @@ module Solargraph
48
48
  def stdio
49
49
  require 'backport'
50
50
  Backport.run do
51
- Signal.trap("INT") do
51
+ Signal.trap('INT') do
52
52
  Backport.stop
53
53
  end
54
- Signal.trap("TERM") do
54
+ Signal.trap('TERM') do
55
55
  Backport.stop
56
56
  end
57
57
  # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
58
58
  Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
59
- STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
59
+ $stderr.puts "Solargraph is listening on stdio PID=#{Process.pid}"
60
60
  end
61
61
  end
62
62
 
@@ -64,11 +64,11 @@ module Solargraph
64
64
  option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
65
65
  # @param directory [String]
66
66
  # @return [void]
67
- def config(directory = '.')
67
+ def config directory = '.'
68
68
  matches = []
69
69
  if options[:extensions]
70
70
  Gem::Specification.each do |g|
71
- if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
71
+ if g.name.match(/^solargraph-[A-Za-z0-9_-]*?-ext/)
72
72
  require g.name
73
73
  matches.push g.name
74
74
  end
@@ -84,7 +84,7 @@ module Solargraph
84
84
  File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
85
85
  file.puts conf.to_yaml
86
86
  end
87
- STDOUT.puts "Configuration file initialized."
87
+ $stdout.puts 'Configuration file initialized.'
88
88
  end
89
89
 
90
90
  desc 'clear', 'Delete all cached documentation'
@@ -93,7 +93,7 @@ module Solargraph
93
93
  )
94
94
  # @return [void]
95
95
  def clear
96
- puts "Deleting all cached documentation (gems, core and stdlib)"
96
+ puts 'Deleting all cached documentation (gems, core and stdlib)'
97
97
  Solargraph::PinCache.clear
98
98
  end
99
99
  map 'clear-cache' => :clear
@@ -105,11 +105,25 @@ module Solargraph
105
105
  # @param gem [String]
106
106
  # @param version [String, nil]
107
107
  def cache gem, version = nil
108
- gems(gem + (version ? "=#{version}" : ''))
109
- # '
108
+ gemspec = Gem::Specification.find_by_name(gem, version)
109
+
110
+ if options[:rebuild] || !PinCache.has_yard?(gemspec)
111
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
112
+ PinCache.serialize_yard_gem(gemspec, pins)
113
+ end
114
+
115
+ workspace = Solargraph::Workspace.new(Dir.pwd)
116
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
117
+ if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
118
+ # cache pins even if result is zero, so we don't retry building pins
119
+ pins = rbs_map.pins || []
120
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
121
+ end
122
+ rescue Gem::MissingSpecError
123
+ warn "Gem '#{gem}' not found"
110
124
  end
111
125
 
112
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
126
+ desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
113
127
  long_desc %(
114
128
  Specify one or more gem names to clear. 'core' or 'stdlib' may
115
129
  also be specified to clear cached system documentation.
@@ -119,24 +133,19 @@ module Solargraph
119
133
  # @return [void]
120
134
  def uncache *gems
121
135
  raise ArgumentError, 'No gems specified.' if gems.empty?
122
- workspace = Solargraph::Workspace.new(Dir.pwd)
123
-
124
136
  gems.each do |gem|
125
137
  if gem == 'core'
126
- PinCache.uncache_core(out: $stdout)
138
+ PinCache.uncache_core
127
139
  next
128
140
  end
129
141
 
130
142
  if gem == 'stdlib'
131
- PinCache.uncache_stdlib(out: $stdout)
143
+ PinCache.uncache_stdlib
132
144
  next
133
145
  end
134
146
 
135
- spec = workspace.find_gem(gem)
136
- raise Thor::InvocationError, "Gem '#{gem}' not found" if spec.nil?
137
-
138
- # @sg-ignore flow sensitive typing needs to handle 'raise if'
139
- workspace.uncache_gem(spec, out: $stdout)
147
+ spec = Gem::Specification.find_by_name(gem)
148
+ PinCache.uncache_gem(spec, out: $stdout)
140
149
  end
141
150
  end
142
151
 
@@ -172,11 +181,13 @@ module Solargraph
172
181
  workspace = Solargraph::Workspace.new('.')
173
182
 
174
183
  if names.empty?
175
- workspace.cache_all_for_workspace!($stdout, rebuild: options[:rebuild])
184
+ Gem::Specification.to_a.each { |spec| do_cache spec, rebuild: options[:rebuild] }
185
+ $stderr.puts "Documentation cached for all #{Gem::Specification.count} gems."
176
186
  else
177
- $stderr.puts("Caching these gems: #{names}")
187
+ warn("Caching these gems: #{names}")
178
188
  names.each do |name|
179
189
  if name == 'core'
190
+ # @sg-ignore cache_core and core? are dynamically defined
180
191
  PinCache.cache_core(out: $stdout) if !PinCache.core? || options[:rebuild]
181
192
  next
182
193
  end
@@ -185,7 +196,18 @@ module Solargraph
185
196
  if gemspec.nil?
186
197
  warn "Gem '#{name}' not found"
187
198
  else
188
- workspace.cache_gem(gemspec, rebuild: options[:rebuild], out: $stdout)
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
189
211
  end
190
212
  rescue Gem::MissingSpecError
191
213
  warn "Gem '#{name}' not found"
@@ -195,7 +217,7 @@ module Solargraph
195
217
  # @sg-ignore Need to add nil check here
196
218
  warn e.backtrace.join("\n")
197
219
  end
198
- $stderr.puts "Documentation cached for #{names.count} gems."
220
+ warn "Documentation cached for #{names.count} gems."
199
221
  end
200
222
  end
201
223
 
@@ -212,7 +234,7 @@ module Solargraph
212
234
 
213
235
  Type checking levels are normal, typed, strict, and strong.
214
236
  )
215
- 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'
216
238
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
217
239
  # @return [void]
218
240
  def typecheck *files
@@ -231,21 +253,28 @@ module Solargraph
231
253
  files.map! { |file| File.realpath(file) }
232
254
  end
233
255
  filecount = 0
234
- time = Benchmark.measure {
256
+ time = Benchmark.measure do
235
257
  files.each do |file|
236
- checker = TypeChecker.new(file, api_map: api_map, rules: rules, 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)
237
260
  problems = checker.problems
238
261
  next if problems.empty?
239
262
  problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
240
- 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")
241
266
  filecount += 1
242
267
  probcount += problems.length
243
268
  end
244
- }
269
+ end
245
270
  puts "Typecheck finished in #{time.real} seconds."
246
- 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}."
247
276
  # "
248
- exit 1 if probcount > 0
277
+ exit 1 if probcount.positive?
249
278
  end
250
279
 
251
280
  desc 'scan', 'Test the workspace for problems'
@@ -262,26 +291,26 @@ module Solargraph
262
291
  directory = File.realpath(options[:directory])
263
292
  # @type [Solargraph::ApiMap, nil]
264
293
  api_map = nil
265
- time = Benchmark.measure {
294
+ time = Benchmark.measure do
266
295
  api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
267
296
  # @sg-ignore flow sensitive typing should be able to handle redefinition
268
297
  api_map.pins.each do |pin|
269
- begin
270
- puts pin_description(pin) if options[:verbose]
271
- pin.typify api_map
272
- pin.probe api_map
273
- rescue StandardError => e
274
- # @todo to add nil check here
275
- # @todo should warn on nil dereference below
276
- STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
277
- STDERR.puts "[#{e.class}]: #{e.message}"
278
- # @todo Need to add nil check here
279
- # @todo flow sensitive typing should be able to handle redefinition
280
- STDERR.puts e.backtrace.join("\n")
281
- exit 1
282
- 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
283
312
  end
284
- }
313
+ end
285
314
  # @sg-ignore Need to add nil check here
286
315
  puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
287
316
  end
@@ -296,12 +325,15 @@ module Solargraph
296
325
  puts "#{workspace.filenames.length} files total."
297
326
  end
298
327
 
299
- desc 'pin [PATH]', 'Describe a pin', hide: true
328
+ desc 'pin [PATH]', 'Describe a pin'
300
329
  option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false
301
- 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
302
332
  option :references, type: :boolean, desc: 'Show references', default: false
303
- option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', default: false
304
- 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
305
337
  # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
306
338
  # @return [void]
307
339
  def pin path
@@ -326,7 +358,7 @@ module Solargraph
326
358
  pin = pins.first
327
359
  case pin
328
360
  when nil
329
- $stderr.puts "Pin not found for path '#{path}'"
361
+ warn "Pin not found for path '#{path}'"
330
362
  exit 1
331
363
  when Pin::Namespace
332
364
  if options[:references]
@@ -354,21 +386,125 @@ module Solargraph
354
386
  end
355
387
  end
356
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
+
357
493
  private
358
494
 
359
495
  # @param pin [Solargraph::Pin::Base]
360
496
  # @return [String]
361
497
  def pin_description pin
362
498
  desc = if pin.path.nil? || pin.path.empty?
363
- if pin.closure
364
- # @sg-ignore Need to add nil check here
365
- "#{pin.closure.path} | #{pin.name}"
366
- else
367
- "#{pin.context.namespace} | #{pin.name}"
368
- end
369
- else
370
- pin.path
371
- 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
372
508
  # @sg-ignore Need to add nil check here
373
509
  desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
374
510
  desc
@@ -376,7 +512,7 @@ module Solargraph
376
512
 
377
513
  # @param type [ComplexType, ComplexType::UniqueType]
378
514
  # @return [void]
379
- def print_type(type)
515
+ def print_type type
380
516
  if options[:rbs]
381
517
  puts type.to_rbs
382
518
  else
@@ -386,12 +522,34 @@ module Solargraph
386
522
 
387
523
  # @param pin [Solargraph::Pin::Base]
388
524
  # @return [void]
389
- def print_pin(pin)
525
+ def print_pin pin
390
526
  if options[:rbs]
391
527
  puts pin.to_rbs
392
528
  else
393
529
  puts pin.inspect
394
530
  end
395
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
396
554
  end
397
555
  end
@@ -1,37 +1,39 @@
1
- module Solargraph
2
- class Source
3
- class Chain
4
- class Array < Literal
5
- # @param children [::Array<Chain>]
6
- # @param node [Parser::AST::Node]
7
- def initialize children, node
8
- super('::Array', node)
9
- @children = children
10
- end
11
-
12
- def word
13
- @word ||= "<#{@type}>"
14
- end
15
-
16
- # @param api_map [ApiMap]
17
- # @param name_pin [Pin::Base]
18
- # @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
19
- 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
32
- [Pin::ProxyType.anonymous(type, source: :chain)]
33
- end
34
- end
35
- end
36
- end
37
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ class Array < Literal
7
+ # @param children [::Array<Chain>]
8
+ # @param node [Parser::AST::Node]
9
+ def initialize children, node
10
+ super('::Array', node)
11
+ @children = children
12
+ end
13
+
14
+ def word
15
+ @word ||= "<#{@type}>"
16
+ end
17
+
18
+ # @param api_map [ApiMap]
19
+ # @param name_pin [Pin::Base]
20
+ # @param locals [::Array<Pin::Parameter, Pin::LocalVariable>]
21
+ def resolve api_map, name_pin, locals
22
+ child_types = @children.map do |child|
23
+ child.infer(api_map, name_pin, locals).simplify_literals
24
+ end
25
+ type = if child_types.empty? || child_types.any?(&:undefined?)
26
+ ComplexType::UniqueType.new('Array', rooted: true)
27
+ elsif child_types.uniq.length == 1 && child_types.first.defined?
28
+ ComplexType::UniqueType.new('Array', [], child_types.uniq, rooted: true, parameters_type: :list)
29
+ elsif child_types.empty?
30
+ ComplexType::UniqueType.new('Array', rooted: true, parameters_type: :list)
31
+ else
32
+ ComplexType::UniqueType.new('Array', [], child_types, rooted: true, parameters_type: :fixed)
33
+ end
34
+ [Pin::ProxyType.anonymous(type, source: :chain)]
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end