solargraph 0.56.0 → 0.58.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/linting.yml +127 -0
  4. data/.github/workflows/plugins.yml +183 -7
  5. data/.github/workflows/rspec.yml +55 -5
  6. data/.github/workflows/typecheck.yml +6 -3
  7. data/.gitignore +6 -0
  8. data/.overcommit.yml +72 -0
  9. data/.rspec +1 -0
  10. data/.rubocop.yml +66 -0
  11. data/.rubocop_todo.yml +1279 -0
  12. data/.yardopts +1 -0
  13. data/CHANGELOG.md +92 -1
  14. data/README.md +8 -4
  15. data/Rakefile +125 -13
  16. data/bin/solargraph +3 -0
  17. data/lib/solargraph/api_map/cache.rb +110 -109
  18. data/lib/solargraph/api_map/constants.rb +279 -0
  19. data/lib/solargraph/api_map/index.rb +193 -175
  20. data/lib/solargraph/api_map/source_to_yard.rb +97 -88
  21. data/lib/solargraph/api_map/store.rb +384 -266
  22. data/lib/solargraph/api_map.rb +945 -973
  23. data/lib/solargraph/bench.rb +1 -0
  24. data/lib/solargraph/complex_type/type_methods.rb +228 -222
  25. data/lib/solargraph/complex_type/unique_type.rb +482 -475
  26. data/lib/solargraph/complex_type.rb +444 -423
  27. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  28. data/lib/solargraph/convention/base.rb +17 -0
  29. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  30. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  31. data/lib/solargraph/convention/data_definition.rb +105 -0
  32. data/lib/solargraph/convention/gemspec.rb +3 -2
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -60
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -100
  35. data/lib/solargraph/convention/struct_definition.rb +164 -101
  36. data/lib/solargraph/convention.rb +32 -2
  37. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  38. data/lib/solargraph/diagnostics/rubocop.rb +118 -113
  39. data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -66
  40. data/lib/solargraph/diagnostics/type_check.rb +55 -55
  41. data/lib/solargraph/doc_map.rb +439 -405
  42. data/lib/solargraph/environ.rb +9 -2
  43. data/lib/solargraph/equality.rb +34 -33
  44. data/lib/solargraph/gem_pins.rb +98 -88
  45. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  46. data/lib/solargraph/language_server/host/dispatch.rb +130 -128
  47. data/lib/solargraph/language_server/host/message_worker.rb +112 -109
  48. data/lib/solargraph/language_server/host/sources.rb +99 -99
  49. data/lib/solargraph/language_server/host.rb +878 -871
  50. data/lib/solargraph/language_server/message/base.rb +2 -1
  51. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -114
  52. data/lib/solargraph/language_server/message/extended/document.rb +23 -23
  53. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
  54. data/lib/solargraph/language_server/message/text_document/definition.rb +40 -38
  55. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
  56. data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -131
  57. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  58. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
  59. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -24
  60. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  61. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  62. data/lib/solargraph/language_server/progress.rb +8 -0
  63. data/lib/solargraph/language_server/request.rb +4 -1
  64. data/lib/solargraph/library.rb +683 -666
  65. data/lib/solargraph/location.rb +82 -79
  66. data/lib/solargraph/logging.rb +37 -28
  67. data/lib/solargraph/page.rb +3 -0
  68. data/lib/solargraph/parser/comment_ripper.rb +69 -62
  69. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -227
  70. data/lib/solargraph/parser/node_processor/base.rb +92 -87
  71. data/lib/solargraph/parser/node_processor.rb +62 -46
  72. data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -159
  73. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  74. data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -164
  75. data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -497
  76. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -21
  77. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -59
  78. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
  79. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -45
  80. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +1 -21
  81. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -53
  82. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -21
  83. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -40
  84. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -29
  85. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -53
  86. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +0 -22
  87. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -41
  88. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -16
  89. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -37
  90. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -43
  91. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -271
  92. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +1 -0
  93. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -29
  94. data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -66
  95. data/lib/solargraph/parser/region.rb +69 -66
  96. data/lib/solargraph/parser/snippet.rb +17 -15
  97. data/lib/solargraph/pin/base.rb +729 -651
  98. data/lib/solargraph/pin/base_variable.rb +126 -125
  99. data/lib/solargraph/pin/block.rb +104 -103
  100. data/lib/solargraph/pin/breakable.rb +9 -9
  101. data/lib/solargraph/pin/callable.rb +231 -218
  102. data/lib/solargraph/pin/closure.rb +72 -74
  103. data/lib/solargraph/pin/common.rb +79 -75
  104. data/lib/solargraph/pin/constant.rb +2 -0
  105. data/lib/solargraph/pin/conversions.rb +123 -123
  106. data/lib/solargraph/pin/delegated_method.rb +120 -120
  107. data/lib/solargraph/pin/documenting.rb +114 -114
  108. data/lib/solargraph/pin/instance_variable.rb +34 -34
  109. data/lib/solargraph/pin/keyword.rb +20 -20
  110. data/lib/solargraph/pin/local_variable.rb +75 -76
  111. data/lib/solargraph/pin/method.rb +672 -651
  112. data/lib/solargraph/pin/method_alias.rb +34 -31
  113. data/lib/solargraph/pin/namespace.rb +115 -115
  114. data/lib/solargraph/pin/parameter.rb +275 -261
  115. data/lib/solargraph/pin/proxy_type.rb +39 -35
  116. data/lib/solargraph/pin/reference/override.rb +47 -33
  117. data/lib/solargraph/pin/reference/superclass.rb +15 -10
  118. data/lib/solargraph/pin/reference.rb +39 -22
  119. data/lib/solargraph/pin/search.rb +61 -56
  120. data/lib/solargraph/pin/signature.rb +61 -59
  121. data/lib/solargraph/pin/symbol.rb +53 -48
  122. data/lib/solargraph/pin/until.rb +18 -18
  123. data/lib/solargraph/pin/while.rb +18 -18
  124. data/lib/solargraph/pin.rb +44 -44
  125. data/lib/solargraph/pin_cache.rb +245 -185
  126. data/lib/solargraph/position.rb +132 -116
  127. data/lib/solargraph/range.rb +112 -107
  128. data/lib/solargraph/rbs_map/conversions.rb +823 -773
  129. data/lib/solargraph/rbs_map/core_fills.rb +18 -0
  130. data/lib/solargraph/rbs_map/core_map.rb +58 -51
  131. data/lib/solargraph/rbs_map/stdlib_map.rb +43 -43
  132. data/lib/solargraph/rbs_map.rb +163 -150
  133. data/lib/solargraph/shell.rb +352 -268
  134. data/lib/solargraph/source/chain/call.rb +337 -333
  135. data/lib/solargraph/source/chain/constant.rb +26 -89
  136. data/lib/solargraph/source/chain/hash.rb +34 -34
  137. data/lib/solargraph/source/chain/if.rb +28 -28
  138. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  139. data/lib/solargraph/source/chain/link.rb +11 -2
  140. data/lib/solargraph/source/chain/literal.rb +48 -48
  141. data/lib/solargraph/source/chain/or.rb +23 -23
  142. data/lib/solargraph/source/chain.rb +291 -282
  143. data/lib/solargraph/source/change.rb +82 -82
  144. data/lib/solargraph/source/cursor.rb +166 -167
  145. data/lib/solargraph/source/encoding_fixes.rb +23 -23
  146. data/lib/solargraph/source/source_chainer.rb +194 -194
  147. data/lib/solargraph/source/updater.rb +55 -55
  148. data/lib/solargraph/source.rb +498 -495
  149. data/lib/solargraph/source_map/clip.rb +226 -234
  150. data/lib/solargraph/source_map/data.rb +34 -30
  151. data/lib/solargraph/source_map/mapper.rb +259 -259
  152. data/lib/solargraph/source_map.rb +212 -200
  153. data/lib/solargraph/type_checker/checks.rb +124 -124
  154. data/lib/solargraph/type_checker/param_def.rb +37 -35
  155. data/lib/solargraph/type_checker/problem.rb +32 -32
  156. data/lib/solargraph/type_checker/rules.rb +84 -62
  157. data/lib/solargraph/type_checker.rb +814 -699
  158. data/lib/solargraph/version.rb +5 -5
  159. data/lib/solargraph/workspace/config.rb +255 -239
  160. data/lib/solargraph/workspace/require_paths.rb +97 -0
  161. data/lib/solargraph/workspace.rb +220 -249
  162. data/lib/solargraph/yard_map/helpers.rb +44 -16
  163. data/lib/solargraph/yard_map/mapper/to_constant.rb +5 -5
  164. data/lib/solargraph/yard_map/mapper/to_method.rb +130 -134
  165. data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -30
  166. data/lib/solargraph/yard_map/mapper.rb +79 -79
  167. data/lib/solargraph/yard_map/to_method.rb +89 -88
  168. data/lib/solargraph/yardoc.rb +87 -49
  169. data/lib/solargraph.rb +105 -90
  170. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  171. data/rbs/fills/open3/0/open3.rbs +172 -0
  172. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  173. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  174. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  175. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  176. data/rbs/fills/{tuple.rbs → tuple/tuple.rbs} +2 -3
  177. data/rbs/shims/ast/0/node.rbs +5 -0
  178. data/rbs/shims/ast/2.4/.rbs_meta.yaml +9 -0
  179. data/rbs/shims/ast/2.4/ast.rbs +73 -0
  180. data/rbs/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  181. data/rbs/shims/parser/3.2.0.1/manifest.yaml +7 -0
  182. data/rbs/shims/parser/3.2.0.1/parser.rbs +201 -0
  183. data/rbs/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  184. data/rbs/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  185. data/rbs/shims/thor/1.2.0.1/manifest.yaml +7 -0
  186. data/rbs/shims/thor/1.2.0.1/thor.rbs +17 -0
  187. data/rbs_collection.yaml +4 -4
  188. data/solargraph.gemspec +26 -5
  189. metadata +187 -15
  190. data/lib/.rubocop.yml +0 -22
  191. data/lib/solargraph/parser/node_methods.rb +0 -97
@@ -1,268 +1,352 @@
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
- Backport.prepare_tcp_server host: options[:host], port: port, adapter: Solargraph::LanguageServer::Transport::Adapter
40
- STDERR.puts "Solargraph is listening PORT=#{port} PID=#{Process.pid}"
41
- end
42
- end
43
-
44
- desc 'stdio', 'Run a Solargraph stdio server'
45
- # @return [void]
46
- def stdio
47
- require 'backport'
48
- Backport.run do
49
- Signal.trap("INT") do
50
- Backport.stop
51
- end
52
- Signal.trap("TERM") do
53
- Backport.stop
54
- end
55
- Backport.prepare_stdio_server adapter: Solargraph::LanguageServer::Transport::Adapter
56
- STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
57
- end
58
- end
59
-
60
- desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
61
- option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
62
- # @param directory [String]
63
- # @return [void]
64
- def config(directory = '.')
65
- matches = []
66
- if options[:extensions]
67
- Gem::Specification.each do |g|
68
- if g.name.match(/^solargraph\-[A-Za-z0-9_\-]*?\-ext/)
69
- require g.name
70
- matches.push g.name
71
- end
72
- end
73
- end
74
- conf = Solargraph::Workspace::Config.new.raw_data
75
- unless matches.empty?
76
- matches.each do |m|
77
- conf['extensions'].push m
78
- end
79
- end
80
- File.open(File.join(directory, '.solargraph.yml'), 'w') do |file|
81
- file.puts conf.to_yaml
82
- end
83
- STDOUT.puts "Configuration file initialized."
84
- end
85
-
86
- desc 'clear', 'Delete all cached documentation'
87
- long_desc %(
88
- This command will delete all core and gem documentation from the cache.
89
- )
90
- # @return [void]
91
- def clear
92
- puts "Deleting all cached documentation (gems, core and stdlib)"
93
- Solargraph::PinCache.clear
94
- end
95
- map 'clear-cache' => :clear
96
- map 'clear-cores' => :clear
97
-
98
- desc 'cache', 'Cache a gem', hide: true
99
- option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
100
- # @return [void]
101
- # @param gem [String]
102
- # @param version [String, nil]
103
- def cache gem, version = nil
104
- api_map = Solargraph::ApiMap.load(Dir.pwd)
105
- spec = Gem::Specification.find_by_name(gem, version)
106
- api_map.cache_gem(spec, rebuild: options[:rebuild], out: $stdout)
107
- end
108
-
109
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
110
- long_desc %(
111
- Specify one or more gem names to clear. 'core' or 'stdlib' may
112
- also be specified to clear cached system documentation.
113
- Documentation will be regenerated as needed.
114
- )
115
- # @param gems [Array<String>]
116
- # @return [void]
117
- def uncache *gems
118
- raise ArgumentError, 'No gems specified.' if gems.empty?
119
- gems.each do |gem|
120
- if gem == 'core'
121
- PinCache.uncache_core
122
- next
123
- end
124
-
125
- if gem == 'stdlib'
126
- PinCache.uncache_stdlib
127
- next
128
- end
129
-
130
- spec = Gem::Specification.find_by_name(gem)
131
- PinCache.uncache_gem(spec, out: $stdout)
132
- end
133
- end
134
-
135
- desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
136
- option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
137
- # @param names [Array<String>]
138
- # @return [void]
139
- def gems *names
140
- api_map = ApiMap.load('.')
141
- if names.empty?
142
- Gem::Specification.to_a.each { |spec| do_cache spec, api_map }
143
- STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems."
144
- else
145
- names.each do |name|
146
- spec = Gem::Specification.find_by_name(*name.split('='))
147
- do_cache spec, api_map
148
- rescue Gem::MissingSpecError
149
- warn "Gem '#{name}' not found"
150
- end
151
- STDERR.puts "Documentation cached for #{names.count} gems."
152
- end
153
- end
154
-
155
- desc 'reporters', 'Get a list of diagnostics reporters'
156
- # @return [void]
157
- def reporters
158
- puts Solargraph::Diagnostics.reporters
159
- end
160
-
161
- desc 'typecheck [FILE(s)]', 'Run the type checker'
162
- long_desc %(
163
- Perform type checking on one or more files in a workspace. Check the
164
- entire workspace if no files are specified.
165
-
166
- Type checking levels are normal, typed, strict, and strong.
167
- )
168
- option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
169
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
170
- # @return [void]
171
- def typecheck *files
172
- directory = File.realpath(options[:directory])
173
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
174
- probcount = 0
175
- if files.empty?
176
- files = api_map.source_maps.map(&:filename)
177
- else
178
- files.map! { |file| File.realpath(file) }
179
- end
180
- filecount = 0
181
-
182
- time = Benchmark.measure {
183
- files.each do |file|
184
- checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
185
- problems = checker.problems
186
- next if problems.empty?
187
- problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
188
- puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
189
- filecount += 1
190
- probcount += problems.length
191
- end
192
- # "
193
- }
194
- puts "Typecheck finished in #{time.real} seconds."
195
- puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
196
- # "
197
- exit 1 if probcount > 0
198
- end
199
-
200
- desc 'scan', 'Test the workspace for problems'
201
- long_desc %(
202
- A scan loads the entire workspace to make sure that the ASTs and
203
- maps do not raise errors during analysis. It does not perform any type
204
- checking or validation; it only confirms that the analysis itself is
205
- error-free.
206
- )
207
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
208
- option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
209
- # @return [void]
210
- def scan
211
- directory = File.realpath(options[:directory])
212
- # @type [Solargraph::ApiMap, nil]
213
- api_map = nil
214
- time = Benchmark.measure {
215
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
216
- api_map.pins.each do |pin|
217
- begin
218
- puts pin_description(pin) if options[:verbose]
219
- pin.typify api_map
220
- pin.probe api_map
221
- rescue StandardError => e
222
- STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
223
- STDERR.puts "[#{e.class}]: #{e.message}"
224
- STDERR.puts e.backtrace.join("\n")
225
- exit 1
226
- end
227
- end
228
- }
229
- puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
230
- end
231
-
232
- desc 'list', 'List the files in the workspace and the total count'
233
- option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
234
- option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
235
- # @return [void]
236
- def list
237
- workspace = Solargraph::Workspace.new(options[:directory])
238
- puts workspace.filenames unless options[:count]
239
- puts "#{workspace.filenames.length} files total."
240
- end
241
-
242
- private
243
-
244
- # @param pin [Solargraph::Pin::Base]
245
- # @return [String]
246
- def pin_description pin
247
- desc = if pin.path.nil? || pin.path.empty?
248
- if pin.closure
249
- "#{pin.closure.path} | #{pin.name}"
250
- else
251
- "#{pin.context.namespace} | #{pin.name}"
252
- end
253
- else
254
- pin.path
255
- end
256
- desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
257
- desc
258
- end
259
-
260
- # @param gemspec [Gem::Specification]
261
- # @return [void]
262
- def do_cache gemspec, api_map
263
- # @todo if the rebuild: option is passed as a positional arg,
264
- # typecheck doesn't complain on the below line
265
- api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout)
266
- end
267
- end
268
- end
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