solargraph 0.58.0 → 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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.gitattributes +2 -0
  4. data/.github/workflows/linting.yml +6 -5
  5. data/.github/workflows/plugins.yml +48 -35
  6. data/.github/workflows/rspec.yml +15 -28
  7. data/.github/workflows/typecheck.yml +3 -2
  8. data/.gitignore +1 -0
  9. data/.rubocop.yml +38 -6
  10. data/.rubocop_todo.yml +53 -966
  11. data/CHANGELOG.md +24 -0
  12. data/Gemfile +3 -1
  13. data/README.md +3 -3
  14. data/Rakefile +26 -23
  15. data/bin/solargraph +9 -8
  16. data/lib/solargraph/api_map/cache.rb +110 -110
  17. data/lib/solargraph/api_map/constants.rb +288 -279
  18. data/lib/solargraph/api_map/index.rb +204 -193
  19. data/lib/solargraph/api_map/source_to_yard.rb +110 -97
  20. data/lib/solargraph/api_map/store.rb +395 -384
  21. data/lib/solargraph/api_map.rb +1029 -945
  22. data/lib/solargraph/bench.rb +44 -45
  23. data/lib/solargraph/complex_type/conformance.rb +176 -0
  24. data/lib/solargraph/complex_type/type_methods.rb +240 -228
  25. data/lib/solargraph/complex_type/unique_type.rb +641 -482
  26. data/lib/solargraph/complex_type.rb +557 -444
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  29. data/lib/solargraph/convention/data_definition.rb +108 -105
  30. data/lib/solargraph/convention/gemfile.rb +15 -15
  31. data/lib/solargraph/convention/gemspec.rb +23 -23
  32. data/lib/solargraph/convention/rakefile.rb +17 -17
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  35. data/lib/solargraph/convention/struct_definition.rb +168 -164
  36. data/lib/solargraph/convention.rb +78 -78
  37. data/lib/solargraph/converters/dd.rb +19 -17
  38. data/lib/solargraph/converters/dl.rb +17 -15
  39. data/lib/solargraph/converters/dt.rb +17 -15
  40. data/lib/solargraph/converters/misc.rb +3 -1
  41. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  42. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  44. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  45. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  46. data/lib/solargraph/doc_map.rb +438 -439
  47. data/lib/solargraph/equality.rb +34 -34
  48. data/lib/solargraph/gem_pins.rb +98 -98
  49. data/lib/solargraph/language_server/error_codes.rb +20 -20
  50. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  51. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  52. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  53. data/lib/solargraph/language_server/host/sources.rb +100 -99
  54. data/lib/solargraph/language_server/host.rb +886 -878
  55. data/lib/solargraph/language_server/message/base.rb +97 -97
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  57. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  58. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +108 -114
  59. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  60. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  61. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  64. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  65. data/lib/solargraph/language_server/message/text_document/definition.rb +49 -40
  66. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  67. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +36 -26
  68. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  69. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  70. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  71. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  72. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  73. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  74. data/lib/solargraph/language_server/message/text_document/type_definition.rb +33 -25
  75. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  76. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  77. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  78. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +33 -23
  79. data/lib/solargraph/language_server/message.rb +94 -94
  80. data/lib/solargraph/language_server/request.rb +29 -27
  81. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  82. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  83. data/lib/solargraph/library.rb +702 -683
  84. data/lib/solargraph/location.rb +85 -82
  85. data/lib/solargraph/logging.rb +55 -37
  86. data/lib/solargraph/page.rb +92 -92
  87. data/lib/solargraph/parser/comment_ripper.rb +84 -69
  88. data/lib/solargraph/parser/flow_sensitive_typing.rb +471 -255
  89. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  90. data/lib/solargraph/parser/node_processor.rb +63 -62
  91. data/lib/solargraph/parser/parser_gem/class_methods.rb +165 -149
  92. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  93. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  94. data/lib/solargraph/parser/parser_gem/node_methods.rb +507 -486
  95. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  96. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  97. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  98. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +45 -46
  99. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  100. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  101. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  102. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +42 -40
  103. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  104. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  105. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  106. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  108. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  109. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +51 -52
  110. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +302 -291
  111. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  112. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  113. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  114. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  115. data/lib/solargraph/parser/parser_gem.rb +14 -12
  116. data/lib/solargraph/parser/region.rb +75 -69
  117. data/lib/solargraph/parser/snippet.rb +19 -17
  118. data/lib/solargraph/parser.rb +25 -23
  119. data/lib/solargraph/pin/base.rb +773 -729
  120. data/lib/solargraph/pin/base_variable.rb +375 -126
  121. data/lib/solargraph/pin/block.rb +127 -104
  122. data/lib/solargraph/pin/breakable.rb +15 -9
  123. data/lib/solargraph/pin/callable.rb +275 -231
  124. data/lib/solargraph/pin/closure.rb +69 -72
  125. data/lib/solargraph/pin/common.rb +97 -79
  126. data/lib/solargraph/pin/compound_statement.rb +55 -0
  127. data/lib/solargraph/pin/constant.rb +43 -45
  128. data/lib/solargraph/pin/conversions.rb +129 -123
  129. data/lib/solargraph/pin/delegated_method.rb +131 -120
  130. data/lib/solargraph/pin/documenting.rb +116 -114
  131. data/lib/solargraph/pin/instance_variable.rb +38 -34
  132. data/lib/solargraph/pin/keyword.rb +16 -20
  133. data/lib/solargraph/pin/local_variable.rb +31 -75
  134. data/lib/solargraph/pin/method.rb +724 -672
  135. data/lib/solargraph/pin/method_alias.rb +42 -34
  136. data/lib/solargraph/pin/namespace.rb +122 -115
  137. data/lib/solargraph/pin/parameter.rb +339 -275
  138. data/lib/solargraph/pin/proxy_type.rb +42 -39
  139. data/lib/solargraph/pin/reference/override.rb +47 -47
  140. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  141. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  142. data/lib/solargraph/pin/reference.rb +59 -39
  143. data/lib/solargraph/pin/search.rb +62 -61
  144. data/lib/solargraph/pin/signature.rb +64 -61
  145. data/lib/solargraph/pin/symbol.rb +54 -53
  146. data/lib/solargraph/pin/until.rb +16 -18
  147. data/lib/solargraph/pin/while.rb +16 -18
  148. data/lib/solargraph/pin.rb +46 -44
  149. data/lib/solargraph/pin_cache.rb +248 -245
  150. data/lib/solargraph/position.rb +139 -119
  151. data/lib/solargraph/range.rb +113 -112
  152. data/lib/solargraph/rbs_map/conversions.rb +952 -823
  153. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  154. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  155. data/lib/solargraph/rbs_map/stdlib_map.rb +71 -43
  156. data/lib/solargraph/rbs_map.rb +207 -163
  157. data/lib/solargraph/server_methods.rb +16 -16
  158. data/lib/solargraph/shell.rb +555 -352
  159. data/lib/solargraph/source/chain/array.rb +39 -37
  160. data/lib/solargraph/source/chain/call.rb +377 -337
  161. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/constant.rb +30 -26
  163. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  164. data/lib/solargraph/source/chain/hash.rb +37 -34
  165. data/lib/solargraph/source/chain/if.rb +30 -28
  166. data/lib/solargraph/source/chain/instance_variable.rb +36 -13
  167. data/lib/solargraph/source/chain/link.rb +99 -109
  168. data/lib/solargraph/source/chain/literal.rb +51 -48
  169. data/lib/solargraph/source/chain/or.rb +29 -23
  170. data/lib/solargraph/source/chain/q_call.rb +13 -11
  171. data/lib/solargraph/source/chain/variable.rb +15 -13
  172. data/lib/solargraph/source/chain/z_super.rb +28 -30
  173. data/lib/solargraph/source/chain.rb +304 -291
  174. data/lib/solargraph/source/change.rb +89 -82
  175. data/lib/solargraph/source/cursor.rb +172 -166
  176. data/lib/solargraph/source/encoding_fixes.rb +22 -23
  177. data/lib/solargraph/source/source_chainer.rb +218 -194
  178. data/lib/solargraph/source/updater.rb +59 -55
  179. data/lib/solargraph/source.rb +522 -498
  180. data/lib/solargraph/source_map/clip.rb +245 -226
  181. data/lib/solargraph/source_map/data.rb +37 -34
  182. data/lib/solargraph/source_map/mapper.rb +286 -259
  183. data/lib/solargraph/source_map.rb +224 -212
  184. data/lib/solargraph/type_checker/problem.rb +34 -32
  185. data/lib/solargraph/type_checker/rules.rb +157 -84
  186. data/lib/solargraph/type_checker.rb +888 -814
  187. data/lib/solargraph/version.rb +5 -5
  188. data/lib/solargraph/workspace/config.rb +258 -255
  189. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  190. data/lib/solargraph/workspace/require_paths.rb +98 -97
  191. data/lib/solargraph/workspace.rb +242 -220
  192. data/lib/solargraph/yard_map/helpers.rb +49 -44
  193. data/lib/solargraph/yard_map/mapper/to_method.rb +136 -130
  194. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  195. data/lib/solargraph/yard_map/mapper.rb +84 -79
  196. data/lib/solargraph/yard_map.rb +17 -18
  197. data/lib/solargraph/yard_tags.rb +20 -20
  198. data/lib/solargraph/yardoc.rb +90 -87
  199. data/lib/solargraph.rb +128 -105
  200. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  201. data/rbs/fills/tuple/tuple.rbs +28 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  204. data/rbs_collection.yaml +1 -1
  205. data/solargraph.gemspec +37 -35
  206. metadata +52 -51
  207. data/lib/solargraph/type_checker/checks.rb +0 -124
  208. data/lib/solargraph/type_checker/param_def.rb +0 -37
  209. data/lib/solargraph/yard_map/to_method.rb +0 -89
  210. data/sig/shims/ast/0/node.rbs +0 -5
  211. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  212. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  215. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  216. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  217. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  218. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  219. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,352 +1,555 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark'
4
- require 'thor'
5
- require 'yard'
6
-
7
- module Solargraph
8
- class Shell < Thor
9
- include Solargraph::ServerMethods
10
-
11
- # Tell Thor to ensure the process exits with status 1 if any error happens.
12
- def self.exit_on_failure?
13
- true
14
- end
15
-
16
- map %w[--version -v] => :version
17
-
18
- desc "--version, -v", "Print the version"
19
- # @return [void]
20
- def version
21
- puts Solargraph::VERSION
22
- end
23
-
24
- desc 'socket', 'Run a Solargraph socket server'
25
- option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1'
26
- option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658
27
- # @return [void]
28
- def socket
29
- require 'backport'
30
- port = options[:port]
31
- port = available_port if port.zero?
32
- Backport.run do
33
- Signal.trap("INT") do
34
- Backport.stop
35
- end
36
- Signal.trap("TERM") do
37
- Backport.stop
38
- end
39
- # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
40
- 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
- end
43
- end
44
-
45
- desc 'stdio', 'Run a Solargraph stdio server'
46
- # @return [void]
47
- def stdio
48
- require 'backport'
49
- Backport.run do
50
- Signal.trap("INT") do
51
- Backport.stop
52
- end
53
- Signal.trap("TERM") do
54
- Backport.stop
55
- end
56
- # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
57
- Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
58
- STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
59
- end
60
- end
61
-
62
- desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
63
- option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
64
- # @param directory [String]
65
- # @return [void]
66
- def config(directory = '.')
67
- matches = []
68
- if options[:extensions]
69
- Gem::Specification.each do |g|
70
- if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
71
- require g.name
72
- matches.push g.name
73
- end
74
- end
75
- end
76
- conf = Solargraph::Workspace::Config.new.raw_data
77
- unless matches.empty?
78
- matches.each do |m|
79
- conf['extensions'].push m
80
- end
81
- end
82
- # @param file [File]
83
- File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
84
- file.puts conf.to_yaml
85
- end
86
- STDOUT.puts "Configuration file initialized."
87
- end
88
-
89
- desc 'clear', 'Delete all cached documentation'
90
- long_desc %(
91
- This command will delete all core and gem documentation from the cache.
92
- )
93
- # @return [void]
94
- def clear
95
- puts "Deleting all cached documentation (gems, core and stdlib)"
96
- Solargraph::PinCache.clear
97
- end
98
- map 'clear-cache' => :clear
99
- map 'clear-cores' => :clear
100
-
101
- desc 'cache', 'Cache a gem', hide: true
102
- option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
103
- # @return [void]
104
- # @param gem [String]
105
- # @param version [String, nil]
106
- def cache gem, version = nil
107
- api_map = Solargraph::ApiMap.load(Dir.pwd)
108
- spec = Gem::Specification.find_by_name(gem, version)
109
- api_map.cache_gem(spec, rebuild: options[:rebuild], out: $stdout)
110
- end
111
-
112
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
113
- long_desc %(
114
- Specify one or more gem names to clear. 'core' or 'stdlib' may
115
- also be specified to clear cached system documentation.
116
- Documentation will be regenerated as needed.
117
- )
118
- # @param gems [Array<String>]
119
- # @return [void]
120
- def uncache *gems
121
- raise ArgumentError, 'No gems specified.' if gems.empty?
122
- gems.each do |gem|
123
- if gem == 'core'
124
- PinCache.uncache_core
125
- next
126
- end
127
-
128
- if gem == 'stdlib'
129
- PinCache.uncache_stdlib
130
- next
131
- end
132
-
133
- spec = Gem::Specification.find_by_name(gem)
134
- PinCache.uncache_gem(spec, out: $stdout)
135
- end
136
- end
137
-
138
- desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
139
- option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
140
- # @param names [Array<String>]
141
- # @return [void]
142
- def gems *names
143
- api_map = ApiMap.load('.')
144
- if names.empty?
145
- Gem::Specification.to_a.each { |spec| do_cache spec, api_map }
146
- STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems."
147
- else
148
- names.each do |name|
149
- spec = Gem::Specification.find_by_name(*name.split('='))
150
- do_cache spec, api_map
151
- rescue Gem::MissingSpecError
152
- warn "Gem '#{name}' not found"
153
- end
154
- STDERR.puts "Documentation cached for #{names.count} gems."
155
- end
156
- end
157
-
158
- desc 'reporters', 'Get a list of diagnostics reporters'
159
- # @return [void]
160
- def reporters
161
- puts Solargraph::Diagnostics.reporters
162
- end
163
-
164
- desc 'typecheck [FILE(s)]', 'Run the type checker'
165
- long_desc %(
166
- Perform type checking on one or more files in a workspace. Check the
167
- entire workspace if no files are specified.
168
-
169
- Type checking levels are normal, typed, strict, and strong.
170
- )
171
- option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
172
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
173
- # @return [void]
174
- def typecheck *files
175
- directory = File.realpath(options[:directory])
176
- workspace = Solargraph::Workspace.new(directory)
177
- level = options[:level].to_sym
178
- rules = workspace.rules(level)
179
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
180
- probcount = 0
181
- if files.empty?
182
- files = api_map.source_maps.map(&:filename)
183
- else
184
- files.map! { |file| File.realpath(file) }
185
- end
186
- filecount = 0
187
-
188
- time = Benchmark.measure {
189
- files.each do |file|
190
- checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym, workspace: workspace)
191
- problems = checker.problems
192
- next if problems.empty?
193
- problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
194
- puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
195
- filecount += 1
196
- probcount += problems.length
197
- end
198
- # "
199
- }
200
- puts "Typecheck finished in #{time.real} seconds."
201
- puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
202
- # "
203
- exit 1 if probcount > 0
204
- end
205
-
206
- desc 'scan', 'Test the workspace for problems'
207
- long_desc %(
208
- A scan loads the entire workspace to make sure that the ASTs and
209
- maps do not raise errors during analysis. It does not perform any type
210
- checking or validation; it only confirms that the analysis itself is
211
- error-free.
212
- )
213
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
214
- option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
215
- # @return [void]
216
- def scan
217
- directory = File.realpath(options[:directory])
218
- # @type [Solargraph::ApiMap, nil]
219
- api_map = nil
220
- time = Benchmark.measure {
221
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
222
- api_map.pins.each do |pin|
223
- begin
224
- puts pin_description(pin) if options[:verbose]
225
- pin.typify api_map
226
- pin.probe api_map
227
- rescue StandardError => e
228
- STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
229
- STDERR.puts "[#{e.class}]: #{e.message}"
230
- STDERR.puts e.backtrace.join("\n")
231
- exit 1
232
- end
233
- end
234
- }
235
- puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
236
- end
237
-
238
- desc 'list', 'List the files in the workspace and the total count'
239
- option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
240
- option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
241
- # @return [void]
242
- def list
243
- workspace = Solargraph::Workspace.new(options[:directory])
244
- puts workspace.filenames unless options[:count]
245
- puts "#{workspace.filenames.length} files total."
246
- end
247
-
248
- desc 'pin [PATH]', 'Describe a pin', hide: true
249
- option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false
250
- option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations', default: false
251
- option :references, type: :boolean, desc: 'Show references', default: false
252
- option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', default: false
253
- option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses', default: false
254
- # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
255
- # @return [void]
256
- def pin path
257
- api_map = Solargraph::ApiMap.load_with_cache('.', $stderr)
258
- is_method = path.include?('#') || path.include?('.')
259
- if is_method && options[:stack]
260
- scope, ns, meth = if path.include? '#'
261
- [:instance, *path.split('#', 2)]
262
- else
263
- [:class, *path.split('.', 2)]
264
- end
265
-
266
- # @sg-ignore Wrong argument type for
267
- # Solargraph::ApiMap#get_method_stack: rooted_tag
268
- # expected String, received Array<String>
269
- pins = api_map.get_method_stack(ns, meth, scope: scope)
270
- else
271
- pins = api_map.get_path_pins path
272
- end
273
- # @type [Hash{Symbol => Pin::Base}]
274
- references = {}
275
- pin = pins.first
276
- case pin
277
- when nil
278
- $stderr.puts "Pin not found for path '#{path}'"
279
- exit 1
280
- when Pin::Namespace
281
- if options[:references]
282
- superclass_tag = api_map.qualify_superclass(pin.return_type.tag)
283
- superclass_pin = api_map.get_path_pins(superclass_tag).first if superclass_tag
284
- references[:superclass] = superclass_pin if superclass_pin
285
- end
286
- end
287
-
288
- pins.each do |pin|
289
- if options[:typify] || options[:probe]
290
- type = ComplexType::UNDEFINED
291
- type = pin.typify(api_map) if options[:typify]
292
- type = pin.probe(api_map) if options[:probe] && type.undefined?
293
- print_type(type)
294
- next
295
- end
296
-
297
- print_pin(pin)
298
- end
299
- references.each do |key, refpin|
300
- puts "\n# #{key.to_s.capitalize}:\n\n"
301
- print_pin(refpin)
302
- end
303
- end
304
-
305
- private
306
-
307
- # @param pin [Solargraph::Pin::Base]
308
- # @return [String]
309
- def pin_description pin
310
- desc = if pin.path.nil? || pin.path.empty?
311
- if pin.closure
312
- "#{pin.closure.path} | #{pin.name}"
313
- else
314
- "#{pin.context.namespace} | #{pin.name}"
315
- end
316
- else
317
- pin.path
318
- end
319
- desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
320
- desc
321
- end
322
-
323
- # @param gemspec [Gem::Specification]
324
- # @param api_map [ApiMap]
325
- # @return [void]
326
- def do_cache gemspec, api_map
327
- # @todo if the rebuild: option is passed as a positional arg,
328
- # typecheck doesn't complain on the below line
329
- api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout)
330
- end
331
-
332
- # @param type [ComplexType]
333
- # @return [void]
334
- def print_type(type)
335
- if options[:rbs]
336
- puts type.to_rbs
337
- else
338
- puts type.rooted_tag
339
- end
340
- end
341
-
342
- # @param pin [Solargraph::Pin::Base]
343
- # @return [void]
344
- def print_pin(pin)
345
- if options[:rbs]
346
- puts pin.to_rbs
347
- else
348
- puts pin.inspect
349
- end
350
- end
351
- end
352
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+ require 'thor'
5
+ require 'yard'
6
+ require 'yaml'
7
+
8
+ module Solargraph
9
+ class Shell < Thor
10
+ include Solargraph::ServerMethods
11
+
12
+ # Tell Thor to ensure the process exits with status 1 if any error happens.
13
+ def self.exit_on_failure?
14
+ true
15
+ end
16
+
17
+ map %w[--version -v] => :version
18
+
19
+ desc '--version, -v', 'Print the version'
20
+ # @return [void]
21
+ def version
22
+ puts Solargraph::VERSION
23
+ end
24
+
25
+ desc 'socket', 'Run a Solargraph socket server'
26
+ option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1'
27
+ option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658
28
+ # @return [void]
29
+ def socket
30
+ require 'backport'
31
+ port = options[:port]
32
+ port = available_port if port.zero?
33
+ Backport.run do
34
+ Signal.trap('INT') do
35
+ Backport.stop
36
+ end
37
+ Signal.trap('TERM') do
38
+ Backport.stop
39
+ end
40
+ # @sg-ignore Wrong argument type for Backport.prepare_tcp_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
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}"
43
+ end
44
+ end
45
+
46
+ desc 'stdio', 'Run a Solargraph stdio server'
47
+ # @return [void]
48
+ def stdio
49
+ require 'backport'
50
+ Backport.run do
51
+ Signal.trap('INT') do
52
+ Backport.stop
53
+ end
54
+ Signal.trap('TERM') do
55
+ Backport.stop
56
+ end
57
+ # @sg-ignore Wrong argument type for Backport.prepare_stdio_server: adapter expected Backport::Adapter, received Module<Solargraph::LanguageServer::Transport::Adapter>
58
+ Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
59
+ $stderr.puts "Solargraph is listening on stdio PID=#{Process.pid}"
60
+ end
61
+ end
62
+
63
+ desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
64
+ option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
65
+ # @param directory [String]
66
+ # @return [void]
67
+ def config directory = '.'
68
+ matches = []
69
+ if options[:extensions]
70
+ Gem::Specification.each do |g|
71
+ if g.name.match(/^solargraph-[A-Za-z0-9_-]*?-ext/)
72
+ require g.name
73
+ matches.push g.name
74
+ end
75
+ end
76
+ end
77
+ conf = Solargraph::Workspace::Config.new.raw_data
78
+ unless matches.empty?
79
+ matches.each do |m|
80
+ conf['extensions'].push m
81
+ end
82
+ end
83
+ # @param file [File]
84
+ File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
85
+ file.puts conf.to_yaml
86
+ end
87
+ $stdout.puts 'Configuration file initialized.'
88
+ end
89
+
90
+ desc 'clear', 'Delete all cached documentation'
91
+ long_desc %(
92
+ This command will delete all core and gem documentation from the cache.
93
+ )
94
+ # @return [void]
95
+ def clear
96
+ puts 'Deleting all cached documentation (gems, core and stdlib)'
97
+ Solargraph::PinCache.clear
98
+ end
99
+ map 'clear-cache' => :clear
100
+ map 'clear-cores' => :clear
101
+
102
+ desc 'cache', 'Cache a gem', hide: true
103
+ option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
104
+ # @return [void]
105
+ # @param gem [String]
106
+ # @param version [String, nil]
107
+ def cache gem, version = nil
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"
124
+ end
125
+
126
+ desc 'uncache GEM [...GEM]', 'Delete specific cached gem documentation'
127
+ long_desc %(
128
+ Specify one or more gem names to clear. 'core' or 'stdlib' may
129
+ also be specified to clear cached system documentation.
130
+ Documentation will be regenerated as needed.
131
+ )
132
+ # @param gems [Array<String>]
133
+ # @return [void]
134
+ def uncache *gems
135
+ raise ArgumentError, 'No gems specified.' if gems.empty?
136
+ gems.each do |gem|
137
+ if gem == 'core'
138
+ PinCache.uncache_core
139
+ next
140
+ end
141
+
142
+ if gem == 'stdlib'
143
+ PinCache.uncache_stdlib
144
+ next
145
+ end
146
+
147
+ spec = Gem::Specification.find_by_name(gem)
148
+ PinCache.uncache_gem(spec, out: $stdout)
149
+ end
150
+ end
151
+
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
+ )
176
+ option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
177
+ # @param names [Array<String>]
178
+ # @return [void]
179
+ def gems *names
180
+ # print time with ms
181
+ workspace = Solargraph::Workspace.new('.')
182
+
183
+ if names.empty?
184
+ Gem::Specification.to_a.each { |spec| do_cache spec, rebuild: options[:rebuild] }
185
+ $stderr.puts "Documentation cached for all #{Gem::Specification.count} gems."
186
+ else
187
+ warn("Caching these gems: #{names}")
188
+ names.each do |name|
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
212
+ rescue Gem::MissingSpecError
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")
219
+ end
220
+ warn "Documentation cached for #{names.count} gems."
221
+ end
222
+ end
223
+
224
+ desc 'reporters', 'Get a list of diagnostics reporters'
225
+ # @return [void]
226
+ def reporters
227
+ puts Solargraph::Diagnostics.reporters
228
+ end
229
+
230
+ desc 'typecheck [FILE(s)]', 'Run the type checker'
231
+ long_desc %(
232
+ Perform type checking on one or more files in a workspace. Check the
233
+ entire workspace if no files are specified.
234
+
235
+ Type checking levels are normal, typed, strict, and strong.
236
+ )
237
+ option :level, type: :string, aliases: %i[mode m l], desc: 'Type checking level', default: 'normal'
238
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
239
+ # @return [void]
240
+ def typecheck *files
241
+ directory = File.realpath(options[:directory])
242
+ workspace = Solargraph::Workspace.new(directory)
243
+ level = options[:level].to_sym
244
+ rules = workspace.rules(level)
245
+ api_map =
246
+ Solargraph::ApiMap.load_with_cache(directory, $stdout,
247
+ loose_unions:
248
+ !rules.require_all_unique_types_support_call?)
249
+ probcount = 0
250
+ if files.empty?
251
+ files = api_map.source_maps.map(&:filename)
252
+ else
253
+ files.map! { |file| File.realpath(file) }
254
+ end
255
+ filecount = 0
256
+ time = Benchmark.measure do
257
+ files.each do |file|
258
+ checker = TypeChecker.new(file, api_map: api_map, rules: rules, level: options[:level].to_sym,
259
+ workspace: workspace)
260
+ problems = checker.problems
261
+ next if problems.empty?
262
+ problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
263
+ puts problems.map { |prob|
264
+ "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}"
265
+ }.join("\n")
266
+ filecount += 1
267
+ probcount += problems.length
268
+ end
269
+ end
270
+ puts "Typecheck finished in #{time.real} seconds."
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}."
276
+ # "
277
+ exit 1 if probcount.positive?
278
+ end
279
+
280
+ desc 'scan', 'Test the workspace for problems'
281
+ long_desc %(
282
+ A scan loads the entire workspace to make sure that the ASTs and
283
+ maps do not raise errors during analysis. It does not perform any type
284
+ checking or validation; it only confirms that the analysis itself is
285
+ error-free.
286
+ )
287
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
288
+ option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
289
+ # @return [void]
290
+ def scan
291
+ directory = File.realpath(options[:directory])
292
+ # @type [Solargraph::ApiMap, nil]
293
+ api_map = nil
294
+ time = Benchmark.measure do
295
+ api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
296
+ # @sg-ignore flow sensitive typing should be able to handle redefinition
297
+ api_map.pins.each do |pin|
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
312
+ end
313
+ end
314
+ # @sg-ignore Need to add nil check here
315
+ puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
316
+ end
317
+
318
+ desc 'list', 'List the files in the workspace and the total count'
319
+ option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
320
+ option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
321
+ # @return [void]
322
+ def list
323
+ workspace = Solargraph::Workspace.new(options[:directory])
324
+ puts workspace.filenames unless options[:count]
325
+ puts "#{workspace.filenames.length} files total."
326
+ end
327
+
328
+ desc 'pin [PATH]', 'Describe a pin'
329
+ option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false
330
+ option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations',
331
+ default: false
332
+ option :references, type: :boolean, desc: 'Show references', 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
337
+ # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
338
+ # @return [void]
339
+ def pin path
340
+ api_map = Solargraph::ApiMap.load_with_cache('.', $stderr)
341
+ is_method = path.include?('#') || path.include?('.')
342
+ if is_method && options[:stack]
343
+ scope, ns, meth = if path.include? '#'
344
+ [:instance, *path.split('#', 2)]
345
+ else
346
+ [:class, *path.split('.', 2)]
347
+ end
348
+
349
+ # @sg-ignore Wrong argument type for
350
+ # Solargraph::ApiMap#get_method_stack: rooted_tag
351
+ # expected String, received Array<String>
352
+ pins = api_map.get_method_stack(ns, meth, scope: scope)
353
+ else
354
+ pins = api_map.get_path_pins path
355
+ end
356
+ # @type [Hash{Symbol => Pin::Base}]
357
+ references = {}
358
+ pin = pins.first
359
+ case pin
360
+ when nil
361
+ warn "Pin not found for path '#{path}'"
362
+ exit 1
363
+ when Pin::Namespace
364
+ if options[:references]
365
+ # @sg-ignore Need to add nil check here
366
+ superclass_tag = api_map.qualify_superclass(pin.return_type.tag)
367
+ superclass_pin = api_map.get_path_pins(superclass_tag).first if superclass_tag
368
+ references[:superclass] = superclass_pin if superclass_pin
369
+ end
370
+ end
371
+
372
+ pins.each do |pin|
373
+ if options[:typify] || options[:probe]
374
+ type = ComplexType::UNDEFINED
375
+ type = pin.typify(api_map) if options[:typify]
376
+ type = pin.probe(api_map) if options[:probe] && type.undefined?
377
+ print_type(type)
378
+ next
379
+ end
380
+
381
+ print_pin(pin)
382
+ end
383
+ references.each do |key, refpin|
384
+ puts "\n# #{key.to_s.capitalize}:\n\n"
385
+ print_pin(refpin)
386
+ end
387
+ end
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
+
493
+ private
494
+
495
+ # @param pin [Solargraph::Pin::Base]
496
+ # @return [String]
497
+ def pin_description pin
498
+ desc = if pin.path.nil? || pin.path.empty?
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
509
+ desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
510
+ desc
511
+ end
512
+
513
+ # @param type [ComplexType, ComplexType::UniqueType]
514
+ # @return [void]
515
+ def print_type type
516
+ if options[:rbs]
517
+ puts type.to_rbs
518
+ else
519
+ puts type.rooted_tag
520
+ end
521
+ end
522
+
523
+ # @param pin [Solargraph::Pin::Base]
524
+ # @return [void]
525
+ def print_pin pin
526
+ if options[:rbs]
527
+ puts pin.to_rbs
528
+ else
529
+ puts pin.inspect
530
+ end
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
554
+ end
555
+ end