solargraph 0.47.2 → 0.53.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 (185) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/plugins.yml +40 -0
  4. data/.github/workflows/rspec.yml +4 -8
  5. data/.github/workflows/typecheck.yml +34 -0
  6. data/.yardopts +2 -2
  7. data/CHANGELOG.md +137 -3
  8. data/LICENSE +1 -1
  9. data/README.md +19 -16
  10. data/SPONSORS.md +2 -9
  11. data/lib/solargraph/api_map/cache.rb +60 -20
  12. data/lib/solargraph/api_map/source_to_yard.rb +17 -10
  13. data/lib/solargraph/api_map/store.rb +60 -12
  14. data/lib/solargraph/api_map.rb +171 -99
  15. data/lib/solargraph/bench.rb +3 -2
  16. data/lib/solargraph/cache.rb +77 -0
  17. data/lib/solargraph/complex_type/type_methods.rb +61 -12
  18. data/lib/solargraph/complex_type/unique_type.rb +193 -16
  19. data/lib/solargraph/complex_type.rb +113 -10
  20. data/lib/solargraph/convention/rakefile.rb +17 -0
  21. data/lib/solargraph/convention.rb +2 -3
  22. data/lib/solargraph/converters/dd.rb +5 -0
  23. data/lib/solargraph/converters/dl.rb +3 -0
  24. data/lib/solargraph/converters/dt.rb +3 -0
  25. data/lib/solargraph/diagnostics/rubocop.rb +23 -8
  26. data/lib/solargraph/diagnostics/rubocop_helpers.rb +4 -1
  27. data/lib/solargraph/diagnostics/type_check.rb +1 -0
  28. data/lib/solargraph/diagnostics.rb +2 -2
  29. data/lib/solargraph/doc_map.rb +171 -0
  30. data/lib/solargraph/gem_pins.rb +64 -0
  31. data/lib/solargraph/language_server/host/cataloger.rb +2 -1
  32. data/lib/solargraph/language_server/host/diagnoser.rb +2 -2
  33. data/lib/solargraph/language_server/host/dispatch.rb +15 -5
  34. data/lib/solargraph/language_server/host/message_worker.rb +4 -0
  35. data/lib/solargraph/language_server/host/sources.rb +7 -4
  36. data/lib/solargraph/language_server/host.rb +50 -26
  37. data/lib/solargraph/language_server/message/completion_item/resolve.rb +3 -1
  38. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +13 -1
  39. data/lib/solargraph/language_server/message/extended/download_core.rb +1 -5
  40. data/lib/solargraph/language_server/message/initialize.rb +13 -0
  41. data/lib/solargraph/language_server/message/initialized.rb +1 -0
  42. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +4 -1
  43. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -4
  44. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  45. data/lib/solargraph/language_server/message/text_document/signature_help.rb +1 -6
  46. data/lib/solargraph/language_server/message/text_document/type_definition.rb +24 -0
  47. data/lib/solargraph/language_server/message/text_document.rb +1 -1
  48. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +5 -0
  49. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +10 -3
  50. data/lib/solargraph/language_server/message.rb +1 -0
  51. data/lib/solargraph/language_server/transport/adapter.rb +16 -1
  52. data/lib/solargraph/language_server/transport/data_reader.rb +2 -0
  53. data/lib/solargraph/library.rb +124 -37
  54. data/lib/solargraph/location.rb +1 -0
  55. data/lib/solargraph/page.rb +6 -0
  56. data/lib/solargraph/parser/comment_ripper.rb +4 -0
  57. data/lib/solargraph/parser/node_methods.rb +47 -7
  58. data/lib/solargraph/parser/node_processor/base.rb +9 -0
  59. data/lib/solargraph/parser/{legacy → parser_gem}/class_methods.rb +31 -5
  60. data/lib/solargraph/parser/{legacy → parser_gem}/flawed_builder.rb +3 -1
  61. data/lib/solargraph/parser/{legacy → parser_gem}/node_chainer.rb +57 -41
  62. data/lib/solargraph/parser/parser_gem/node_methods.rb +499 -0
  63. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/alias_node.rb +1 -1
  64. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +53 -0
  65. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/begin_node.rb +1 -1
  66. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/block_node.rb +3 -2
  67. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/casgn_node.rb +14 -4
  68. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/cvasgn_node.rb +1 -1
  69. data/lib/solargraph/parser/{rubyvm → parser_gem}/node_processors/def_node.rb +7 -20
  70. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/defs_node.rb +2 -2
  71. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/gvasgn_node.rb +1 -1
  72. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/ivasgn_node.rb +2 -2
  73. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/lvasgn_node.rb +2 -2
  74. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/namespace_node.rb +2 -2
  75. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/orasgn_node.rb +1 -1
  76. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/resbody_node.rb +3 -3
  77. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +42 -0
  78. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/send_node.rb +2 -2
  79. data/lib/solargraph/parser/{legacy → parser_gem}/node_processors/sym_node.rb +1 -1
  80. data/lib/solargraph/parser/parser_gem/node_processors.rb +54 -0
  81. data/lib/solargraph/parser/parser_gem.rb +12 -0
  82. data/lib/solargraph/parser/region.rb +1 -1
  83. data/lib/solargraph/parser/snippet.rb +2 -0
  84. data/lib/solargraph/parser.rb +9 -10
  85. data/lib/solargraph/pin/base.rb +69 -11
  86. data/lib/solargraph/pin/base_variable.rb +8 -4
  87. data/lib/solargraph/pin/block.rb +21 -28
  88. data/lib/solargraph/pin/closure.rb +17 -2
  89. data/lib/solargraph/pin/common.rb +7 -3
  90. data/lib/solargraph/pin/conversions.rb +34 -8
  91. data/lib/solargraph/pin/delegated_method.rb +97 -0
  92. data/lib/solargraph/pin/documenting.rb +25 -34
  93. data/lib/solargraph/pin/instance_variable.rb +4 -0
  94. data/lib/solargraph/pin/local_variable.rb +13 -1
  95. data/lib/solargraph/pin/method.rb +270 -16
  96. data/lib/solargraph/pin/namespace.rb +17 -1
  97. data/lib/solargraph/pin/parameter.rb +52 -17
  98. data/lib/solargraph/pin/reference/override.rb +2 -2
  99. data/lib/solargraph/pin/reference.rb +8 -0
  100. data/lib/solargraph/pin/search.rb +4 -4
  101. data/lib/solargraph/pin/signature.rb +143 -0
  102. data/lib/solargraph/pin.rb +2 -1
  103. data/lib/solargraph/range.rb +4 -6
  104. data/lib/solargraph/rbs_map/conversions.rb +601 -0
  105. data/lib/solargraph/rbs_map/core_fills.rb +47 -0
  106. data/lib/solargraph/rbs_map/core_map.rb +28 -0
  107. data/lib/solargraph/rbs_map/stdlib_map.rb +33 -0
  108. data/lib/solargraph/rbs_map.rb +84 -0
  109. data/lib/solargraph/shell.rb +69 -48
  110. data/lib/solargraph/source/chain/array.rb +32 -0
  111. data/lib/solargraph/source/chain/block_symbol.rb +13 -0
  112. data/lib/solargraph/source/chain/call.rb +125 -61
  113. data/lib/solargraph/source/chain/constant.rb +15 -1
  114. data/lib/solargraph/source/chain/if.rb +23 -0
  115. data/lib/solargraph/source/chain/link.rb +8 -2
  116. data/lib/solargraph/source/chain/or.rb +1 -1
  117. data/lib/solargraph/source/chain/z_super.rb +3 -3
  118. data/lib/solargraph/source/chain.rb +44 -14
  119. data/lib/solargraph/source/change.rb +3 -0
  120. data/lib/solargraph/source/cursor.rb +2 -0
  121. data/lib/solargraph/source/source_chainer.rb +8 -5
  122. data/lib/solargraph/source.rb +18 -19
  123. data/lib/solargraph/source_map/clip.rb +30 -23
  124. data/lib/solargraph/source_map/mapper.rb +20 -5
  125. data/lib/solargraph/source_map.rb +28 -13
  126. data/lib/solargraph/type_checker/checks.rb +10 -2
  127. data/lib/solargraph/type_checker.rb +201 -98
  128. data/lib/solargraph/version.rb +1 -1
  129. data/lib/solargraph/views/environment.erb +2 -2
  130. data/lib/solargraph/workspace/config.rb +14 -11
  131. data/lib/solargraph/workspace.rb +28 -17
  132. data/lib/solargraph/yard_map/cache.rb +6 -0
  133. data/lib/solargraph/yard_map/helpers.rb +1 -1
  134. data/lib/solargraph/yard_map/mapper/to_method.rb +18 -5
  135. data/lib/solargraph/yard_map/mapper.rb +1 -1
  136. data/lib/solargraph/yard_map/to_method.rb +11 -4
  137. data/lib/solargraph/yard_map.rb +1 -443
  138. data/lib/solargraph/yard_tags.rb +20 -0
  139. data/lib/solargraph/yardoc.rb +52 -0
  140. data/lib/solargraph.rb +8 -6
  141. data/solargraph.gemspec +19 -8
  142. metadata +162 -98
  143. data/.travis.yml +0 -19
  144. data/lib/solargraph/api_map/bundler_methods.rb +0 -22
  145. data/lib/solargraph/compat.rb +0 -37
  146. data/lib/solargraph/convention/rspec.rb +0 -30
  147. data/lib/solargraph/documentor.rb +0 -76
  148. data/lib/solargraph/parser/legacy/node_methods.rb +0 -325
  149. data/lib/solargraph/parser/legacy/node_processors/alias_node.rb +0 -23
  150. data/lib/solargraph/parser/legacy/node_processors/args_node.rb +0 -35
  151. data/lib/solargraph/parser/legacy/node_processors/begin_node.rb +0 -15
  152. data/lib/solargraph/parser/legacy/node_processors/def_node.rb +0 -63
  153. data/lib/solargraph/parser/legacy/node_processors/sclass_node.rb +0 -21
  154. data/lib/solargraph/parser/legacy/node_processors.rb +0 -54
  155. data/lib/solargraph/parser/legacy.rb +0 -12
  156. data/lib/solargraph/parser/rubyvm/class_methods.rb +0 -144
  157. data/lib/solargraph/parser/rubyvm/node_chainer.rb +0 -160
  158. data/lib/solargraph/parser/rubyvm/node_methods.rb +0 -315
  159. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +0 -85
  160. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +0 -42
  161. data/lib/solargraph/parser/rubyvm/node_processors/casgn_node.rb +0 -22
  162. data/lib/solargraph/parser/rubyvm/node_processors/cvasgn_node.rb +0 -23
  163. data/lib/solargraph/parser/rubyvm/node_processors/defs_node.rb +0 -57
  164. data/lib/solargraph/parser/rubyvm/node_processors/gvasgn_node.rb +0 -23
  165. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +0 -38
  166. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +0 -39
  167. data/lib/solargraph/parser/rubyvm/node_processors/lit_node.rb +0 -20
  168. data/lib/solargraph/parser/rubyvm/node_processors/lvasgn_node.rb +0 -27
  169. data/lib/solargraph/parser/rubyvm/node_processors/namespace_node.rb +0 -39
  170. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +0 -26
  171. data/lib/solargraph/parser/rubyvm/node_processors/orasgn_node.rb +0 -15
  172. data/lib/solargraph/parser/rubyvm/node_processors/resbody_node.rb +0 -45
  173. data/lib/solargraph/parser/rubyvm/node_processors/sclass_node.rb +0 -21
  174. data/lib/solargraph/parser/rubyvm/node_processors/scope_node.rb +0 -15
  175. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +0 -277
  176. data/lib/solargraph/parser/rubyvm/node_processors/sym_node.rb +0 -18
  177. data/lib/solargraph/parser/rubyvm/node_processors.rb +0 -63
  178. data/lib/solargraph/parser/rubyvm.rb +0 -40
  179. data/lib/solargraph/yard_map/core_docs.rb +0 -170
  180. data/lib/solargraph/yard_map/core_fills.rb +0 -208
  181. data/lib/solargraph/yard_map/core_gen.rb +0 -76
  182. data/lib/solargraph/yard_map/rdoc_to_yard.rb +0 -140
  183. data/lib/solargraph/yard_map/stdlib_fills.rb +0 -43
  184. data/lib/yard-solargraph.rb +0 -33
  185. data/yardoc/2.2.2.tar.gz +0 -0
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbs'
4
+
5
+ module Solargraph
6
+ class RbsMap
7
+ # Ruby stdlib pins
8
+ #
9
+ class StdlibMap < RbsMap
10
+ # @type [Hash{String => RbsMap}]
11
+ @stdlib_maps_hash = {}
12
+
13
+ # @param library [String]
14
+ def initialize library
15
+ cache = Cache.load('stdlib', "#{library}.ser")
16
+ if cache
17
+ pins.replace cache
18
+ @resolved = true
19
+ else
20
+ super
21
+ return unless resolved?
22
+ Cache.save('stdlib', "#{library}.ser", pins)
23
+ end
24
+ end
25
+
26
+ # @param library [String]
27
+ # @return [StdlibMap]
28
+ def self.load library
29
+ @stdlib_maps_hash[library] ||= StdlibMap.new(library)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbs'
4
+
5
+ module Solargraph
6
+ class RbsMap
7
+ autoload :Conversions, 'solargraph/rbs_map/conversions'
8
+ autoload :CoreMap, 'solargraph/rbs_map/core_map'
9
+ autoload :CoreFills, 'solargraph/rbs_map/core_fills'
10
+ autoload :StdlibMap, 'solargraph/rbs_map/stdlib_map'
11
+
12
+ include Conversions
13
+
14
+ # @type [Hash{String => RbsMap}]
15
+ @@rbs_maps_hash = {}
16
+
17
+ attr_reader :library
18
+
19
+ # @param library [String]
20
+ def initialize library, version = nil
21
+ @library = library
22
+ @version = version
23
+ @collection = nil
24
+ loader = RBS::EnvironmentLoader.new(core_root: nil, repository: repository)
25
+ add_library loader, library, version
26
+ return unless resolved?
27
+ load_environment_to_pins(loader)
28
+ end
29
+
30
+ # @generic T
31
+ # @param path [String]
32
+ # @param klass [Class<generic<T>>]
33
+ # @return [generic<T>, nil]
34
+ def path_pin path, klass = Pin::Base
35
+ pin = pins.find { |p| p.path == path }
36
+ pin if pin&.is_a?(klass)
37
+ end
38
+
39
+ # @param path [String]
40
+ # @return [Array<Pin::Base>]
41
+ def path_pins path
42
+ pins.select { |p| p.path == path }
43
+ end
44
+
45
+ def resolved?
46
+ @resolved
47
+ end
48
+
49
+ def repository
50
+ @repository ||= RBS::Repository.new(no_stdlib: false)
51
+ end
52
+
53
+ # @param library [String]
54
+ # @return [RbsMap]
55
+ def self.load library
56
+ @@rbs_maps_hash[library] ||= RbsMap.new(library)
57
+ end
58
+
59
+ def self.from_gemspec(gemspec)
60
+ RbsMap.new(gemspec.name, gemspec.version)
61
+ end
62
+
63
+ private
64
+
65
+ # @param loader [RBS::EnvironmentLoader]
66
+ # @param library [String]
67
+ # @return [Boolean] true if adding the library succeeded
68
+ def add_library loader, library, version
69
+ @resolved = if loader.has_library?(library: library, version: version)
70
+ loader.add library: library, version: version
71
+ Solargraph.logger.info "#{short_name} successfully loaded library #{library}"
72
+ true
73
+ else
74
+ Solargraph.logger.debug "#{short_name} failed to load library #{library}"
75
+ false
76
+ end
77
+ end
78
+
79
+ # @return [String]
80
+ def short_name
81
+ self.class.name.split('::').last
82
+ end
83
+ end
84
+ end
@@ -1,14 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require 'yard'
4
5
 
5
6
  module Solargraph
6
7
  class Shell < Thor
7
8
  include Solargraph::ServerMethods
8
9
 
10
+ # Tell Thor to ensure the process exits with status 1 if any error happens.
11
+ def self.exit_on_failure?
12
+ true
13
+ end
14
+
9
15
  map %w[--version -v] => :version
10
16
 
11
17
  desc "--version, -v", "Print the version"
18
+ # @return [void]
12
19
  def version
13
20
  puts Solargraph::VERSION
14
21
  end
@@ -16,6 +23,7 @@ module Solargraph
16
23
  desc 'socket', 'Run a Solargraph socket server'
17
24
  option :host, type: :string, aliases: :h, desc: 'The server host', default: '127.0.0.1'
18
25
  option :port, type: :numeric, aliases: :p, desc: 'The server port', default: 7658
26
+ # @return [void]
19
27
  def socket
20
28
  require 'backport'
21
29
  port = options[:port]
@@ -33,6 +41,7 @@ module Solargraph
33
41
  end
34
42
 
35
43
  desc 'stdio', 'Run a Solargraph stdio server'
44
+ # @return [void]
36
45
  def stdio
37
46
  require 'backport'
38
47
  Backport.run do
@@ -49,6 +58,8 @@ module Solargraph
49
58
 
50
59
  desc 'config [DIRECTORY]', 'Create or overwrite a default configuration file'
51
60
  option :extensions, type: :boolean, aliases: :e, desc: 'Add installed extensions', default: true
61
+ # @param directory [String]
62
+ # @return [void]
52
63
  def config(directory = '.')
53
64
  matches = []
54
65
  if options[:extensions]
@@ -71,59 +82,57 @@ module Solargraph
71
82
  STDOUT.puts "Configuration file initialized."
72
83
  end
73
84
 
74
- desc 'download-core [VERSION]', 'Download core documentation'
75
- def download_core version = nil
76
- ver = version || Solargraph::YardMap::CoreDocs.best_download
77
- if RUBY_VERSION != ver
78
- puts "Documentation for #{RUBY_VERSION} is not available. Reverting to closest match..."
79
- end
80
- puts "Downloading docs for #{ver}..."
81
- Solargraph::YardMap::CoreDocs.download ver
82
- # Clear cached documentation if it exists
83
- FileUtils.rm_rf Dir.glob(File.join(Solargraph::YardMap::CoreDocs.cache_dir, ver, '*.ser'))
84
- puts "Download complete."
85
- rescue ArgumentError => e
86
- STDERR.puts "ERROR: #{e.message}"
87
- STDERR.puts "Run `solargraph available-cores` for a list."
88
- exit 1
89
- end
90
-
91
- desc 'list-cores', 'List the local documentation versions'
92
- def list_cores
93
- puts Solargraph::YardMap::CoreDocs.versions.join("\n")
94
- end
95
-
96
- desc 'available-cores', 'List available documentation versions'
97
- def available_cores
98
- puts Solargraph::YardMap::CoreDocs.available.join("\n")
99
- end
100
-
101
85
  desc 'clear', 'Delete all cached documentation'
102
86
  long_desc %(
103
87
  This command will delete all core and gem documentation from the cache.
104
- You can also delete specific gem caches with the `uncache` command or
105
- update documentation for specific Ruby versions with the `download-core`
106
- command.
107
88
  )
89
+ # @return [void]
108
90
  def clear
109
91
  puts "Deleting the cached documentation"
110
- Solargraph::YardMap::CoreDocs.clear
92
+ Solargraph::Cache.clear
111
93
  end
112
94
  map 'clear-cache' => :clear
113
95
  map 'clear-cores' => :clear
114
96
 
97
+ desc 'cache', 'Cache a gem', hide: true
98
+ # @return [void]
99
+ # @param gem [String]
100
+ # @param version [String, nil]
101
+ def cache gem, version = nil
102
+ spec = Gem::Specification.find_by_name(gem, version)
103
+ pins = GemPins.build(spec)
104
+ Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins)
105
+ end
106
+
115
107
  desc 'uncache GEM [...GEM]', "Delete cached gem documentation"
108
+ # @return [void]
116
109
  def uncache *gems
117
110
  raise ArgumentError, 'No gems specified.' if gems.empty?
118
111
  gems.each do |gem|
119
- Dir[File.join(Solargraph::YardMap::CoreDocs.cache_dir, 'gems', "#{gem}-*")].each do |dir|
120
- puts "Deleting cache: #{dir}"
121
- FileUtils.remove_entry_secure dir
112
+ spec = Gem::Specification.find_by_name(gem)
113
+ Cache.uncache('gems', "#{spec.name}-#{spec.version}.ser")
114
+ Cache.uncache('gems', "#{spec.name}-#{spec.version}.yardoc")
115
+ end
116
+ end
117
+
118
+ desc 'gems [GEM[=VERSION]]', 'Cache documentation for installed gems'
119
+ option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false
120
+ # @return [void]
121
+ def gems *names
122
+ if names.empty?
123
+ Gem::Specification.to_a.each { |spec| do_cache spec }
124
+ else
125
+ names.each do |name|
126
+ spec = Gem::Specification.find_by_name(*name.split('='))
127
+ do_cache spec
128
+ rescue Gem::MissingSpecError
129
+ warn "Gem '#{name}' not found"
122
130
  end
123
131
  end
124
132
  end
125
133
 
126
134
  desc 'reporters', 'Get a list of diagnostics reporters'
135
+ # @return [void]
127
136
  def reporters
128
137
  puts Solargraph::Diagnostics.reporters
129
138
  end
@@ -137,9 +146,10 @@ module Solargraph
137
146
  )
138
147
  option :level, type: :string, aliases: [:mode, :m, :l], desc: 'Type checking level', default: 'normal'
139
148
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
149
+ # @return [void]
140
150
  def typecheck *files
141
151
  directory = File.realpath(options[:directory])
142
- api_map = Solargraph::ApiMap.load(directory)
152
+ api_map = Solargraph::ApiMap.load_with_cache(directory)
143
153
  if files.empty?
144
154
  files = api_map.source_maps.map(&:filename)
145
155
  else
@@ -157,6 +167,7 @@ module Solargraph
157
167
  probcount += problems.length
158
168
  end
159
169
  puts "#{probcount} problem#{probcount != 1 ? 's' : ''} found#{files.length != 1 ? " in #{filecount} of #{files.length} files" : ''}."
170
+ # "
160
171
  exit 1 if probcount > 0
161
172
  end
162
173
 
@@ -169,12 +180,13 @@ module Solargraph
169
180
  )
170
181
  option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
171
182
  option :verbose, type: :boolean, aliases: :v, desc: 'Verbose output', default: false
183
+ # @return [void]
172
184
  def scan
173
185
  require 'benchmark'
174
186
  directory = File.realpath(options[:directory])
175
187
  api_map = nil
176
188
  time = Benchmark.measure {
177
- api_map = Solargraph::ApiMap.load(directory)
189
+ api_map = Solargraph::ApiMap.load_with_cache(directory)
178
190
  api_map.pins.each do |pin|
179
191
  begin
180
192
  puts pin_description(pin) if options[:verbose]
@@ -191,18 +203,14 @@ module Solargraph
191
203
  puts "Scanned #{directory} (#{api_map.pins.length} pins) in #{time.real} seconds."
192
204
  end
193
205
 
194
- desc 'bundle', 'Generate documentation for bundled gems'
195
- option :directory, type: :string, aliases: :d, desc: 'The workspace directory', default: '.'
196
- option :rebuild, type: :boolean, aliases: :r, desc: 'Rebuild existing documentation', default: false
197
- def bundle
198
- Documentor.new(options[:directory], rebuild: options[:rebuild], out: STDOUT).document
199
- end
200
-
201
- desc 'rdoc GEM [VERSION]', 'Use RDoc to cache documentation'
202
- def rdoc gem, version = '>= 0'
203
- spec = Gem::Specification.find_by_name(gem, version)
204
- puts "Caching #{spec.name} #{spec.version} from RDoc"
205
- Solargraph::YardMap::RdocToYard.run(spec)
206
+ desc 'list', 'List the files in the workspace and the total count'
207
+ option :count, type: :boolean, aliases: :c, desc: 'Display the file count only', default: false
208
+ option :directory, type: :string, aliases: :d, desc: 'The directory to read', default: '.'
209
+ # @return [void]
210
+ def list
211
+ workspace = Solargraph::Workspace.new(options[:directory])
212
+ puts workspace.filenames unless options[:count]
213
+ puts "#{workspace.filenames.length} files total."
206
214
  end
207
215
 
208
216
  private
@@ -222,5 +230,18 @@ module Solargraph
222
230
  desc += " (#{pin.location.filename} #{pin.location.range.start.line})" if pin.location
223
231
  desc
224
232
  end
233
+
234
+ # @param gemspec [Gem::Specification]
235
+ # @return [void]
236
+ def do_cache gemspec
237
+ cached = Yardoc.cached?(gemspec)
238
+ if cached && !options.rebuild
239
+ puts "Cache already exists for #{gemspec.name} #{gemspec.version}"
240
+ else
241
+ puts "#{cached ? 'Rebuilding' : 'Caching'} gem documentation for #{gemspec.name} #{gemspec.version}"
242
+ pins = GemPins.build(gemspec)
243
+ Cache.save('gems', "#{gemspec.name}-#{gemspec.version}.ser", pins)
244
+ end
245
+ end
225
246
  end
226
247
  end
@@ -0,0 +1,32 @@
1
+ module Solargraph
2
+ class Source
3
+ class Chain
4
+ class Array < Literal
5
+ # @param children [::Array<Chain>]
6
+ def initialize children
7
+ super('::Array')
8
+ @children = children
9
+ end
10
+
11
+ def word
12
+ @word ||= "<#{@type}>"
13
+ end
14
+
15
+ # @param api_map [ApiMap]
16
+ # @param name_pin [Pin::Base]
17
+ # @param locals [Enumerable<Pin::LocalVariable>]
18
+ def resolve api_map, name_pin, locals
19
+ child_types = @children.map do |child|
20
+ child.infer(api_map, name_pin, locals).tag
21
+ end
22
+ type = if child_types.uniq.length == 1 && child_types.first != 'undefined'
23
+ "::Array<#{child_types.first}>"
24
+ else
25
+ '::Array'
26
+ end
27
+ [Pin::ProxyType.anonymous(ComplexType.try_parse(type))]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class Source
5
+ class Chain
6
+ class BlockSymbol < Link
7
+ def resolve api_map, name_pin, locals
8
+ [Pin::ProxyType.anonymous(ComplexType.try_parse('Proc'))]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -7,97 +7,100 @@ module Solargraph
7
7
  # @return [String]
8
8
  attr_reader :word
9
9
 
10
- # @return [Array<Chain>]
10
+ # @return [::Array<Chain>]
11
11
  attr_reader :arguments
12
12
 
13
+ # @return [Chain, nil]
14
+ attr_reader :block
15
+
13
16
  # @param word [String]
14
- # @param arguments [Array<Chain>]
15
- # @param with_block [Boolean] True if the chain is inside a block
16
- # @param head [Boolean] True if the call is the start of its chain
17
- def initialize word, arguments = [], with_block = false
17
+ # @param arguments [::Array<Chain>]
18
+ # @param block [Chain, nil]
19
+ def initialize word, arguments = [], block = nil
18
20
  @word = word
19
21
  @arguments = arguments
20
- @with_block = with_block
22
+ @block = block
23
+ fix_block_pass
21
24
  end
22
25
 
23
26
  def with_block?
24
- @with_block
27
+ !!@block
25
28
  end
26
29
 
27
30
  # @param api_map [ApiMap]
28
31
  # @param name_pin [Pin::Base]
29
- # @param locals [Array<Pin::Base>]
32
+ # @param locals [::Array<Pin::LocalVariable>]
30
33
  def resolve api_map, name_pin, locals
31
34
  return super_pins(api_map, name_pin) if word == 'super'
35
+ return yield_pins(api_map, name_pin) if word == 'yield'
32
36
  found = if head?
33
37
  locals.select { |p| p.name == word }
34
38
  else
35
39
  []
36
40
  end
37
41
  return inferred_pins(found, api_map, name_pin.context, locals) unless found.empty?
38
- pins = api_map.get_method_stack(name_pin.binder.namespace, word, scope: name_pin.binder.scope)
42
+ # @param [ComplexType::UniqueType]
43
+ pins = name_pin.binder.each_unique_type.flat_map do |context|
44
+ api_map.get_method_stack(context.namespace == '' ? '' : context.tag, word, scope: context.scope)
45
+ end
39
46
  return [] if pins.empty?
40
47
  inferred_pins(pins, api_map, name_pin.context, locals)
41
48
  end
42
49
 
43
50
  private
44
51
 
45
- # @param pins [Array<Pin::Base>]
52
+ # @param pins [::Enumerable<Pin::Method>]
46
53
  # @param api_map [ApiMap]
47
54
  # @param context [ComplexType]
48
- # @param locals [Pin::LocalVariable]
49
- # @return [Array<Pin::Base>]
55
+ # @param locals [::Array<Pin::LocalVariable>]
56
+ # @return [::Array<Pin::Base>]
50
57
  def inferred_pins pins, api_map, context, locals
51
58
  result = pins.map do |p|
52
- overloads = p.docstring.tags(:overload)
59
+ next p unless p.is_a?(Pin::Method)
60
+ overloads = p.signatures
53
61
  # next p if overloads.empty?
54
62
  type = ComplexType::UNDEFINED
55
- # @param [YARD::Tags::OverloadTag]
56
- overloads.each do |ol|
57
- next unless arguments_match(arguments, ol.parameters)
58
- next if ol.parameters.last && ol.parameters.last.first.start_with?('&') && ol.parameters.last.last.nil? && !with_block?
63
+ # start with overloads that require blocks; if we are
64
+ # passing a block, we want to find a signature that will
65
+ # use it. If we didn't pass a block, the logic below will
66
+ # reject it regardless
67
+
68
+ sorted_overloads = overloads.sort { |ol| ol.block? ? -1 : 1 }
69
+ new_signature_pin = nil
70
+ sorted_overloads.each do |ol|
71
+ next unless arity_matches?(arguments, ol)
59
72
  match = true
73
+
74
+ atypes = []
60
75
  arguments.each_with_index do |arg, idx|
61
- achain = arguments[idx]
62
- next if achain.nil?
63
76
  param = ol.parameters[idx]
64
77
  if param.nil?
65
- match = false unless ol.parameters.last && ol.parameters.last.first.start_with?('*')
78
+ match = ol.parameters.any?(&:restarg?)
66
79
  break
67
80
  end
68
- par = ol.tags(:param).select { |pp| pp.name == param.first }.first
69
- next if par.nil? || par.types.nil? || par.types.empty?
70
- atype = achain.infer(api_map, Pin::ProxyType.anonymous(context), locals)
71
- other = ComplexType.try_parse(*par.types)
81
+ atype = atypes[idx] ||= arg.infer(api_map, Pin::ProxyType.anonymous(context), locals)
72
82
  # @todo Weak type comparison
73
- unless atype.tag == other.tag || api_map.super_and_sub?(other.tag, atype.tag)
83
+ # unless atype.tag == param.return_type.tag || api_map.super_and_sub?(param.return_type.tag, atype.tag)
84
+ unless param.return_type.undefined? || atype.name == param.return_type.name || api_map.super_and_sub?(param.return_type.name, atype.name) || param.return_type.generic?
74
85
  match = false
75
86
  break
76
87
  end
77
88
  end
78
89
  if match
79
- type = extra_return_type(ol, context)
80
- break if type
81
- type = ComplexType.try_parse(*ol.tag(:return).types).self_to(context.to_s).qualify(api_map, context.namespace) if ol.has_tag?(:return) && ol.tag(:return).types && !ol.tag(:return).types.empty? && (type.nil? || type.undefined?)
90
+ if ol.block && with_block?
91
+ block_atypes = ol.block.parameters.map(&:return_type)
92
+ blocktype = block_call_type(api_map, context, block_atypes, locals)
93
+ end
94
+ new_signature_pin = ol.resolve_generics_from_context_until_complete(ol.generics, atypes, nil, nil, blocktype)
95
+ new_return_type = new_signature_pin.return_type
96
+ type = with_params(new_return_type.self_to(context.to_s), context).qualify(api_map, context.namespace) if new_return_type.defined?
82
97
  type ||= ComplexType::UNDEFINED
83
98
  end
84
99
  break if type.defined?
85
100
  end
101
+ p = p.with_single_signature(new_signature_pin) unless new_signature_pin.nil?
86
102
  next p.proxy(type) if type.defined?
87
- type = extra_return_type(p.docstring, context)
88
- if type
89
- next Solargraph::Pin::Method.new(
90
- location: p.location,
91
- closure: p.closure,
92
- name: p.name,
93
- comments: "@return [#{context.subtypes.first.to_s}]",
94
- scope: p.scope,
95
- visibility: p.visibility,
96
- parameters: p.parameters,
97
- node: p.node
98
- )
99
- end
100
- if p.is_a?(Pin::Method) && !p.macros.empty?
103
+ if !p.macros.empty?
101
104
  result = process_macro(p, api_map, context, locals)
102
105
  next result unless result.return_type.undefined?
103
106
  elsif !p.directives.empty?
@@ -107,19 +110,29 @@ module Solargraph
107
110
  p
108
111
  end
109
112
  result.map do |pin|
110
- next pin if pin.return_type.undefined?
111
- selfy = pin.return_type.self_to(context.tag)
112
- selfy == pin.return_type ? pin : pin.proxy(selfy)
113
+ if pin.path == 'Class#new' && context.tag != 'Class'
114
+ pin.proxy(ComplexType.try_parse(context.namespace))
115
+ else
116
+ next pin if pin.return_type.undefined?
117
+ selfy = pin.return_type.self_to(context.tag)
118
+ selfy == pin.return_type ? pin : pin.proxy(selfy)
119
+ end
113
120
  end
114
121
  end
115
122
 
116
- # @param pin [Pin::Method]
123
+ # @param pin [Pin::Base]
117
124
  # @param api_map [ApiMap]
118
125
  # @param context [ComplexType]
119
- # @param locals [Pin::Base]
126
+ # @param locals [Enumerable<Pin::Base>]
120
127
  # @return [Pin::Base]
121
128
  def process_macro pin, api_map, context, locals
122
129
  pin.macros.each do |macro|
130
+ # @todo 'Wrong argument type for
131
+ # Solargraph::Source::Chain::Call#inner_process_macro:
132
+ # macro expected YARD::Tags::MacroDirective, received
133
+ # generic<Elem>' is because we lose 'rooted' information
134
+ # in the 'Chain::Array' class internally, leaving
135
+ # ::Array#each shadowed when it shouldn't be.
123
136
  result = inner_process_macro(pin, macro, api_map, context, locals)
124
137
  return result unless result.return_type.undefined?
125
138
  end
@@ -129,7 +142,7 @@ module Solargraph
129
142
  # @param pin [Pin::Method]
130
143
  # @param api_map [ApiMap]
131
144
  # @param context [ComplexType]
132
- # @param locals [Pin::Base]
145
+ # @param locals [Enumerable<Pin::Base>]
133
146
  # @return [Pin::ProxyType]
134
147
  def process_directive pin, api_map, context, locals
135
148
  pin.directives.each do |dir|
@@ -141,11 +154,11 @@ module Solargraph
141
154
  Pin::ProxyType.anonymous ComplexType::UNDEFINED
142
155
  end
143
156
 
144
- # @param pin [Pin]
157
+ # @param pin [Pin::Base]
145
158
  # @param macro [YARD::Tags::MacroDirective]
146
159
  # @param api_map [ApiMap]
147
160
  # @param context [ComplexType]
148
- # @param locals [Array<Pin::Base>]
161
+ # @param locals [Enumerable<Pin::Base>]
149
162
  # @return [Pin::ProxyType]
150
163
  def inner_process_macro pin, macro, api_map, context, locals
151
164
  vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
@@ -169,35 +182,86 @@ module Solargraph
169
182
 
170
183
  # @param docstring [YARD::Docstring]
171
184
  # @param context [ComplexType]
172
- # @return [ComplexType]
185
+ # @return [ComplexType, nil]
173
186
  def extra_return_type docstring, context
174
- if docstring.has_tag?(:return_single_parameter) #&& context.subtypes.one?
187
+ if docstring.has_tag?('return_single_parameter') #&& context.subtypes.one?
175
188
  return context.subtypes.first || ComplexType::UNDEFINED
176
- elsif docstring.has_tag?(:return_value_parameter) && context.value_types.one?
189
+ elsif docstring.has_tag?('return_value_parameter') && context.value_types.one?
177
190
  return context.value_types.first
178
191
  end
179
192
  nil
180
193
  end
181
194
 
182
- # @param arguments [Array<Chain>]
183
- # @param parameters [Array<String>]
195
+ # @param arguments [::Array<Chain>]
196
+ # @param signature [Pin::Signature]
184
197
  # @return [Boolean]
185
- def arguments_match arguments, parameters
198
+ def arity_matches? arguments, signature
199
+ parameters = signature.parameters
186
200
  argcount = arguments.length
187
- # argcount -= 1 if !arguments.empty? && arguments.last.links.first.word.start_with?('&')
188
201
  parcount = parameters.length
189
- parcount -= 1 if !parameters.empty? && parameters.last.first.start_with?('&')
190
- return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.first.start_with?('*'))
202
+ parcount -= 1 if !parameters.empty? && parameters.last.block?
203
+ return false if signature.block? && !with_block?
204
+ return false if argcount < parcount && !(argcount == parcount - 1 && parameters.last.restarg?)
191
205
  true
192
206
  end
193
207
 
194
208
  # @param api_map [ApiMap]
195
209
  # @param name_pin [Pin::Base]
196
- # @return [Array<Pin::Base>]
210
+ # @return [::Array<Pin::Base>]
197
211
  def super_pins api_map, name_pin
198
- pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.scope)
212
+ pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.context.scope)
199
213
  pins.reject{|p| p.path == name_pin.path}
200
214
  end
215
+
216
+ # @param api_map [ApiMap]
217
+ # @param name_pin [Pin::Base]
218
+ # @return [::Array<Pin::Base>]
219
+ def yield_pins api_map, name_pin
220
+ method_pin = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.context.scope).first
221
+ return [] if method_pin.nil?
222
+
223
+ method_pin.signatures.map(&:block).compact
224
+ end
225
+
226
+ # @param type [ComplexType]
227
+ # @param context [ComplexType]
228
+ # @return [ComplexType]
229
+ def with_params type, context
230
+ return type unless type.to_s.include?('$')
231
+ ComplexType.try_parse(type.to_s.gsub('$', context.value_types.map(&:tag).join(', ')).gsub('<>', ''))
232
+ end
233
+
234
+ # @return [void]
235
+ def fix_block_pass
236
+ argument = @arguments.last&.links&.first
237
+ @block = @arguments.pop if argument.is_a?(BlockSymbol) || argument.is_a?(BlockVariable)
238
+ end
239
+
240
+ # @param api_map [ApiMap]
241
+ # @param context [ComplexType]
242
+ # @param block_parameter_types [::Array<ComplexType>]
243
+ # @param locals [::Array<Pin::LocalVariable>]
244
+ # @return [ComplexType, nil]
245
+ def block_call_type(api_map, context, block_parameter_types, locals)
246
+ return nil unless with_block?
247
+
248
+ # @todo Handle BlockVariable
249
+ if block.links.map(&:class) == [BlockSymbol]
250
+ # Ruby's shorthand for sending the passed in method name
251
+ # to the first yield parameter with no arguments
252
+ block_symbol_name = block.links.first.word
253
+ block_symbol_call_path = "#{block_parameter_types.first}##{block_symbol_name}"
254
+ callee = api_map.get_path_pins(block_symbol_call_path).first
255
+ return_type = callee&.return_type
256
+ # @todo: Figure out why we get unresolved generics at
257
+ # this point and need to assume method return types
258
+ # based on the generic type
259
+ return_type ||= api_map.get_path_pins("#{context.subtypes.first}##{block.links.first.word}").first&.return_type
260
+ return_type || ComplexType::UNDEFINED
261
+ else
262
+ block.infer(api_map, Pin::ProxyType.anonymous(context), locals)
263
+ end
264
+ end
201
265
  end
202
266
  end
203
267
  end