lsegal / yard

YARD is a Ruby Documentation tool. The Y stands for "Yay!"
http://yardoc.org
MIT License
1.92k stars 394 forks source link

How is !group directive working? #1534

Open thomthom opened 4 months ago

thomthom commented 4 months ago

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:

I have read the Contributing Guide.

MSP-Greg commented 4 months ago

@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.

thomthom commented 4 months ago

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 commented 4 months ago

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);
}
lsegal commented 4 months ago

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.

thomthom commented 4 months ago

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.

MSP-Greg commented 4 months ago

@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