lib/sord/rbi_generator.rb in sord-0.9.0 vs lib/sord/rbi_generator.rb in sord-0.10.0

- old
+ new

@@ -36,15 +36,16 @@ @method_count = 0 @warnings = [] @replace_errors_with_untyped = options[:replace_errors_with_untyped] @replace_unresolved_with_untyped = options[:replace_unresolved_with_untyped] + @keep_original_comments = options[:keep_original_comments] # Hook the logger so that messages are added as comments to the RBI file Logging.add_hook do |type, msg, item| @current_object.add_comment_to_next_child("sord #{type} - #{msg}") - end if options[:comments] + end if options[:sord_comments] # Hook the logger so that warnings are collected Logging.add_hook do |type, msg, item| # TODO: is it possible to get line numbers here? warnings << [msg, item, 0] if type == :warn @@ -86,11 +87,13 @@ # Take a constant (like "A::B::CONSTANT"), split it on each '::', and # set the constant name to the last string in the array. constant_name = constant.to_s.split('::').last # Add the constant to the current object being generated. - @current_object.create_constant(constant_name, value: "T.let(#{constant.value}, T.untyped)") + @current_object.create_constant(constant_name, value: "T.let(#{constant.value}, T.untyped)") do |c| + c.add_comments(constant.docstring.all.split("\n")) + end end end # Given a YARD NamespaceObject, add lines defining its methods and their # signatures to the current RBI file. @@ -156,12 +159,17 @@ Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth) next 'T.untyped' end end + return_types = getter.tags('return').flat_map(&:types) + unless return_types.any? + Logging.omit("no YARD type given for #{name.inspect}, using T.untyped", meth) + next 'T.untyped' + end inferred_type = TypeConverter.yard_to_sorbet( - getter.tags('return').flat_map(&:types), meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped) + return_types, meth, @replace_errors_with_untyped, @replace_unresolved_with_untyped) Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type} using getter's return type", meth) inferred_type else # Is this the only argument, and was a @param specified without an @@ -207,11 +215,93 @@ @current_object.create_method( meth.name.to_s, parameters: parlour_params, returns: returns, class_method: meth.scope == :class - ) + ) do |m| + if @keep_original_comments + m.add_comments(meth.docstring.all.split("\n")) + else + parser = YARD::Docstring.parser + parser.parse(meth.docstring.all) + + docs_array = parser.text.split("\n") + + # Add @param tags if there are any with names and descriptions. + params = parser.tags.select { |tag| tag.tag_name == 'param' && tag.is_a?(YARD::Tags::Tag) && !tag.name.nil? } + # Add a blank line if there's anything before the params. + docs_array << '' if docs_array.length.positive? && params.length.positive? + params.each do |param| + docs_array << '' if docs_array.last != '' && docs_array.length.positive? + # Output params in the form of: + # _@param_ `foo` — Lorem ipsum. + # _@param_ `foo` + if param.text.nil? + docs_array << "_@param_ `#{param.name}`" + else + docs_array << "_@param_ `#{param.name}` — #{param.text}" + end + end + + # Add @return tags (there could possibly be more than one, despite this not being supported) + returns = parser.tags.select { |tag| tag.tag_name == 'return' && tag.is_a?(YARD::Tags::Tag) && !tag.text.nil? && tag.text.strip != '' } + # Add a blank line if there's anything before the returns. + docs_array << '' if docs_array.length.positive? && returns.length.positive? + returns.each do |retn| + docs_array << '' if docs_array.last != '' && docs_array.length.positive? + # Output returns in the form of: + # _@return_ — Lorem ipsum. + docs_array << "_@return_ — #{retn.text}" + end + + # Iterate through the @example tags for a given YARD doc and output them in Markdown codeblocks. + examples = parser.tags.select { |tag| tag.tag_name == 'example' && tag.is_a?(YARD::Tags::Tag) } + examples.each do |example| + # Only add a blank line if there's anything before the example. + docs_array << '' if docs_array.length.positive? + # Include the example's 'name' if there is one. + docs_array << example.name unless example.name.nil? || example.name == "" + docs_array << "```ruby" + docs_array.concat(example.text.split("\n")) + docs_array << "```" + end if examples.length.positive? + + # Add @note and @deprecated tags. + notice_tags = parser.tags.select { |tag| ['note', 'deprecated'].include?(tag.tag_name) && tag.is_a?(YARD::Tags::Tag) } + # Add a blank line if there's anything before the params. + docs_array << '' if docs_array.last != '' && docs_array.length.positive? && notice_tags.length.positive? + notice_tags.each do |notice_tag| + docs_array << '' if docs_array.last != '' + # Output note/deprecated/see in the form of: + # _@note_ — Lorem ipsum. + # _@note_ + if notice_tag.text.nil? + docs_array << "_@#{notice_tag.tag_name}_" + else + docs_array << "_@#{notice_tag.tag_name}_ — #{notice_tag.text}" + end + end + + # Add @see tags. + see_tags = parser.tags.select { |tag| tag.tag_name == 'see' && tag.is_a?(YARD::Tags::Tag) } + # Add a blank line if there's anything before the params. + docs_array << '' if docs_array.last != '' && docs_array.length.positive? && see_tags.length.positive? + see_tags.each do |see_tag| + docs_array << '' if docs_array.last != '' + # Output note/deprecated/see in the form of: + # _@see_ `B` — Lorem ipsum. + # _@see_ `B` + if see_tag.text.nil? + docs_array << "_@see_ `#{see_tag.name}`" + else + docs_array << "_@see_ `#{see_tag.name}` — #{see_tag.text}" + end + end + + m.add_comments(docs_array) + end + end end end # Given a YARD NamespaceObject, add lines defining its mixins, methods # and children to the RBI file. @@ -225,9 +315,10 @@ parent = @current_object @current_object = item.type == :class \ ? parent.create_class(item.name.to_s, superclass: superclass) : parent.create_module(item.name.to_s) + @current_object.add_comments(item.docstring.all.split("\n")) add_mixins(item) add_methods(item) add_constants(item)