Second version fixes a bug wherein methods were being assumed to be in the same file as their enclosing class/module. Also adds more tags, so: module Foo::Bar::Baz def [i] end end will generate tags for Baz.[] Bar.Baz.[] Foo.Bar.Baz.[] Usage is the same, I'll probably make it do the merging itself soon, to make this simpler. Cheers, Sam Quoteing sroberts / uniserve.com, on Sun, Nov 14, 2004 at 08:31:50AM +0900: > Why? Because I want things tagged that means implementing a fairly > complex ruby parser, like qualified tags, and rdoc already is a > ruby parser. > > To try it out, put the tags_generator.rb into rdoc/generators/, in your > path somewhere, then run: > > rdoc -f tags mylib/ > exhuberant-ctags -R mylib > mv tags tags.ctags > sort tags.ctags tags.rdoc > tags > > Currently, it only generates qualified method tags, these are tags > exhuberant ctags does NOT support (it would be turned on by --extra=+q > if it was supported), so there shouldn't be duplicates. > > Try it out, qualified tags allow you to do: > > :ta Rdoc.<TAB> > > and see all the methods and classes in Rdoc, choose a sub-class, keep > tabbing, see all the methods in that class, etc. It allows you to use > your tags to drill down through the tree to the exact method you want, > even to discover the methods, in a way. > > Exhuberant ctags does this for C++ like languages (Java, etc.), but its > ruby support doesn't. I added some discussion of this over on the wiki > (maybe not a good page?): > > http://www.rubygarden.org/ruby?VimRubySupport > > Because this uses rdoc, it tags methods like == and [] that exhuberant > ctags doesn't get. Also, rdoc knows to map Vpim.Icalendar.Address.new to > the 'def initialise" of that class! > > For me, personally, lack of qualified tags in an OO language makes tags > almost useless, how many classes are going to have #initialise, or > #to_s? Its been bugging me for a while now that I'm slower navigating > ruby code than C code, this might help a bit. > > What do you all think? Useful to anybody else? Worth pushing forward? > > Cheers, > Sam > > Note for Dave: > > A problem I'm having is that I'd like to add support for qualified > classes and modules, but that means recovering the text from the line > they were defined on in order to build the ctags REGEX. The src > text/tokens are kept around for methods, but it isn't kept around for > class/module definitions, and I don't think its kept around for > constants either, which would also be cool to tag. And maybe even > attributes? > ----- tags_generator.rb ----- require 'ftools' require 'rdoc/options' require 'rdoc/template' require 'rdoc/markup/simple_markup' require 'rdoc/markup/simple_markup/to_flow' require 'cgi' require 'rdoc/ri/ri_cache' require 'rdoc/ri/ri_reader' require 'rdoc/ri/ri_writer' require 'rdoc/ri/ri_descriptions' require 'pp' module RDoc class ClassModule # FIXME - I don't think this works... def file_name if @parent.class === TopLevel @parent.file_absolute_name else @parent.file_name end end end class AnyMethod # Collect all the tokens for the method up-to and including the identifier, and the filename. def decl_string_and_file src = '' filename = nil break_on_nl = false if @token_stream @token_stream.each do |t| next unless t case t when RubyToken::TkCOMMENT # TkCOMMENT.text is "# File vpim/maker/vcard.rb, line 29" if( t.text =~ /# File (.*), line \d+/ ) filename = $1 end when RubyToken::TkNL break if break_on_nl src = '' else src << t.text end break_on_nl = true if RubyToken::TkIDENTIFIER === t end if false puts "----------------------" pp @token_stream puts "+++" puts src puts "----------------------" end end [ src, filename ] end end end module Generators class TAGSGenerator # Generators may need to return specific subclasses depending # on the options they are passed. Because of this # we create them using a factory def TAGSGenerator.for(options) new(options) end class <<self protected :new end # Set up a new HTML generator. Basically all we do here is load # up the correct output temlate def initialize(options) #:not-new: @options = options # TODO - make this a command-line option @gen_qualified = true # TODO - make this a command-line option @gen_unqualified = false # TODO - make this a command-line option @output = File.open("tags.rdoc", 'w') # TODO - make this a command-line option @dump = nil # File.open("rdoc.dump", 'w') # TODO - make this a command-line option @verbose = nil #pp options end def generate(toplevels) # This takes +8 minutes on vPim! Wow! PP.pp( toplevels, @dump ) if @dump RDoc::TopLevel.all_classes_and_modules.each do |cls| process_class(cls) end end def process_class(from_class) generate_class_info(from_class) # now recure into this classes constituent classess from_class.each_classmodule do |mod| process_class(mod) end end def generate_class_info(cls) # TODO: # - when generating qualified names, generate the intermediate qualified as well, so # all of these: # Outer.Middle.Inner.a_method # Middle.Inner.a_method # Inner.a_method # a_method =begin # TODO: can't do classes and modules, we don't have the original text tokens to reconstruct # the tag's REGEX. if cls === RDoc::NormalModule tag_type = 'c' else tag_type = 'm' end @output.puts tag = "#{cls.name}\t#{cls.file_name}\t/^class *#{cls.name}/;"\t#{tag_type}" if @gen_qualified && cls.name != cls.full_name @output.puts "#{cls.full_name.gsub('::', '.')}\t#{cls.file_name}\t/^class *#{cls.name}/;\"\t#{tag_type}" end =end cls.method_list.each do |m| if m.singleton tag_type = 'F' else tag_type = 'f' end decl_string, decl_file = m.decl_string_and_file puts "Tagging: #{m.name} in: #{cls.full_name} from: #{decl_file}" if @verbose tag = "#{m.name}\t#{decl_file}\t/^#{decl_string}$/;\"\t#{tag_type}" if @gen_unqualified @output.puts tag end if @gen_qualified path = cls.full_name.split('::') (1..path.length).each do |elements| qualifier = path[-elements, elements].join('.') puts " ..#{qualifier}" if @verbose @output.puts "#{qualifier}.#{tag}" end end end # TODO: It would be great to tag attributes and contstants # cls.attributes.each do |a| # cls.constants.each do |c| end end end