yard icon indicating copy to clipboard operation
yard copied to clipboard

How is !group directive working?

Open thomthom opened this issue 1 year ago • 6 comments

I'm having problem seeing the !group directive doing anything. When adding it I don't see constants or methods grouped in any way in the generated docs.

Steps to reproduce

# Hello module.
module Example

  # Hi there.
  # @return [Integer]
  def self.hello
  end

  HELLO_CONSTANT = "hi!"

  #!group ExampleGroup

  # Tellus.
  # @return [Integer]
  def self.world
  end

  WORLD_CONSTANT = "hi!"
  UNIVERSE_CONSTANT = "hi!"

  #!endgroup ExampleGroup

end

Actual Output

image

Expected Output

I thought it would visually group methods and constants in the generated docs.

Environment details:

  • OS: macOS, Windows
  • Ruby version (ruby -v): ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [arm64-darwin20]
  • YARD version (yard -v): yard 0.9.34

I have read the Contributing Guide.

thomthom avatar Feb 22 '24 16:02 thomthom

@thomthom

Try (note the '@'):

# @!group ExampleGroup
# @!endgroup ExampleGroup  

I don't recall groups ever affecting constants, just methods and attributes. Also, they are only grouped in the 'summary' section.

MSP-Greg avatar Feb 22 '24 17:02 MSP-Greg

Try (note the '@'):

Duh! Thanks for catching that! (Though, this was a bug in my repro code, not my real scenario.)

I don't recall groups ever affecting constants, just methods and attributes. Also, they are only grouped in the 'summary' section.

This jogged my memory; constants can be grouped in Ruby code, but not via C Ruby code: https://github.com/lsegal/yard/issues/1237

Which, might be my original problem. I see it working with my pure Ruby example. But I was trying to add this via C Ruby code:

// @!group Example

/**
 * Hello World
 * @return [nil]
 */
static VALUE _wrap_example(VALUE self) {
  return Qnil;
}

It might be the same problem I had with constants apply to methods...

thomthom avatar Feb 22 '24 23:02 thomthom

Ok, so this is an issue with the C parser.

This doesn't work:

// @!group Foos

/**
  * @!group Foos
  * Hello foo bar
  */
VALUE foobar(VALUE self, VALUE x) {
  int value = x;
}

/**
  * Hello foo biz
  */
VALUE foobiz(VALUE self, VALUE x) {
  int value = x;
}

// @!endgroup

/**
  * Hello world
  */
VALUE hello(VALUE self, VALUE x) {
  int value = x;
}

void Init_Mask(void)
{
    rb_cExample  = rb_define_class("Example", rb_cObject);
    rb_define_method(rb_cExample, "foobar", foobar, 1);
    rb_define_method(rb_cExample, "foobiz", foobiz, 1); 
    rb_define_method(rb_cExample, "hello", hello, 1);
}

But this works:

/**
  * @!group Foos
  * Hello foo bar
  */
VALUE foobar(VALUE self, VALUE x) {
  int value = x;
}

/**
  * Hello foo biz
  */
VALUE foobiz(VALUE self, VALUE x) {
  int value = x;
}

/**
  * @!endgroup
  * Hello world
  */
VALUE hello(VALUE self, VALUE x) {
  int value = x;
}

void Init_Mask(void)
{
    rb_cExample  = rb_define_class("Example", rb_cObject);
    rb_define_method(rb_cExample, "foobar", foobar, 1);
    rb_define_method(rb_cExample, "foobiz", foobiz, 1); 
    rb_define_method(rb_cExample, "hello", hello, 1);
}

thomthom avatar Feb 23 '24 12:02 thomthom

Based on my best recollection, the C parser doesn't parse loose comments because we don't have context on what those comments are "attached" to-- in other words, we can't really differentiate if the comment is part of a function, above a member declaration in a C++ class, etc. We have some sense of "toplevel", but things get hairy once you start using scopes / namespaces in C++, and thus we tend to ignore those. The one exception to this rule, ironically, is RDoc style directives, since they're fundamentally context-free.

It's worth noting that the C parser is significantly more rudimentary than the Ruby parser(s) and is mostly designed to pattern match for comments that sit above function declarations or very specific method calls (rb_*). Your best bet is probably to stick with your second syntax.

lsegal avatar Feb 23 '24 18:02 lsegal

Noted. I'll go back and see if I the same thing let me pick up groups for constants as well.

Based on my best recollection, the C parser doesn't parse loose comments because we don't have context on what those comments are "attached" to-- in other words, we can't really differentiate if the comment is part of a function, above a member declaration in a C++ class, etc.

How does the Ruby parser handle this? Is it a difference in how the parsers keep state? I think I recalled when I looked at the issue with constant and groups that the state in the C parser ended up in the "wrong" place.

thomthom avatar Feb 24 '24 12:02 thomthom

@thomthom

I just did a quick check of things, and only used groups in the rb_* section of methods. The following patch seemed to correct things.

diff --git a/lib/yard/handlers/c/handler_methods.rb b/lib/yard/handlers/c/handler_methods.rb
index 9f1769b..bd3aee2 100644
--- a/lib/yard/handlers/c/handler_methods.rb
+++ b/lib/yard/handlers/c/handler_methods.rb
@@ -67,6 +67,11 @@ module YARD
           register MethodObject.new(namespace, name, scope) do |obj|
             register_visibility(obj, visibility)
             find_method_body(obj, func_name)
+
+            if statement&.comments&.source&.start_with? '@!endgroup'
+              extra_state.group = nil
+            end
+
             obj.explicit = true
             add_predicate_return_tag(obj) if name =~ /\?$/
           end

MSP-Greg avatar Feb 24 '24 15:02 MSP-Greg