solargraph
Advanced tools
| # frozen_string_literal: true | ||
| module Solargraph | ||
| module Convention | ||
| module DataDefinition | ||
| autoload :DataDefintionNode, 'solargraph/convention/data_definition/data_definition_node' | ||
| autoload :DataAssignmentNode, 'solargraph/convention/data_definition/data_assignment_node' | ||
| module NodeProcessors | ||
| class DataNode < Parser::NodeProcessor::Base | ||
| # @return [Boolean] continue processing the next processor of the same node. | ||
| def process | ||
| return true if data_definition_node.nil? | ||
| loc = get_node_location(node) | ||
| nspin = Solargraph::Pin::Namespace.new( | ||
| type: :class, | ||
| location: loc, | ||
| closure: region.closure, | ||
| name: data_definition_node.class_name, | ||
| comments: comments_for(node), | ||
| visibility: :public, | ||
| gates: region.closure.gates.freeze | ||
| ) | ||
| pins.push nspin | ||
| # define initialize method | ||
| initialize_method_pin = Pin::Method.new( | ||
| name: 'initialize', | ||
| parameters: [], | ||
| scope: :instance, | ||
| location: get_node_location(node), | ||
| closure: nspin, | ||
| visibility: :private, | ||
| comments: comments_for(node) | ||
| ) | ||
| # TODO: Support both arg and kwarg initializers for Data.define | ||
| # Solargraph::SourceMap::Clip#complete_keyword_parameters does not seem to currently take into account [Pin::Method#signatures] hence we only one for :kwarg | ||
| pins.push initialize_method_pin | ||
| data_definition_node.attributes.map do |attribute_node, attribute_name| | ||
| initialize_method_pin.parameters.push( | ||
| Pin::Parameter.new( | ||
| name: attribute_name, | ||
| decl: :kwarg, | ||
| location: get_node_location(attribute_node), | ||
| closure: initialize_method_pin | ||
| ) | ||
| ) | ||
| end | ||
| # define attribute readers and instance variables | ||
| data_definition_node.attributes.each do |attribute_node, attribute_name| | ||
| name = attribute_name.to_s | ||
| method_pin = Pin::Method.new( | ||
| name: name, | ||
| parameters: [], | ||
| scope: :instance, | ||
| location: get_node_location(attribute_node), | ||
| closure: nspin, | ||
| comments: attribute_comments(attribute_node, attribute_name), | ||
| visibility: :public | ||
| ) | ||
| pins.push method_pin | ||
| pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}", | ||
| closure: method_pin, | ||
| location: get_node_location(attribute_node), | ||
| comments: attribute_comments(attribute_node, attribute_name)) | ||
| end | ||
| process_children region.update(closure: nspin, visibility: :public) | ||
| false | ||
| end | ||
| private | ||
| # @return [DataDefintionNode, nil] | ||
| def data_definition_node | ||
| @data_definition_node ||= if DataDefintionNode.match?(node) | ||
| DataDefintionNode.new(node) | ||
| elsif DataAssignmentNode.match?(node) | ||
| DataAssignmentNode.new(node) | ||
| end | ||
| end | ||
| # @param attribute_node [Parser::AST::Node] | ||
| # @return [String, nil] | ||
| def attribute_comments(attribute_node, attribute_name) | ||
| data_comments = comments_for(attribute_node) | ||
| return if data_comments.nil? || data_comments.empty? | ||
| data_comments.split("\n").find do |row| | ||
| row.include?(attribute_name) | ||
| end&.gsub('@param', '@return')&.gsub(attribute_name, '') | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| # frozen_string_literal: true | ||
| module Solargraph | ||
| module Convention | ||
| module DataDefinition | ||
| # A node wrapper for a Data definition via const assignment. | ||
| # @example | ||
| # MyData = Data.new(:bar, :baz) do | ||
| # def foo | ||
| # end | ||
| # end | ||
| class DataAssignmentNode < DataDefintionNode | ||
| class << self | ||
| # @example | ||
| # s(:casgn, nil, :Foo, | ||
| # s(:block, | ||
| # s(:send, | ||
| # s(:const, nil, :Data), :define, | ||
| # s(:sym, :bar), | ||
| # s(:sym, :baz)), | ||
| # s(:args), | ||
| # s(:def, :foo, | ||
| # s(:args), | ||
| # s(:send, nil, :bar)))) | ||
| def match?(node) | ||
| return false unless node&.type == :casgn | ||
| return false if node.children[2].nil? | ||
| data_node = if node.children[2].type == :block | ||
| node.children[2].children[0] | ||
| else | ||
| node.children[2] | ||
| end | ||
| data_definition_node?(data_node) | ||
| end | ||
| end | ||
| def class_name | ||
| if node.children[0] | ||
| Parser::NodeMethods.unpack_name(node.children[0]) + "::#{node.children[1]}" | ||
| else | ||
| node.children[1].to_s | ||
| end | ||
| end | ||
| private | ||
| # @return [Parser::AST::Node] | ||
| def data_node | ||
| if node.children[2].type == :block | ||
| node.children[2].children[0] | ||
| else | ||
| node.children[2] | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| # frozen_string_literal: true | ||
| module Solargraph | ||
| module Convention | ||
| module DataDefinition | ||
| # A node wrapper for a Data definition via inheritance. | ||
| # @example | ||
| # class MyData < Data.new(:bar, :baz) | ||
| # def foo | ||
| # end | ||
| # end | ||
| class DataDefintionNode | ||
| class << self | ||
| # @example | ||
| # s(:class, | ||
| # s(:const, nil, :Foo), | ||
| # s(:send, | ||
| # s(:const, nil, :Data), :define, | ||
| # s(:sym, :bar), | ||
| # s(:sym, :baz)), | ||
| # s(:hash, | ||
| # s(:pair, | ||
| # s(:sym, :keyword_init), | ||
| # s(:true)))), | ||
| # s(:def, :foo, | ||
| # s(:args), | ||
| # s(:send, nil, :bar))) | ||
| def match?(node) | ||
| return false unless node&.type == :class | ||
| data_definition_node?(node.children[1]) | ||
| end | ||
| private | ||
| # @param data_node [Parser::AST::Node] | ||
| # @return [Boolean] | ||
| def data_definition_node?(data_node) | ||
| return false unless data_node.is_a?(::Parser::AST::Node) | ||
| return false unless data_node&.type == :send | ||
| return false unless data_node.children[0]&.type == :const | ||
| return false unless data_node.children[0].children[1] == :Data | ||
| return false unless data_node.children[1] == :define | ||
| true | ||
| end | ||
| end | ||
| # @return [Parser::AST::Node] | ||
| def initialize(node) | ||
| @node = node | ||
| end | ||
| # @return [String] | ||
| def class_name | ||
| Parser::NodeMethods.unpack_name(node) | ||
| end | ||
| # @return [Array<Array(Parser::AST::Node, String)>] | ||
| def attributes | ||
| data_attribute_nodes.map do |data_def_param| | ||
| next unless data_def_param.type == :sym | ||
| [data_def_param, data_def_param.children[0].to_s] | ||
| end.compact | ||
| end | ||
| # @return [Parser::AST::Node] | ||
| def body_node | ||
| node.children[2] | ||
| end | ||
| private | ||
| # @return [Parser::AST::Node] | ||
| attr_reader :node | ||
| # @return [Parser::AST::Node] | ||
| def data_node | ||
| node.children[1] | ||
| end | ||
| # @return [Array<Parser::AST::Node>] | ||
| def data_attribute_nodes | ||
| data_node.children[2..-1] | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
+8
-0
@@ -0,1 +1,9 @@ | ||
| ## 0.56.2 - July 29, 2025 | ||
| - Add support for Ruby Data.define (#970) | ||
| - Ensure that pin locations are always populated (#965) | ||
| - Improve struct support (#992) | ||
| - Include Rakefile, Gemfile, and gemspec files in config by default (#984) | ||
| - Use Open3 to cache gemspecs (#1000) | ||
| - Eager load node processors (#1002) | ||
| ## 0.56.1 - July 13, 2025 | ||
@@ -2,0 +10,0 @@ - Library avoids blocking on pending yardoc caches (#990) |
+13
-0
@@ -68,2 +68,6 @@ # frozen_string_literal: true | ||
| # @param type [Symbol] The type of assertion to perform. | ||
| # @param msg [String, nil] An optional message to log | ||
| # @param block [Proc] A block that returns a message to log | ||
| # @return [void] | ||
| def self.assert_or_log(type, msg = nil, &block) | ||
@@ -83,2 +87,7 @@ raise (msg || block.call) if asserts_on?(type) && ![:combine_with_visibility].include?(type) | ||
| # Bundler.with_clean_env for earlier versions of Bundler. | ||
| # | ||
| # @generic T | ||
| # @yieldreturn [generic<T>] | ||
| # @sg-ignore dynamic call, but both functions behave the same | ||
| # @return [generic<T>] | ||
| def self.with_clean_env &block | ||
@@ -93,1 +102,5 @@ meth = if Bundler.respond_to?(:with_original_env) | ||
| end | ||
| # Ensure that ParserGem node processors are properly loaded to avoid conflicts | ||
| # with Convention node processors | ||
| require 'solargraph/parser/parser_gem/node_processors' |
@@ -14,2 +14,3 @@ # frozen_string_literal: true | ||
| autoload :StructDefinition, 'solargraph/convention/struct_definition' | ||
| autoload :DataDefinition, 'solargraph/convention/data_definition' | ||
@@ -16,0 +17,0 @@ # @type [Set<Convention::Base>] |
@@ -15,3 +15,4 @@ # frozen_string_literal: true | ||
| @yieldparam [self] | ||
| ) | ||
| ), | ||
| source: :gemspec | ||
| ) | ||
@@ -18,0 +19,0 @@ ] |
@@ -11,4 +11,5 @@ # frozen_string_literal: true | ||
| class StructNode < Parser::NodeProcessor::Base | ||
| # @return [Boolean] continue processing the next processor of the same node. | ||
| def process | ||
| return if struct_definition_node.nil? | ||
| return true if struct_definition_node.nil? | ||
@@ -21,3 +22,3 @@ loc = get_node_location(node) | ||
| name: struct_definition_node.class_name, | ||
| comments: comments_for(node), | ||
| docstring: docstring, | ||
| visibility: :public, | ||
@@ -36,3 +37,3 @@ gates: region.closure.gates.freeze | ||
| visibility: :private, | ||
| comments: comments_for(node) | ||
| docstring: docstring | ||
| ) | ||
@@ -56,2 +57,4 @@ | ||
| [attribute_name, "#{attribute_name}="].each do |name| | ||
| docs = docstring.tags.find { |t| t.tag_name == 'param' && t.name == attribute_name } | ||
| method_pin = Pin::Method.new( | ||
@@ -63,13 +66,22 @@ name: name, | ||
| closure: nspin, | ||
| comments: attribute_comments(attribute_node, attribute_name), | ||
| # even assignments return the value | ||
| comments: attribute_comment(docs, false), | ||
| visibility: :public | ||
| ) | ||
| if name.end_with?('=') | ||
| method_pin.parameters << Pin::Parameter.new( | ||
| name: attribute_name, | ||
| location: get_node_location(attribute_node), | ||
| closure: nspin, | ||
| comments: attribute_comment(docs, true) | ||
| ) | ||
| pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}", | ||
| closure: method_pin, | ||
| location: get_node_location(attribute_node), | ||
| comments: attribute_comment(docs, false)) | ||
| end | ||
| pins.push method_pin | ||
| next unless name.include?('=') # setter | ||
| pins.push Pin::InstanceVariable.new(name: "@#{attribute_name}", | ||
| closure: method_pin, | ||
| location: get_node_location(attribute_node), | ||
| comments: attribute_comments(attribute_node, attribute_name)) | ||
| end | ||
@@ -79,2 +91,3 @@ end | ||
| process_children region.update(closure: nspin, visibility: :public) | ||
| false | ||
| end | ||
@@ -86,5 +99,5 @@ | ||
| def struct_definition_node | ||
| @struct_definition_node ||= if StructDefintionNode.valid?(node) | ||
| @struct_definition_node ||= if StructDefintionNode.match?(node) | ||
| StructDefintionNode.new(node) | ||
| elsif StructAssignmentNode.valid?(node) | ||
| elsif StructAssignmentNode.match?(node) | ||
| StructAssignmentNode.new(node) | ||
@@ -94,12 +107,39 @@ end | ||
| # @param attribute_node [Parser::AST::Node] | ||
| # @return [String, nil] | ||
| def attribute_comments(attribute_node, attribute_name) | ||
| struct_comments = comments_for(attribute_node) | ||
| return if struct_comments.nil? || struct_comments.empty? | ||
| # Gets/generates the relevant docstring for this struct & it's attributes | ||
| # @return [YARD::Docstring] | ||
| def docstring | ||
| @docstring ||= parse_comments | ||
| end | ||
| struct_comments.split("\n").find do |row| | ||
| row.include?(attribute_name) | ||
| end&.gsub('@param', '@return')&.gsub(attribute_name, '') | ||
| # Parses any relevant comments for a struct int a yard docstring | ||
| # @return [YARD::Docstring] | ||
| def parse_comments | ||
| struct_comments = comments_for(node) || '' | ||
| struct_definition_node.attributes.each do |attr_node, attr_name| | ||
| comment = comments_for(attr_node) | ||
| next if comment.nil? | ||
| # We should support specific comments for an attribute, and that can be either a @return on an @param | ||
| # But since we merge into the struct_comments, then we should interpret either as a param | ||
| comment = '@param ' + attr_name + comment[7..] if comment.start_with?('@return') | ||
| struct_comments += "\n#{comment}" | ||
| end | ||
| Solargraph::Source.parse_docstring(struct_comments).to_docstring | ||
| end | ||
| # @param tag [YARD::Tags::Tag, nil] The param tag for this attribute. If nil, this method is a no-op | ||
| # @param for_setter [Boolean] If true, will return a @param tag instead of a @return tag | ||
| def attribute_comment(tag, for_setter) | ||
| return "" if tag.nil? | ||
| suffix = "[#{tag.types&.join(',') || 'undefined'}] #{tag.text}" | ||
| if for_setter | ||
| "@param #{tag.name} #{suffix}" | ||
| else | ||
| "@return #{suffix}" | ||
| end | ||
| end | ||
| end | ||
@@ -106,0 +146,0 @@ end |
@@ -25,3 +25,3 @@ # frozen_string_literal: true | ||
| # s(:send, nil, :bar)))) | ||
| def valid?(node) | ||
| def match?(node) | ||
| return false unless node&.type == :casgn | ||
@@ -28,0 +28,0 @@ return false if node.children[2].nil? |
@@ -28,3 +28,3 @@ # frozen_string_literal: true | ||
| # s(:send, nil, :bar))) | ||
| def valid?(node) | ||
| def match?(node) | ||
| return false unless node&.type == :class | ||
@@ -31,0 +31,0 @@ |
@@ -5,2 +5,3 @@ # frozen_string_literal: true | ||
| require 'observer' | ||
| require 'open3' | ||
@@ -608,12 +609,11 @@ module Solargraph | ||
| Thread.new do | ||
| cache_pid = Process.spawn(workspace.command_path, 'cache', spec.name, spec.version.to_s) | ||
| report_cache_progress spec.name, pending | ||
| Process.wait(cache_pid) | ||
| logger.info "Cached #{spec.name} #{spec.version}" | ||
| rescue Errno::EINVAL => _e | ||
| logger.info "Cached #{spec.name} #{spec.version} with EINVAL" | ||
| rescue StandardError => e | ||
| cache_errors.add spec | ||
| Solargraph.logger.warn "Error caching gemspec #{spec.name} #{spec.version}: [#{e.class}] #{e.message}" | ||
| ensure | ||
| _o, e, s = Open3.capture3(workspace.command_path, 'cache', spec.name, spec.version.to_s) | ||
| if s.success? | ||
| logger.info "Cached #{spec.name} #{spec.version}" | ||
| else | ||
| cache_errors.add spec | ||
| logger.warn "Error caching gemspec #{spec.name} #{spec.version}" | ||
| logger.warn e | ||
| end | ||
| end_cache_progress | ||
@@ -620,0 +620,0 @@ catalog |
@@ -12,6 +12,7 @@ # frozen_string_literal: true | ||
| class << self | ||
| # @type [Hash{Symbol => Class<NodeProcessor::Base>}] | ||
| # @type [Hash<Symbol, Array<Class<NodeProcessor::Base>>>] | ||
| @@processors ||= {} | ||
| # Register a processor for a node type. | ||
| # Register a processor for a node type. You can register multiple processors for the same type. | ||
| # If a node processor returns true, it will skip the next processor of the same node type. | ||
| # | ||
@@ -22,4 +23,9 @@ # @param type [Symbol] | ||
| def register type, cls | ||
| @@processors[type] = cls | ||
| @@processors[type] ||= [] | ||
| @@processors[type] << cls | ||
| end | ||
| def deregister type, cls | ||
| @@processors[type].delete(cls) | ||
| end | ||
| end | ||
@@ -41,6 +47,12 @@ | ||
| return [pins, locals] unless Parser.is_ast_node?(node) | ||
| klass = @@processors[node.type] || NodeProcessor::Base | ||
| processor = klass.new(node, region, pins, locals) | ||
| processor.process | ||
| [processor.pins, processor.locals] | ||
| node_processor_classes = @@processors[node.type] || [NodeProcessor::Base] | ||
| node_processor_classes.each do |klass| | ||
| processor = klass.new(node, region, pins, locals) | ||
| process_next = processor.process | ||
| break unless process_next | ||
| end | ||
| [pins, locals] | ||
| end | ||
@@ -47,0 +59,0 @@ end |
@@ -33,5 +33,8 @@ # frozen_string_literal: true | ||
| # | ||
| # @return [void] | ||
| # @return [Boolean] continue processing the next processor of the same node type. | ||
| # @return [void] In case there is only one processor registered for the node type, it can be void. | ||
| def process | ||
| process_children | ||
| true | ||
| end | ||
@@ -68,3 +71,5 @@ | ||
| def named_path_pin position | ||
| pins.select{|pin| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)}.last | ||
| pins.select do |pin| | ||
| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position) | ||
| end.last | ||
| end | ||
@@ -77,3 +82,3 @@ | ||
| # @todo determine if this can return a Pin::Block | ||
| pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last | ||
| pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last | ||
| end | ||
@@ -85,3 +90,3 @@ | ||
| def closure_pin position | ||
| pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last | ||
| pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last | ||
| end | ||
@@ -88,0 +93,0 @@ end |
@@ -45,2 +45,4 @@ # frozen_string_literal: true | ||
| register :send, ParserGem::NodeProcessors::SendNode | ||
| register :class, Convention::StructDefinition::NodeProcessors::StructNode | ||
| register :class, Convention::DataDefinition::NodeProcessors::DataNode | ||
| register :class, ParserGem::NodeProcessors::NamespaceNode | ||
@@ -53,2 +55,4 @@ register :module, ParserGem::NodeProcessors::NamespaceNode | ||
| register :gvasgn, ParserGem::NodeProcessors::GvasgnNode | ||
| register :casgn, Convention::StructDefinition::NodeProcessors::StructNode | ||
| register :casgn, Convention::DataDefinition::NodeProcessors::DataNode | ||
| register :casgn, ParserGem::NodeProcessors::CasgnNode | ||
@@ -55,0 +59,0 @@ register :masgn, ParserGem::NodeProcessors::MasgnNode |
@@ -11,13 +11,2 @@ # frozen_string_literal: true | ||
| def process | ||
| if Convention::StructDefinition::StructAssignmentNode.valid?(node) | ||
| process_struct_assignment | ||
| else | ||
| process_constant_assignment | ||
| end | ||
| end | ||
| private | ||
| # @return [void] | ||
| def process_constant_assignment | ||
| pins.push Solargraph::Pin::Constant.new( | ||
@@ -34,13 +23,4 @@ location: get_node_location(node), | ||
| # @todo Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports | ||
| # multiple processors. | ||
| def process_struct_assignment | ||
| processor_klass = Convention::StructDefinition::NodeProcessors::StructNode | ||
| processor = processor_klass.new(node, region, pins, locals) | ||
| processor.process | ||
| private | ||
| @pins = processor.pins | ||
| @locals = processor.locals | ||
| end | ||
| # @return [String] | ||
@@ -47,0 +27,0 @@ def const_name |
@@ -10,2 +10,3 @@ # frozen_string_literal: true | ||
| # @return [void] | ||
| def process | ||
@@ -44,3 +45,5 @@ # Example: | ||
| if pin.nil? | ||
| Solargraph.logger.debug { "Could not find local for masgn= value in location #{location.inspect} in #{lhs_arr} - masgn = #{masgn}, lhs.type = #{lhs.type}" } | ||
| Solargraph.logger.debug do | ||
| "Could not find local for masgn= value in location #{location.inspect} in #{lhs_arr} - masgn = #{masgn}, lhs.type = #{lhs.type}" | ||
| end | ||
| next | ||
@@ -47,0 +50,0 @@ end |
@@ -14,13 +14,2 @@ # frozen_string_literal: true | ||
| if Convention::StructDefinition::StructDefintionNode.valid?(node) | ||
| process_struct_definition | ||
| else | ||
| process_namespace(superclass_name) | ||
| end | ||
| end | ||
| private | ||
| # @param superclass_name [String, nil] | ||
| def process_namespace(superclass_name) | ||
| loc = get_node_location(node) | ||
@@ -48,13 +37,2 @@ nspin = Solargraph::Pin::Namespace.new( | ||
| end | ||
| # @todo Move this out of [NamespaceNode] once [Solargraph::Parser::NodeProcessor] supports | ||
| # multiple processors. | ||
| def process_struct_definition | ||
| processor_klass = Convention::StructDefinition::NodeProcessors::StructNode | ||
| processor = processor_klass.new(node, region, pins, locals) | ||
| processor.process | ||
| @pins = processor.pins | ||
| @locals = processor.locals | ||
| end | ||
| end | ||
@@ -61,0 +39,0 @@ end |
@@ -10,2 +10,3 @@ # frozen_string_literal: true | ||
| class OpasgnNode < Parser::NodeProcessor::Base | ||
| # @return [void] | ||
| def process | ||
@@ -12,0 +13,0 @@ # Parser::CurrentRuby.parse("a += 2") |
@@ -8,2 +8,3 @@ # frozen_string_literal: true | ||
| class OrasgnNode < Parser::NodeProcessor::Base | ||
| # @return [void] | ||
| def process | ||
@@ -10,0 +11,0 @@ new_node = node.updated(node.children[0].type, node.children[0].children + [node.children[1]]) |
@@ -10,2 +10,3 @@ # frozen_string_literal: true | ||
| # @return [void] | ||
| def process | ||
@@ -12,0 +13,0 @@ if node.children[1] # Exception local variable name |
@@ -8,2 +8,3 @@ # frozen_string_literal: true | ||
| class SymNode < Parser::NodeProcessor::Base | ||
| # @return [void] | ||
| def process | ||
@@ -10,0 +11,0 @@ pins.push Solargraph::Pin::Symbol.new( |
@@ -54,4 +54,12 @@ # frozen_string_literal: true | ||
| assert_source_provided | ||
| assert_location_provided | ||
| end | ||
| # @return [void] | ||
| def assert_location_provided | ||
| return unless best_location.nil? && %i[yardoc source rbs].include?(source) | ||
| Solargraph.assert_or_log(:best_location, "Neither location nor type_location provided - #{path} #{source} #{self.class}") | ||
| end | ||
| # @param other [self] | ||
@@ -294,2 +302,5 @@ # @param attrs [Hash{Symbol => Object}] | ||
| # @param other [self] | ||
| # @param attr [::Symbol] | ||
| # @return [undefined] | ||
| def choose_pin_attr(other, attr) | ||
@@ -309,2 +320,3 @@ # @type [Pin::Base, nil] | ||
| # @return [void] | ||
| def assert_source_provided | ||
@@ -554,2 +566,3 @@ Solargraph.assert_or_log(:source, "source not provided - #{@path} #{@source} #{self.class}") if source.nil? | ||
| # @return [String] | ||
| def desc | ||
@@ -564,2 +577,3 @@ "[#{inner_desc}]" | ||
| # @return [String] | ||
| def all_location_text | ||
@@ -577,2 +591,3 @@ if location.nil? && type_location.nil? | ||
| # @return [void] | ||
| def reset_generated! | ||
@@ -579,0 +594,0 @@ end |
@@ -202,5 +202,7 @@ # frozen_string_literal: true | ||
| yield_return_type = ComplexType.try_parse(*yieldreturn_tags.flat_map(&:types)) | ||
| block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source, closure: self) | ||
| block = Signature.new(generics: generics, parameters: yield_parameters, return_type: yield_return_type, source: source, | ||
| closure: self, location: location, type_location: type_location) | ||
| end | ||
| signature = Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, closure: self, source: source) | ||
| signature = Signature.new(generics: generics, parameters: parameters, return_type: return_type, block: block, closure: self, source: source, | ||
| location: location, type_location: type_location) | ||
| block.closure = signature if block | ||
@@ -207,0 +209,0 @@ signature |
@@ -25,2 +25,3 @@ # frozen_string_literal: true | ||
| # @param loader [RBS::EnvironmentLoader] | ||
| def initialize(loader:) | ||
@@ -32,2 +33,3 @@ @loader = loader | ||
| # @return [RBS::EnvironmentLoader] | ||
| attr_reader :loader | ||
@@ -111,3 +113,3 @@ | ||
| def convert_members_to_pins decl, closure | ||
| context = Context.new | ||
| context = Conversions::Context.new | ||
| decl.members.each { |m| context = convert_member_to_pin(m, closure, context) } | ||
@@ -119,3 +121,3 @@ end | ||
| # @param context [Context] | ||
| # @return [void] | ||
| # @return [Context] | ||
| def convert_member_to_pin member, closure, context | ||
@@ -302,2 +304,3 @@ case member | ||
| comments: decl.comment&.string, | ||
| type_location: location_decl_to_pin_location(decl.location), | ||
| source: :rbs | ||
@@ -324,2 +327,3 @@ ) | ||
| # allow that to be extended via .solargraph.yml | ||
| # @type [Hash{Array(String, Symbol, String) => Symbol} | ||
| VISIBILITY_OVERRIDE = { | ||
@@ -349,2 +353,9 @@ ["Rails::Engine", :instance, "run_tasks_blocks"] => :protected, | ||
| # @param decl [RBS::AST::Members::MethodDefinition, RBS::AST::Members::AttrReader, RBS::AST::Members::AttrAccessor] | ||
| # @param closure [Pin::Namespace] | ||
| # @param context [Context] | ||
| # @param scope [Symbol] :instance or :class | ||
| # @param name [String] The name of the method | ||
| # @sg-ignore | ||
| # @return [Symbol] | ||
| def calculate_method_visibility(decl, context, closure, scope, name) | ||
@@ -424,2 +435,3 @@ override_key = [closure.path, scope, name] | ||
| decl.overloads.map do |overload| | ||
| type_location = location_decl_to_pin_location(overload.method_type.location) | ||
| generics = overload.method_type.type_params.map(&:name).map(&:to_s) | ||
@@ -429,7 +441,7 @@ signature_parameters, signature_return_type = parts_of_function(overload.method_type, pin) | ||
| block_parameters, block_return_type = parts_of_function(overload.method_type.block, pin) | ||
| Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, | ||
| closure: pin, source: :rbs) | ||
| Pin::Signature.new(generics: generics, parameters: block_parameters, return_type: block_return_type, source: :rbs, | ||
| type_location: type_location, closure: pin) | ||
| end | ||
| Pin::Signature.new(generics: generics, parameters: signature_parameters, return_type: signature_return_type, block: block, | ||
| closure: pin, source: :rbs) | ||
| Pin::Signature.new(generics: generics, parameters: signature_parameters, return_type: signature_return_type, block: block, source: :rbs, | ||
| type_location: type_location, closure: pin) | ||
| end | ||
@@ -453,3 +465,9 @@ end | ||
| def parts_of_function type, pin | ||
| return [[Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs)], ComplexType.try_parse(method_type_to_tag(type)).force_rooted] if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) | ||
| type_location = pin.type_location | ||
| if defined?(RBS::Types::UntypedFunction) && type.type.is_a?(RBS::Types::UntypedFunction) | ||
| return [ | ||
| [Solargraph::Pin::Parameter.new(decl: :restarg, name: 'arg', closure: pin, source: :rbs, type_location: type_location)], | ||
| ComplexType.try_parse(method_type_to_tag(type)).force_rooted | ||
| ] | ||
| end | ||
@@ -460,3 +478,3 @@ parameters = [] | ||
| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs) | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, source: :rbs, type_location: type_location) | ||
| end | ||
@@ -467,2 +485,3 @@ type.type.optional_positionals.each do |param| | ||
| return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, | ||
| type_location: type_location, | ||
| source: :rbs) | ||
@@ -472,7 +491,7 @@ end | ||
| name = type.type.rest_positionals.name ? type.type.rest_positionals.name.to_s : "arg_#{arg_num += 1}" | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs) | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :restarg, name: name, closure: pin, source: :rbs, type_location: type_location) | ||
| end | ||
| type.type.trailing_positionals.each do |param| | ||
| name = param.name ? param.name.to_s : "arg_#{arg_num += 1}" | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs) | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :arg, name: name, closure: pin, source: :rbs, type_location: type_location) | ||
| end | ||
@@ -483,3 +502,3 @@ type.type.required_keywords.each do |orig, param| | ||
| return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, | ||
| source: :rbs) | ||
| source: :rbs, type_location: type_location) | ||
| end | ||
@@ -490,2 +509,3 @@ type.type.optional_keywords.each do |orig, param| | ||
| return_type: ComplexType.try_parse(other_type_to_tag(param.type)).force_rooted, | ||
| type_location: type_location, | ||
| source: :rbs) | ||
@@ -496,3 +516,3 @@ end | ||
| parameters.push Solargraph::Pin::Parameter.new(decl: :kwrestarg, name: type.type.rest_keywords.name.to_s, closure: pin, | ||
| source: :rbs) | ||
| source: :rbs, type_location: type_location) | ||
| end | ||
@@ -507,2 +527,3 @@ | ||
| # @param closure [Pin::Namespace] | ||
| # @param context [Context] | ||
| # @return [void] | ||
@@ -531,2 +552,3 @@ def attr_reader_to_pin(decl, closure, context) | ||
| # @param closure [Pin::Namespace] | ||
| # @param context [Context] | ||
| # @return [void] | ||
@@ -537,5 +559,6 @@ def attr_writer_to_pin(decl, closure, context) | ||
| visibility = calculate_method_visibility(decl, context, closure, final_scope, name) | ||
| type_location = location_decl_to_pin_location(decl.location) | ||
| pin = Solargraph::Pin::Method.new( | ||
| name: name, | ||
| type_location: location_decl_to_pin_location(decl.location), | ||
| type_location: type_location, | ||
| closure: closure, | ||
@@ -554,3 +577,4 @@ parameters: [], | ||
| source: :rbs, | ||
| closure: pin | ||
| closure: pin, | ||
| type_location: type_location | ||
| ) | ||
@@ -564,2 +588,3 @@ rooted_tag = ComplexType.parse(other_type_to_tag(decl.type)).force_rooted.rooted_tags | ||
| # @param closure [Pin::Namespace] | ||
| # @param context [Context] | ||
| # @return [void] | ||
@@ -596,2 +621,3 @@ def attr_accessor_to_pin(decl, closure, context) | ||
| comments: decl.comment&.string, | ||
| type_location: location_decl_to_pin_location(decl.location), | ||
| source: :rbs | ||
@@ -613,2 +639,3 @@ ) | ||
| comments: decl.comment&.string, | ||
| type_location: location_decl_to_pin_location(decl.location), | ||
| source: :rbs | ||
@@ -615,0 +642,0 @@ ) |
| # frozen_string_literal: true | ||
| module Solargraph | ||
| VERSION = '0.56.1' | ||
| VERSION = '0.56.2' | ||
| end |
@@ -154,3 +154,3 @@ # frozen_string_literal: true | ||
| { | ||
| 'include' => ['**/*.rb'], | ||
| 'include' => ['Rakefile', 'Gemfile', '*.gemspec', '**/*.rb'], | ||
| 'exclude' => ['spec/**/*', 'test/**/*', 'vendor/**/*', '.bundle/**/*'], | ||
@@ -157,0 +157,0 @@ 'require' => [], |
@@ -10,8 +10,36 @@ module Solargraph | ||
| def object_location code_object, spec | ||
| return nil if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil? | ||
| if spec.nil? || code_object.nil? || code_object.file.nil? || code_object.line.nil? | ||
| if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) | ||
| # If the code object is a namespace, use the namespace's location | ||
| return object_location(code_object.namespace, spec) | ||
| end | ||
| return Solargraph::Location.new(__FILE__, Solargraph::Range.from_to(__LINE__ - 1, 0, __LINE__ - 1, 0)) | ||
| end | ||
| file = File.join(spec.full_gem_path, code_object.file) | ||
| Solargraph::Location.new(file, Solargraph::Range.from_to(code_object.line - 1, 0, code_object.line - 1, 0)) | ||
| end | ||
| # @param code_object [YARD::CodeObjects::Base] | ||
| # @param spec [Gem::Specification, nil] | ||
| # @return [Solargraph::Pin::Namespace] | ||
| def create_closure_namespace_for(code_object, spec) | ||
| code_object_for_location = code_object | ||
| # code_object.namespace is sometimes a YARD proxy object pointing to a method path ("Object#new") | ||
| code_object_for_location = code_object.namespace if code_object.namespace.is_a?(YARD::CodeObjects::NamespaceObject) | ||
| namespace_location = object_location(code_object_for_location, spec) | ||
| ns_name = code_object.namespace.to_s | ||
| if ns_name.empty? | ||
| Solargraph::Pin::ROOT_PIN | ||
| else | ||
| Solargraph::Pin::Namespace.new( | ||
| name: ns_name, | ||
| closure: Pin::ROOT_PIN, | ||
| gates: [code_object.namespace.to_s], | ||
| source: :yardoc, | ||
| location: namespace_location | ||
| ) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
@@ -10,8 +10,8 @@ # frozen_string_literal: true | ||
| # @param code_object [YARD::CodeObjects::Base] | ||
| # @param closure [Pin::Closure, nil] | ||
| # @param spec [Gem::Specification, nil] | ||
| # @return [Pin::Constant] | ||
| def self.make code_object, closure = nil, spec = nil | ||
| closure ||= Solargraph::Pin::Namespace.new( | ||
| name: code_object.namespace.to_s, | ||
| gates: [code_object.namespace.to_s], | ||
| source: :yardoc, | ||
| ) | ||
| closure ||= create_closure_namespace_for(code_object, spec) | ||
| Pin::Constant.new( | ||
@@ -18,0 +18,0 @@ location: object_location(code_object, spec), |
@@ -22,8 +22,3 @@ # frozen_string_literal: true | ||
| def self.make code_object, name = nil, scope = nil, visibility = nil, closure = nil, spec = nil | ||
| closure ||= Solargraph::Pin::Namespace.new( | ||
| name: code_object.namespace.to_s, | ||
| gates: [code_object.namespace.to_s], | ||
| type: code_object.namespace.is_a?(YARD::CodeObjects::ClassObject) ? :class : :module, | ||
| source: :yardoc, | ||
| ) | ||
| closure ||= create_closure_namespace_for(code_object, spec) | ||
| location = object_location(code_object, spec) | ||
@@ -30,0 +25,0 @@ name ||= code_object.name.to_s |
@@ -10,11 +10,11 @@ # frozen_string_literal: true | ||
| # @param code_object [YARD::CodeObjects::NamespaceObject] | ||
| # @param spec [Gem::Specification, nil] | ||
| # @param closure [Pin::Closure, nil] | ||
| # @return [Pin::Namespace] | ||
| def self.make code_object, spec, closure = nil | ||
| closure ||= Solargraph::Pin::Namespace.new( | ||
| name: code_object.namespace.to_s, | ||
| closure: Pin::ROOT_PIN, | ||
| gates: [code_object.namespace.to_s], | ||
| source: :yardoc, | ||
| ) | ||
| closure ||= create_closure_namespace_for(code_object, spec) | ||
| location = object_location(code_object, spec) | ||
| Pin::Namespace.new( | ||
| location: object_location(code_object, spec), | ||
| location: location, | ||
| name: code_object.name.to_s, | ||
@@ -21,0 +21,0 @@ comments: code_object.docstring ? code_object.docstring.all.to_s : '', |