Hi List,

While ri is a really nice quick reference, I found it hard to read the
long lists of comma-separated method names.  So I patched it to format
these lists in columns.  Now, instead of this:


  -------------------------------------------------------------- Class: IO
       (no description...)
  ------------------------------------------------------------------------
  
  
  Includes:
  ---------
       Enumerable(all?, any?, collect, count, detect, each_cons,
       each_slice, each_with_index, entries, enum_cons, enum_slice,
       enum_with_index, find, find_all, grep, include?, inject, map, max,
       max_by, member?, min, min_by, partition, reject, select, sort,
       sort_by, to_a, to_set, zip), File::Constants()
  
  
  Constants:
  ----------
       SEEK_SET: INT2FIX(SEEK_SET)
       SEEK_CUR: INT2FIX(SEEK_CUR)
       SEEK_END: INT2FIX(SEEK_END)
  
  
  Class methods:
  --------------
       for_fd, foreach, new, new, open, pipe, popen, read, readlines,
       select, sysopen
  
  
  Instance methods:
  -----------------
       <<, binmode, close, close_read, close_write, closed?, each,
       each_byte, each_line, eof, eof?, fcntl, fileno, flush, fsync, getc,
       gets, inspect, ioctl, isatty, lineno, lineno=, pid, pos, pos=,
       print, printf, putc, puts, read, readchar, readline, readlines,
       readpartial, reopen, rewind, seek, stat, sync, sync=, sysread,
       sysseek, syswrite, tell, to_i, to_io, tty?, ungetc, write


...you get this:


  -------------------------------------------------------------- Class: IO
       (no description...)
  ------------------------------------------------------------------------
  
  
  Includes:
  ---------
       File::Constants:
            (no instance methods)
       Enumerable:
            all?        each_with_index  find_all  max_by     select 
            any?        entries          grep      member?    sort   
            collect     enum_cons        include?  min        sort_by
            count       enum_slice       inject    min_by     to_a   
            detect      enum_with_index  map       partition  to_set 
            each_cons   find             max       reject     zip    
            each_slice                                               
  
  
  Constants:
  ----------
       SEEK_SET: INT2FIX(SEEK_SET)
       SEEK_CUR: INT2FIX(SEEK_CUR)
       SEEK_END: INT2FIX(SEEK_END)
  
  
  Class methods:
  --------------
       for_fd   new  open  pipe  popen  read  readlines  select  sysopen
       foreach  new                                                     
  
  
  Instance methods:
  -----------------
       <<           eof     inspect  print      readpartial  sysseek 
       binmode      eof?    ioctl    printf     reopen       syswrite
       close        fcntl   isatty   putc       rewind       tell    
       close_read   fileno  lineno   puts       seek         to_i    
       close_write  flush   lineno=  read       stat         to_io   
       closed?      fsync   pid      readchar   sync         tty?    
       each         getc    pos      readline   sync=        ungetc  
       each_byte    gets    pos=     readlines  sysread      write   
       each_line                                                     


What do you think?  Patch follows.

Cheers,
George.


diff -Naur ruby/lib/rdoc/ri/ri_display.rb ruby.mod/lib/rdoc/ri/ri_display.rb --- ruby/lib/rdoc/ri/ri_display.rb 2004-03-25 06:17:13.000000000 +1100 +++ ruby.mod/lib/rdoc/ri/ri_display.rb 2005-11-27 20:07:39.000000000 +1100 @@ -88,7 +88,7 @@ display_flow(klass.comment) @formatter.draw_line - + unless klass.includes.empty? @formatter.blankline @formatter.display_heading("Includes:", 2, "") @@ -96,15 +96,14 @@ klass.includes.each do |inc| inc_desc = ri_reader.find_class_by_name(inc.name) if inc_desc - str = inc.name + "(" - str << inc_desc.instance_methods.map{|m| m.name}.join(", ") - str << ")" - incs << str + methods = inc_desc.instance_methods.map{|m| m.name} + methods = ['(no instance methods)'] if methods.empty? + @formatter.wrap("#{inc.name}:") + @formatter.wrap_list(methods.sort, @formatter.indent*2) else - incs << inc.name + @formatter.wrap(inc.name) end - end - @formatter.wrap(incs.sort.join(', ')) + end end unless klass.constants.empty? @@ -122,19 +121,19 @@ unless klass.class_methods.empty? @formatter.blankline @formatter.display_heading("Class methods:", 2, "") - @formatter.wrap(klass.class_methods.map{|m| m.name}.sort.join(', ')) + @formatter.wrap_list(klass.class_methods.map{|m| m.name}.sort) end unless klass.instance_methods.empty? @formatter.blankline @formatter.display_heading("Instance methods:", 2, "") - @formatter.wrap(klass.instance_methods.map{|m| m.name}.sort.join(', ')) + @formatter.wrap_list(klass.instance_methods.map{|m| m.name}.sort) end unless klass.attributes.empty? @formatter.blankline - @formatter.wrap("Attributes:", "") - @formatter.wrap(klass.attributes.map{|a| a.name}.sort.join(', ')) + @formatter.display_heading("Attributes:", 2, "") + @formatter.wrap_list(klass.attributes.map{|a| a.name}.sort) end end end @@ -147,7 +146,7 @@ page do puts "More than one method matched your request. You can refine" puts "your search by asking for information on one of:\n\n" - @formatter.wrap(methods.map {|m| m.full_name} .join(", ")) + @formatter.wrap_list(methods.map {|m| m.full_name}.sort) end end @@ -157,7 +156,7 @@ page do puts "More than one class or module matched your request. You can refine" puts "your search by asking for information on one of:\n\n" - @formatter.wrap(namespaces.map {|m| m.full_name}.join(", ")) + @formatter.wrap_list(namespaces.map {|m| m.full_name}.sort) end end @@ -170,7 +169,7 @@ page do @formatter.draw_line("Known classes and modules") @formatter.blankline - @formatter.wrap(classes.sort.join(", ")) + @formatter.wrap_list(classes.sort) end end end diff -Naur ruby/lib/rdoc/ri/ri_formatter.rb ruby.mod/lib/rdoc/ri/ri_formatter.rb --- ruby/lib/rdoc/ri/ri_formatter.rb 2004-12-31 16:12:37.000000000 +1100 +++ ruby.mod/lib/rdoc/ri/ri_formatter.rb 2005-11-27 20:07:26.000000000 +1100 @@ -47,6 +47,64 @@ end ###################################################################### + + def wrap_list(list, prefix=@indent, linelen=@width) + return if list.empty? + + cols = wrap_list_cols(list, linelen - prefix.length) + widths = cols.map do |col| + col.map{|s| s.length}.max + end + + cols[0].each_index do |rowno| + items = (0...cols.length).map do |colno| + cols[colno][rowno].to_s.ljust(widths[colno]) + end + puts "#{prefix}#{items.join(' ')}" + end + end + + def wrap_list_cols(list, width) + num_cols = 1 + while num_cols < list.length && + wrap_list_fit?(list, width, num_cols+1) + num_cols += 1 + end + ranges = wrap_list_ranges(list.length, num_cols) + ranges.map{|r| list[r]} + end + protected :wrap_list_cols + + def wrap_list_ranges(num_items, num_cols) + ranges = [] + q, r = num_items.divmod(num_cols) + k = 0 + num_cols.times do |i| + len = i < r ? q+1 : q + ranges << (k...k+len) + k += len + end + return ranges + end + private :wrap_list_ranges + + def wrap_list_fit?(list, linelen, num_cols) + ranges = wrap_list_ranges(list.length, num_cols) + + widths = ranges.map do |range| + list[range].map{|s| s.length}.max + end + pad = 2*(num_cols - 1) + + if widths.inject(0){|sum,w| sum+w} + pad > linelen + return false + else + return true + end + end + private :wrap_list_fit? + + ###################################################################### def blankline puts @@ -560,6 +618,19 @@ puts("</pre>") end + def wrap_list(list, prefix=@indent, linelen=@width) + cols = wrap_list_cols(list, linelen - prefix.length) + puts "<table>" + cols[0].each_index do |rowno| + print " <tr>" + cols.each_index do |colno| + print "<td>#{cols[colno][rowno]}</td>" + end + puts "</tr>" + end + puts "</table>" + end + private ATTR_MAP = {