diff --git a/lib/solargraph/api_map/source_to_yard.rb b/lib/solargraph/api_map/source_to_yard.rb index 5e7784f01..f92a7a351 100644 --- a/lib/solargraph/api_map/source_to_yard.rb +++ b/lib/solargraph/api_map/source_to_yard.rb @@ -45,12 +45,14 @@ def rake_yard store code_object_map[pin.path].docstring = pin.docstring store.get_includes(pin.path).each do |ref| include_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject) - include_object.instance_mixins.push code_object_map[ref] unless include_object.nil? or include_object.nil? + code_object = code_object_map[ref] + include_object.instance_mixins.push code_object_map[ref] if include_object && code_object end store.get_extends(pin.path).each do |ref| extend_object = code_object_at(pin.path, YARD::CodeObjects::ClassObject) - extend_object.instance_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? - extend_object.class_mixins.push code_object_map[ref] unless extend_object.nil? or extend_object.nil? + code_object = code_object_map[ref] + extend_object.instance_mixins.push code_object_map[ref] if extend_object && code_object + extend_object.class_mixins.push code_object_map[ref] if extend_object && code_object end end store.method_pins.each do |pin| diff --git a/lib/solargraph/doc_map.rb b/lib/solargraph/doc_map.rb index bb93b52e7..dab23a294 100644 --- a/lib/solargraph/doc_map.rb +++ b/lib/solargraph/doc_map.rb @@ -147,6 +147,7 @@ def resolve_path_to_gemspecs path gemspec = Gem::Specification.find_by_path(path) if gemspec.nil? gem_name_guess = path.split('/').first + return nil if gem_name_guess.to_s.empty? begin # this can happen when the gem is included via a local path in # a Gemfile; Gem doesn't try to index the paths in that case. diff --git a/lib/solargraph/rbs_map.rb b/lib/solargraph/rbs_map.rb index b0b4b470d..35494a592 100644 --- a/lib/solargraph/rbs_map.rb +++ b/lib/solargraph/rbs_map.rb @@ -86,6 +86,8 @@ def conversions # @return [Boolean] true if adding the library succeeded def add_library loader, library, version @resolved = if loader.has_library?(library: library, version: version) + # @todo Typecheck thinks path keyword param is required + # @sg-ignore loader.add library: library, version: version Solargraph.logger.info "#{short_name} successfully loaded library #{library}" true diff --git a/lib/solargraph/shell.rb b/lib/solargraph/shell.rb index d08e575c1..521c427f5 100755 --- a/lib/solargraph/shell.rb +++ b/lib/solargraph/shell.rb @@ -3,10 +3,13 @@ require 'benchmark' require 'thor' require 'yard' +require 'sord' +require 'tmpdir' module Solargraph class Shell < Thor include Solargraph::ServerMethods + include ApiMap::SourceToYard # Tell Thor to ensure the process exits with status 1 if any error happens. def self.exit_on_failure? @@ -236,6 +239,75 @@ def list puts "#{workspace.filenames.length} files total." end + desc 'cache', 'Cache a gem', hide: true + # @return [void] + # @param gem [String] + # @param version [String, nil] + def cache gem, version = nil + spec = Gem::Specification.find_by_name(gem, version) + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + end + + desc 'gems', 'Cache documentation for installed gems' + option :rebuild, type: :boolean, desc: 'Rebuild existing documentation', default: false + # @return [void] + def gems *names + if names.empty? + Gem::Specification.to_a.each do |spec| + next unless options.rebuild || !Yardoc.cached?(spec) + + puts "Processing gem: #{spec.name} #{spec.version}" + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + end + else + names.each do |name| + spec = Gem::Specification.find_by_name(name) + if spec + next unless options.rebuild || !Yardoc.cached?(spec) + + puts "Processing gem: #{spec.name} #{spec.version}" + pins = GemPins.build(spec) + Cache.save('gems', "#{spec.name}-#{spec.version}.ser", pins) + else + warn "Gem '#{name}' not found" + end + end + end + end + + desc 'rbs', 'Generate RBS definitions' + option :filename, type: :string, alias: :f, desc: 'Generated file name', default: 'sig.rbs' + option :inference, type: :boolean, desc: 'Enhance definitions with type inference', default: true + def rbs + api_map = Solargraph::ApiMap.load('.') + pins = api_map.source_maps.flat_map(&:pins) + store = Solargraph::ApiMap::Store.new(pins) + if options[:inference] + store.method_pins.each do |pin| + if pin.return_type.undefined? + type = pin.typify(api_map) + type = pin.probe(api_map) if type.undefined? + pin.docstring.add_tag YARD::Tags::Tag.new('return', nil, type.items.map(&:to_s)) + pin.instance_variable_set(:@return_type, type) + end + end + end + rake_yard(store) + work_dir = Dir.pwd + Dir.mktmpdir do |tmpdir| + Dir.chdir tmpdir do + yardoc = File.join(tmpdir, '.yardoc') + YARD::Registry.save(false, yardoc) + YARD::Registry.load(yardoc) + target = File.join(work_dir, 'sig', options[:filename]) + FileUtils.mkdir_p(File.join(work_dir, 'sig')) + `sord #{target} --rbs --no-regenerate` + end + end + end + private # @param pin [Solargraph::Pin::Base] diff --git a/solargraph.gemspec b/solargraph.gemspec index 5008b6247..cdaf967b0 100755 --- a/solargraph.gemspec +++ b/solargraph.gemspec @@ -38,6 +38,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'rbs', '~> 3.3' s.add_runtime_dependency 'reverse_markdown', '~> 3.0' s.add_runtime_dependency 'rubocop', '~> 1.38' + s.add_runtime_dependency 'sord', '~> 7.0' s.add_runtime_dependency 'thor', '~> 1.0' s.add_runtime_dependency 'tilt', '~> 2.0' s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24'