solargraph 0.54.4 → 0.58.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/linting.yml +127 -0
  4. data/.github/workflows/plugins.yml +185 -6
  5. data/.github/workflows/rspec.yml +55 -5
  6. data/.github/workflows/typecheck.yml +8 -3
  7. data/.gitignore +8 -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 +143 -0
  14. data/README.md +20 -6
  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 -167
  20. data/lib/solargraph/api_map/source_to_yard.rb +97 -88
  21. data/lib/solargraph/api_map/store.rb +384 -241
  22. data/lib/solargraph/api_map.rb +945 -875
  23. data/lib/solargraph/bench.rb +45 -28
  24. data/lib/solargraph/complex_type/type_methods.rb +228 -217
  25. data/lib/solargraph/complex_type/unique_type.rb +482 -386
  26. data/lib/solargraph/complex_type.rb +444 -394
  27. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  28. data/lib/solargraph/convention/base.rb +20 -3
  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/gemfile.rb +15 -15
  33. data/lib/solargraph/convention/gemspec.rb +23 -22
  34. data/lib/solargraph/convention/rakefile.rb +17 -17
  35. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  36. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  37. data/lib/solargraph/convention/struct_definition.rb +164 -0
  38. data/lib/solargraph/convention.rb +78 -47
  39. data/lib/solargraph/converters/dd.rb +17 -17
  40. data/lib/solargraph/converters/dl.rb +15 -15
  41. data/lib/solargraph/converters/dt.rb +15 -15
  42. data/lib/solargraph/converters/misc.rb +1 -1
  43. data/lib/solargraph/diagnostics/require_not_found.rb +53 -53
  44. data/lib/solargraph/diagnostics/rubocop.rb +118 -113
  45. data/lib/solargraph/diagnostics/rubocop_helpers.rb +68 -66
  46. data/lib/solargraph/diagnostics/type_check.rb +55 -55
  47. data/lib/solargraph/diagnostics/update_errors.rb +41 -41
  48. data/lib/solargraph/doc_map.rb +439 -188
  49. data/lib/solargraph/environ.rb +9 -2
  50. data/lib/solargraph/equality.rb +34 -33
  51. data/lib/solargraph/gem_pins.rb +98 -72
  52. data/lib/solargraph/language_server/error_codes.rb +20 -20
  53. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  54. data/lib/solargraph/language_server/host/dispatch.rb +130 -128
  55. data/lib/solargraph/language_server/host/message_worker.rb +112 -106
  56. data/lib/solargraph/language_server/host/sources.rb +99 -99
  57. data/lib/solargraph/language_server/host.rb +878 -861
  58. data/lib/solargraph/language_server/message/base.rb +97 -96
  59. data/lib/solargraph/language_server/message/client/register_capability.rb +15 -15
  60. data/lib/solargraph/language_server/message/completion_item/resolve.rb +60 -60
  61. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +114 -112
  62. data/lib/solargraph/language_server/message/extended/document.rb +23 -20
  63. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  64. data/lib/solargraph/language_server/message/extended/download_core.rb +19 -19
  65. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  66. data/lib/solargraph/language_server/message/initialize.rb +191 -191
  67. data/lib/solargraph/language_server/message/text_document/completion.rb +56 -56
  68. data/lib/solargraph/language_server/message/text_document/definition.rb +40 -38
  69. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +16 -16
  70. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +26 -26
  71. data/lib/solargraph/language_server/message/text_document/formatting.rb +148 -131
  72. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  73. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +11 -11
  74. data/lib/solargraph/language_server/message/text_document/references.rb +16 -16
  75. data/lib/solargraph/language_server/message/text_document/rename.rb +19 -19
  76. data/lib/solargraph/language_server/message/text_document/signature_help.rb +24 -24
  77. data/lib/solargraph/language_server/message/text_document/type_definition.rb +25 -24
  78. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +35 -35
  79. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +40 -40
  80. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +26 -24
  81. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +23 -23
  82. data/lib/solargraph/language_server/message.rb +94 -94
  83. data/lib/solargraph/language_server/progress.rb +8 -0
  84. data/lib/solargraph/language_server/request.rb +27 -24
  85. data/lib/solargraph/language_server/transport/data_reader.rb +74 -74
  86. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  87. data/lib/solargraph/library.rb +683 -662
  88. data/lib/solargraph/location.rb +82 -58
  89. data/lib/solargraph/logging.rb +37 -27
  90. data/lib/solargraph/page.rb +92 -89
  91. data/lib/solargraph/parser/comment_ripper.rb +69 -56
  92. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  93. data/lib/solargraph/parser/node_processor/base.rb +92 -87
  94. data/lib/solargraph/parser/node_processor.rb +62 -45
  95. data/lib/solargraph/parser/parser_gem/class_methods.rb +149 -157
  96. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -18
  97. data/lib/solargraph/parser/parser_gem/node_chainer.rb +166 -164
  98. data/lib/solargraph/parser/parser_gem/node_methods.rb +486 -495
  99. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  100. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -0
  101. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +59 -57
  102. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +15 -15
  103. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -43
  104. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  105. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  106. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +53 -50
  107. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +37 -36
  108. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  109. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  110. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +40 -38
  111. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +29 -28
  112. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +59 -53
  113. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  114. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -0
  115. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -16
  116. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +38 -36
  117. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +52 -42
  118. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +291 -259
  119. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  120. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  121. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  122. data/lib/solargraph/parser/parser_gem/node_processors.rb +70 -56
  123. data/lib/solargraph/parser/parser_gem.rb +12 -12
  124. data/lib/solargraph/parser/region.rb +69 -66
  125. data/lib/solargraph/parser/snippet.rb +17 -15
  126. data/lib/solargraph/parser.rb +23 -22
  127. data/lib/solargraph/pin/base.rb +729 -378
  128. data/lib/solargraph/pin/base_variable.rb +126 -118
  129. data/lib/solargraph/pin/block.rb +104 -101
  130. data/lib/solargraph/pin/breakable.rb +9 -0
  131. data/lib/solargraph/pin/callable.rb +231 -147
  132. data/lib/solargraph/pin/closure.rb +72 -57
  133. data/lib/solargraph/pin/common.rb +79 -70
  134. data/lib/solargraph/pin/constant.rb +45 -43
  135. data/lib/solargraph/pin/conversions.rb +123 -123
  136. data/lib/solargraph/pin/delegated_method.rb +120 -101
  137. data/lib/solargraph/pin/documenting.rb +114 -98
  138. data/lib/solargraph/pin/instance_variable.rb +34 -34
  139. data/lib/solargraph/pin/keyword.rb +20 -15
  140. data/lib/solargraph/pin/local_variable.rb +75 -67
  141. data/lib/solargraph/pin/method.rb +672 -527
  142. data/lib/solargraph/pin/method_alias.rb +34 -31
  143. data/lib/solargraph/pin/namespace.rb +115 -107
  144. data/lib/solargraph/pin/parameter.rb +275 -212
  145. data/lib/solargraph/pin/proxy_type.rb +39 -29
  146. data/lib/solargraph/pin/reference/override.rb +47 -29
  147. data/lib/solargraph/pin/reference/require.rb +2 -2
  148. data/lib/solargraph/pin/reference/superclass.rb +15 -10
  149. data/lib/solargraph/pin/reference.rb +39 -22
  150. data/lib/solargraph/pin/search.rb +61 -56
  151. data/lib/solargraph/pin/signature.rb +61 -17
  152. data/lib/solargraph/pin/singleton.rb +1 -1
  153. data/lib/solargraph/pin/symbol.rb +53 -47
  154. data/lib/solargraph/pin/until.rb +18 -0
  155. data/lib/solargraph/pin/while.rb +18 -0
  156. data/lib/solargraph/pin.rb +44 -41
  157. data/lib/solargraph/pin_cache.rb +245 -0
  158. data/lib/solargraph/position.rb +132 -107
  159. data/lib/solargraph/range.rb +112 -98
  160. data/lib/solargraph/rbs_map/conversions.rb +823 -646
  161. data/lib/solargraph/rbs_map/core_fills.rb +84 -50
  162. data/lib/solargraph/rbs_map/core_map.rb +58 -28
  163. data/lib/solargraph/rbs_map/stdlib_map.rb +43 -33
  164. data/lib/solargraph/rbs_map.rb +163 -93
  165. data/lib/solargraph/server_methods.rb +16 -16
  166. data/lib/solargraph/shell.rb +363 -269
  167. data/lib/solargraph/source/chain/array.rb +37 -33
  168. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  169. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  170. data/lib/solargraph/source/chain/call.rb +337 -303
  171. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  172. data/lib/solargraph/source/chain/constant.rb +26 -89
  173. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  174. data/lib/solargraph/source/chain/hash.rb +34 -33
  175. data/lib/solargraph/source/chain/head.rb +1 -1
  176. data/lib/solargraph/source/chain/if.rb +28 -28
  177. data/lib/solargraph/source/chain/instance_variable.rb +13 -13
  178. data/lib/solargraph/source/chain/link.rb +109 -98
  179. data/lib/solargraph/source/chain/literal.rb +48 -28
  180. data/lib/solargraph/source/chain/or.rb +23 -23
  181. data/lib/solargraph/source/chain/q_call.rb +11 -11
  182. data/lib/solargraph/source/chain/variable.rb +13 -13
  183. data/lib/solargraph/source/chain/z_super.rb +30 -30
  184. data/lib/solargraph/source/chain.rb +291 -252
  185. data/lib/solargraph/source/change.rb +82 -82
  186. data/lib/solargraph/source/cursor.rb +166 -167
  187. data/lib/solargraph/source/encoding_fixes.rb +23 -23
  188. data/lib/solargraph/source/source_chainer.rb +194 -194
  189. data/lib/solargraph/source/updater.rb +55 -55
  190. data/lib/solargraph/source.rb +498 -495
  191. data/lib/solargraph/source_map/clip.rb +226 -232
  192. data/lib/solargraph/source_map/data.rb +34 -30
  193. data/lib/solargraph/source_map/mapper.rb +259 -255
  194. data/lib/solargraph/source_map.rb +212 -217
  195. data/lib/solargraph/type_checker/checks.rb +124 -120
  196. data/lib/solargraph/type_checker/param_def.rb +37 -35
  197. data/lib/solargraph/type_checker/problem.rb +32 -32
  198. data/lib/solargraph/type_checker/rules.rb +84 -62
  199. data/lib/solargraph/type_checker.rb +814 -672
  200. data/lib/solargraph/version.rb +5 -5
  201. data/lib/solargraph/views/_method.erb +10 -10
  202. data/lib/solargraph/views/_namespace.erb +3 -3
  203. data/lib/solargraph/views/document.erb +10 -10
  204. data/lib/solargraph/workspace/config.rb +255 -239
  205. data/lib/solargraph/workspace/require_paths.rb +97 -0
  206. data/lib/solargraph/workspace.rb +220 -239
  207. data/lib/solargraph/yard_map/helpers.rb +44 -16
  208. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  209. data/lib/solargraph/yard_map/mapper/to_method.rb +130 -94
  210. data/lib/solargraph/yard_map/mapper/to_namespace.rb +31 -28
  211. data/lib/solargraph/yard_map/mapper.rb +79 -78
  212. data/lib/solargraph/yard_map/to_method.rb +89 -86
  213. data/lib/solargraph/yard_tags.rb +20 -20
  214. data/lib/solargraph/yardoc.rb +87 -52
  215. data/lib/solargraph.rb +105 -72
  216. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  217. data/rbs/fills/open3/0/open3.rbs +172 -0
  218. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  219. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  220. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  221. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  222. data/rbs/fills/tuple/tuple.rbs +149 -0
  223. data/rbs/shims/ast/0/node.rbs +5 -0
  224. data/rbs/shims/ast/2.4/.rbs_meta.yaml +9 -0
  225. data/rbs/shims/ast/2.4/ast.rbs +73 -0
  226. data/rbs/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  227. data/rbs/shims/parser/3.2.0.1/manifest.yaml +7 -0
  228. data/rbs/shims/parser/3.2.0.1/parser.rbs +201 -0
  229. data/rbs/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  230. data/rbs/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  231. data/rbs/shims/thor/1.2.0.1/manifest.yaml +7 -0
  232. data/rbs/shims/thor/1.2.0.1/thor.rbs +17 -0
  233. data/rbs_collection.yaml +19 -0
  234. data/solargraph.gemspec +27 -5
  235. metadata +215 -18
  236. data/lib/.rubocop.yml +0 -22
  237. data/lib/solargraph/cache.rb +0 -77
  238. data/lib/solargraph/parser/node_methods.rb +0 -83
@@ -1,269 +1,363 @@
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::Cache.clear
94
- end
95
- map 'clear-cache' => :clear
96
- map 'clear-cores' => :clear
97
-
98
- desc 'cache', 'Cache a gem', hide: true
99
- # @return [void]
100
- # @param gem [String]
101
- # @param version [String, nil]
102
- def cache gem, version = nil
103
- spec = Gem::Specification.find_by_name(gem, version)
104
- pins = GemPins.build(spec)
105
- Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins)
106
- end
107
-
108
- desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
109
- long_desc %(
110
- Specify one or more gem names to clear. 'core' or 'stdlib' may
111
- also be specified to clear cached system documentation.
112
- Documentation will be regenerated as needed.
113
- )
114
- # @param gems [Array<String>]
115
- # @return [void]
116
- def uncache *gems
117
- raise ArgumentError, 'No gems specified.' if gems.empty?
118
- gems.each do |gem|
119
- if gem == 'core'
120
- Cache.uncache("core.ser")
121
- next
122
- end
123
-
124
- if gem == 'stdlib'
125
- Cache.uncache("stdlib")
126
- next
127
- end
128
-
129
- spec = Gem::Specification.find_by_name(gem)
130
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.ser")
131
- Cache.uncache('gems', "#{spec.name}-#{spec.version}.yardoc")
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
- if names.empty?
141
- Gem::Specification.to_a.each { |spec| do_cache spec }
142
- else
143
- names.each do |name|
144
- spec = Gem::Specification.find_by_name(*name.split('='))
145
- do_cache spec
146
- rescue Gem::MissingSpecError
147
- warn "Gem '#{name}' not found"
148
- end
149
- end
150
- end
151
-
152
- desc 'reporters', 'Get a list of diagnostics reporters'
153
- # @return [void]
154
- def reporters
155
- puts Solargraph::Diagnostics.reporters
156
- end
157
-
158
- desc 'typecheck [FILE(s)]', 'Run the type checker'
159
- long_desc %(
160
- Perform type checking on one or more files in a workspace. Check the
161
- entire workspace if no files are specified.
162
-
163
- Type checking levels are normal, typed, strict, and strong.
164
- )
165
- option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
166
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
167
- # @return [void]
168
- def typecheck *files
169
- directory = File.realpath(options[:directory])
170
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
171
- probcount = 0
172
- if files.empty?
173
- files = api_map.source_maps.map(&:filename)
174
- else
175
- files.map! { |file| File.realpath(file) }
176
- end
177
- filecount = 0
178
-
179
- time = Benchmark.measure {
180
- files.each do |file|
181
- checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym)
182
- problems = checker.problems
183
- next if problems.empty?
184
- problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
185
- puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
186
- filecount += 1
187
- probcount += problems.length
188
- end
189
- # "
190
- }
191
- puts "Typecheck finished in #{time.real} seconds."
192
- puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
193
- # "
194
- exit 1 if probcount > 0
195
- end
196
-
197
- desc 'scan', 'Test the workspace for problems'
198
- long_desc %(
199
- A scan loads the entire workspace to make sure that the ASTs and
200
- maps do not raise errors during analysis. It does not perform any type
201
- checking or validation; it only confirms that the analysis itself is
202
- error-free.
203
- )
204
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
205
- option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
206
- # @return [void]
207
- def scan
208
- directory = File.realpath(options[:directory])
209
- api_map = nil
210
- time = Benchmark.measure {
211
- api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
212
- api_map.pins.each do |pin|
213
- begin
214
- puts pin_description(pin) if options[:verbose]
215
- pin.typify api_map
216
- pin.probe api_map
217
- rescue StandardError => e
218
- STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
219
- STDERR.puts "[#{e.class}]: #{e.message}"
220
- STDERR.puts e.backtrace.join("\n")
221
- exit 1
222
- end
223
- end
224
- }
225
- puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
226
- end
227
-
228
- desc 'list', 'List the files in the workspace and the total count'
229
- option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
230
- option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
231
- # @return [void]
232
- def list
233
- workspace = Solargraph::Workspace.new(options[:directory])
234
- puts workspace.filenames unless options[:count]
235
- puts "#{workspace.filenames.length} files total."
236
- end
237
-
238
- private
239
-
240
- # @param pin [Solargraph::Pin::Base]
241
- # @return [String]
242
- def pin_description pin
243
- desc = if pin.path.nil? || pin.path.empty?
244
- if pin.closure
245
- "#{pin.closure.path} | #{pin.name}"
246
- else
247
- "#{pin.context.namespace} | #{pin.name}"
248
- end
249
- else
250
- pin.path
251
- end
252
- desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
253
- desc
254
- end
255
-
256
- # @param gemspec [Gem::Specification]
257
- # @return [void]
258
- def do_cache gemspec
259
- cached = Yardoc.cached?(gemspec)
260
- if cached && !options.rebuild
261
- puts "Cache already exists for #{gemspec.name} #{gemspec.version}"
262
- else
263
- puts "#{cached ? 'Rebuilding' : 'Caching'} gem documentation for #{gemspec.name} #{gemspec.version}"
264
- pins = GemPins.build(gemspec)
265
- Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
266
- end
267
- end
268
- end
269
- 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
+ gemspec = Gem::Specification.find_by_name(gem, version)
108
+
109
+ if options[:rebuild] || !PinCache.has_yard?(gemspec)
110
+ pins = GemPins.build_yard_pins(['yard-activesupport-concern'], gemspec)
111
+ PinCache.serialize_yard_gem(gemspec, pins)
112
+ end
113
+
114
+ workspace = Solargraph::Workspace.new(Dir.pwd)
115
+ rbs_map = RbsMap.from_gemspec(gemspec, workspace.rbs_collection_path, workspace.rbs_collection_config_path)
116
+ if options[:rebuild] || !PinCache.has_rbs_collection?(gemspec, rbs_map.cache_key)
117
+ # cache pins even if result is zero, so we don't retry building pins
118
+ pins = rbs_map.pins || []
119
+ PinCache.serialize_rbs_collection_gem(gemspec, rbs_map.cache_key, pins)
120
+ end
121
+ end
122
+
123
+ desc 'uncache GEM [...GEM]', "Delete specific cached gem documentation"
124
+ long_desc %(
125
+ Specify one or more gem names to clear. 'core' or 'stdlib' may
126
+ also be specified to clear cached system documentation.
127
+ Documentation will be regenerated as needed.
128
+ )
129
+ # @param gems [Array<String>]
130
+ # @return [void]
131
+ def uncache *gems
132
+ raise ArgumentError, 'No gems specified.' if gems.empty?
133
+ gems.each do |gem|
134
+ if gem == 'core'
135
+ PinCache.uncache_core
136
+ next
137
+ end
138
+
139
+ if gem == 'stdlib'
140
+ PinCache.uncache_stdlib
141
+ next
142
+ end
143
+
144
+ spec = Gem::Specification.find_by_name(gem)
145
+ PinCache.uncache_gem(spec, out: $stdout)
146
+ end
147
+ end
148
+
149
+ desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
150
+ option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
151
+ # @param names [Array<String>]
152
+ # @return [void]
153
+ def gems *names
154
+ api_map = ApiMap.load('.')
155
+ if names.empty?
156
+ Gem::Specification.to_a.each { |spec| do_cache spec, api_map }
157
+ STDERR.puts "Documentation cached for all #{Gem::Specification.count} gems."
158
+ else
159
+ names.each do |name|
160
+ spec = Gem::Specification.find_by_name(*name.split('='))
161
+ do_cache spec, api_map
162
+ rescue Gem::MissingSpecError
163
+ warn "Gem '#{name}' not found"
164
+ end
165
+ STDERR.puts "Documentation cached for #{names.count} gems."
166
+ end
167
+ end
168
+
169
+ desc 'reporters', 'Get a list of diagnostics reporters'
170
+ # @return [void]
171
+ def reporters
172
+ puts Solargraph::Diagnostics.reporters
173
+ end
174
+
175
+ desc 'typecheck [FILE(s)]', 'Run the type checker'
176
+ long_desc %(
177
+ Perform type checking on one or more files in a workspace. Check the
178
+ entire workspace if no files are specified.
179
+
180
+ Type checking levels are normal, typed, strict, and strong.
181
+ )
182
+ option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
183
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
184
+ # @return [void]
185
+ def typecheck *files
186
+ directory = File.realpath(options[:directory])
187
+ workspace = Solargraph::Workspace.new(directory)
188
+ level = options[:level].to_sym
189
+ rules = workspace.rules(level)
190
+ api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
191
+ probcount = 0
192
+ if files.empty?
193
+ files = api_map.source_maps.map(&:filename)
194
+ else
195
+ files.map! { |file| File.realpath(file) }
196
+ end
197
+ filecount = 0
198
+
199
+ time = Benchmark.measure {
200
+ files.each do |file|
201
+ checker = TypeChecker.new(file, api_map: api_map, level: options[:level].to_sym, workspace: workspace)
202
+ problems = checker.problems
203
+ next if problems.empty?
204
+ problems.sort! { |a, b| a.location.range.start.line <=> b.location.range.start.line }
205
+ puts problems.map { |prob| "#{prob.location.filename}:#{prob.location.range.start.line + 1} - #{prob.message}" }.join("\n")
206
+ filecount += 1
207
+ probcount += problems.length
208
+ end
209
+ # "
210
+ }
211
+ puts "Typecheck finished in #{time.real} seconds."
212
+ puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
213
+ # "
214
+ exit 1 if probcount > 0
215
+ end
216
+
217
+ desc 'scan', 'Test the workspace for problems'
218
+ long_desc %(
219
+ A scan loads the entire workspace to make sure that the ASTs and
220
+ maps do not raise errors during analysis. It does not perform any type
221
+ checking or validation; it only confirms that the analysis itself is
222
+ error-free.
223
+ )
224
+ option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
225
+ option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
226
+ # @return [void]
227
+ def scan
228
+ directory = File.realpath(options[:directory])
229
+ # @type [Solargraph::ApiMap, nil]
230
+ api_map = nil
231
+ time = Benchmark.measure {
232
+ api_map = Solargraph::ApiMap.load_with_cache(directory, $stdout)
233
+ api_map.pins.each do |pin|
234
+ begin
235
+ puts pin_description(pin) if options[:verbose]
236
+ pin.typify api_map
237
+ pin.probe api_map
238
+ rescue StandardError => e
239
+ STDERR.puts "Error testing #{pin_description(pin)} #{pin.location ? "at #{pin.location.filename}:#{pin.location.range.start.line + 1}" : ''}"
240
+ STDERR.puts "[#{e.class}]: #{e.message}"
241
+ STDERR.puts e.backtrace.join("\n")
242
+ exit 1
243
+ end
244
+ end
245
+ }
246
+ puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
247
+ end
248
+
249
+ desc 'list', 'List the files in the workspace and the total count'
250
+ option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
251
+ option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
252
+ # @return [void]
253
+ def list
254
+ workspace = Solargraph::Workspace.new(options[:directory])
255
+ puts workspace.filenames unless options[:count]
256
+ puts "#{workspace.filenames.length} files total."
257
+ end
258
+
259
+ desc 'pin [PATH]', 'Describe a pin', hide: true
260
+ option :rbs, type: :boolean, desc: 'Output the pin as RBS', default: false
261
+ option :typify, type: :boolean, desc: 'Output the calculated return type of the pin from annotations', default: false
262
+ option :references, type: :boolean, desc: 'Show references', default: false
263
+ option :probe, type: :boolean, desc: 'Output the calculated return type of the pin from annotations and inference', default: false
264
+ option :stack, type: :boolean, desc: 'Show entire stack of a method pin by including definitions in superclasses', default: false
265
+ # @param path [String] The path to the method pin, e.g. 'Class#method' or 'Class.method'
266
+ # @return [void]
267
+ def pin path
268
+ api_map = Solargraph::ApiMap.load_with_cache('.', $stderr)
269
+ is_method = path.include?('#') || path.include?('.')
270
+ if is_method && options[:stack]
271
+ scope, ns, meth = if path.include? '#'
272
+ [:instance, *path.split('#', 2)]
273
+ else
274
+ [:class, *path.split('.', 2)]
275
+ end
276
+
277
+ # @sg-ignore Wrong argument type for
278
+ # Solargraph::ApiMap#get_method_stack: rooted_tag
279
+ # expected String, received Array<String>
280
+ pins = api_map.get_method_stack(ns, meth, scope: scope)
281
+ else
282
+ pins = api_map.get_path_pins path
283
+ end
284
+ # @type [Hash{Symbol => Pin::Base}]
285
+ references = {}
286
+ pin = pins.first
287
+ case pin
288
+ when nil
289
+ $stderr.puts "Pin not found for path '#{path}'"
290
+ exit 1
291
+ when Pin::Namespace
292
+ if options[:references]
293
+ superclass_tag = api_map.qualify_superclass(pin.return_type.tag)
294
+ superclass_pin = api_map.get_path_pins(superclass_tag).first if superclass_tag
295
+ references[:superclass] = superclass_pin if superclass_pin
296
+ end
297
+ end
298
+
299
+ pins.each do |pin|
300
+ if options[:typify] || options[:probe]
301
+ type = ComplexType::UNDEFINED
302
+ type = pin.typify(api_map) if options[:typify]
303
+ type = pin.probe(api_map) if options[:probe] && type.undefined?
304
+ print_type(type)
305
+ next
306
+ end
307
+
308
+ print_pin(pin)
309
+ end
310
+ references.each do |key, refpin|
311
+ puts "\n# #{key.to_s.capitalize}:\n\n"
312
+ print_pin(refpin)
313
+ end
314
+ end
315
+
316
+ private
317
+
318
+ # @param pin [Solargraph::Pin::Base]
319
+ # @return [String]
320
+ def pin_description pin
321
+ desc = if pin.path.nil? || pin.path.empty?
322
+ if pin.closure
323
+ "#{pin.closure.path} | #{pin.name}"
324
+ else
325
+ "#{pin.context.namespace} | #{pin.name}"
326
+ end
327
+ else
328
+ pin.path
329
+ end
330
+ desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
331
+ desc
332
+ end
333
+
334
+ # @param gemspec [Gem::Specification]
335
+ # @param api_map [ApiMap]
336
+ # @return [void]
337
+ def do_cache gemspec, api_map
338
+ # @todo if the rebuild: option is passed as a positional arg,
339
+ # typecheck doesn't complain on the below line
340
+ api_map.cache_gem(gemspec, rebuild: options.rebuild, out: $stdout)
341
+ end
342
+
343
+ # @param type [ComplexType]
344
+ # @return [void]
345
+ def print_type(type)
346
+ if options[:rbs]
347
+ puts type.to_rbs
348
+ else
349
+ puts type.rooted_tag
350
+ end
351
+ end
352
+
353
+ # @param pin [Solargraph::Pin::Base]
354
+ # @return [void]
355
+ def print_pin(pin)
356
+ if options[:rbs]
357
+ puts pin.to_rbs
358
+ else
359
+ puts pin.inspect
360
+ end
361
+ end
362
+ end
363
+ end