yard icon indicating copy to clipboard operation
yard copied to clipboard

Allow method calls (namely `sig`) to be between docs and the object they're documenting

Open AaronC81 opened this issue 5 years ago • 3 comments

This motivation behind this is primarily introducing better support for using YARD documentation alongside Sorbet type signatures. These are specified by a sig call before the method's definition.

Currently the YARD documentation must go in-between the sig and definition, which feels awkward.

Steps to reproduce

Generate docs for these two snippets:

sig { returns(String) }
# This is a method.
# @return An example string.
def example
  "Foobar"
end
# This is a method.
# @return An example string.
sig { returns(String) }
def example
  "Foobar"
end

Actual Output

The first snippet generates the documentation fine. The second snippet generates no documentation for example and prints:

[warn]: in YARD::Handlers::Ruby::DSLHandler: Undocumentable method, missing name
        in file 'test.rb':3:

        3: sig { returns(String) }

Expected Output

Both snippets should generate documentation for example.

Environment details:

  • OS: Ubuntu 18.04
  • Ruby version (ruby -v): ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
  • YARD version (yard -v): yard 0.9.20
  • Relevant software dependency/versions:
    • Sorbet typechecker 0.4.4254

I have read the Contributing Guide.

AaronC81 avatar Jul 06 '19 06:07 AaronC81

I think supporting this would belong in a plugin since Sorbet is not yet a Ruby standard. Fortunately, here's a quick little YARD extension you can use to add support:

# place in something like `yardext/sorbet.rb`
module YardSorbet
  class SigHandler < YARD::Handlers::Ruby::Base
    namespace_only
    handles method_call

    process do
      case statement.jump(:ident).first
      when "sig"
        extra_state.sorbet_signature_docstring ||= ""
        extra_state.sorbet_signature_docstring += statement.docstring
      else
        extra_state.sorbet_signature_docstring = nil
      end
    end
  end

  module SigStopHandler
    def process
      super
      extra_state.sorbet_signature_docstring = nil
    end
  end

  module SigDefHandler
    def process
      return unless extra_state.sorbet_signature_docstring

      statement.docstring = [
        extra_state.sorbet_signature_docstring,
        statement.docstring
      ].join("\n")

      super
    end
  end

  YARD::Handlers::Ruby::ClassHandler.include SigStopHandler
  YARD::Handlers::Ruby::ModuleHandler.include SigStopHandler
  YARD::Handlers::Ruby::MethodHandler.include SigDefHandler
end

Run with

yard -e yardext/sorbet.rb

Or add -e yardext/sorbet.rb to your .yardopts file.

You could turn this into a gem if you are interested, say, yard-sorbet, at which point you (and others!) could gem install yard-sorbet and use it via yard --plugin sorbet

A nice addition to this would be to parse out the sigs and add YARD param/return type decorations to docstrings-- fairly easy to do given the above base code. That would allow the sig { returns(String) } stuff to get documented as a @return [String] tag. You could even interleave into existing docs so as to allow the following to work:

# @param value the value to reverse
# @return the reversed string
sig { params(value: String).returns(String) }
def reverse(value); end

But I will leave this as an exercise for the reader 😊.

lsegal avatar Jul 06 '19 07:07 lsegal

Thank you! I'll give this a try when I can.


A nice addition to this would be to parse out the sigs and add YARD param/return type decorations to docstrings-- fairly easy to do given the above base code. That would allow the sig { returns(String) } stuff to get documented as a @return [String] tag.

I've been doing almost exactly the opposite of this recently, where the sig calls are generated from the YARD documentation: https://github.com/AaronC81/sord

AaronC81 avatar Jul 06 '19 07:07 AaronC81

The yard-sorbet plugin supports this.

christi-stripe avatar Dec 13 '22 15:12 christi-stripe