From: ShingoKintaka <kamuycikap / tulip.ocn.ne.jp>
Subject: [ruby-list:46793] Emacsでメソッドのアウトライン表示
Date: Tue, 19 Jan 2010 21:16:06 +0900

るびきちです。

> EmacsでRubyコードをコーディングする時、クラスをツリー表示できたら。。。。
> Rubyで利用できるアウトライン表示を利用している方はいらっしゃいませんか?

大昔にあおきさんが作ったrdefs.rbを改造してクラスツリー表示に使っています。

M-x compile rdefs.rb -nPH ファイル名

を実行すると、クラス名とメソッド名と引数がリストされます。
C-x ` (next-error)で順次たどれるようになっています。

手元では anything.el (現メンテナです)による絞り込みやタグジャンプがで
きるようになっていますが、需要があるならばそのうちファイルに分離して公
開しようかと思っています。


#!/usr/bin/env ruby18 # # $Id: rdefs.rb,v 1.22 2009/05/31 04:53:02 rubikitch Exp $ require 'optparse' DEF_RE = /\A\s*(?: def\s | class\s | module\s | include[\s\(] | extend[\s\(] | alias(?:_\w+)? | attr_reader[\s\(] | attr_writer[\s\(] | attr_accessor[\s\(] | attr[\s\(] | public[\s\(] | private[\s\(] | private_class_method[\s\(] | public_class_method[\s\(] | module_function[\s\(] | protected[\s\(] | def_delegators?[\s\(] | .*Struct(?:\.|::)new[\s\(] )/x CLASS_METH_RE = /\A\s*(?:def|class|module)\s/ REQUIRE_ALL_LIBS = %w[active_support] def main print_line_number_p = false print_filename = false print_prefix_p = false additional_regexp = nil tab_width = 8 include_dependencies = false parser = OptionParser.new parser.banner = "#{File.basename $0} [-nH] [-r REGEXP] file..." parser.on('-n', '--lineno', 'Prints line number.') { print_line_number_p = true } parser.on('-H', '--with-filename', 'Prints filename.'){ print_filename = true } parser.on('-P', '--with-prefix', 'Prints prefix.'){ print_prefix_p = true } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 0 } parser.on('-r REGEXP', '--regexp=REGEXP', 'Print lines matching REGEXP additionally.') {|x| additional_regexp = Regexp.new(x) } parser.on('-t TAB-WIDTH', '--tab-width=TAB-WIDTH', 'Tab width.') {|x| tab_width = x.to_i } parser.on('-d', 'Include dependencies') {|x| include_dependencies = true } parser.on('--dev', 'Add this project\'s bin/ and lib/ to $LOAD_PATH.') {|x| $:.unshift "lib", "bin" } parser.on('--gem', 'Add RubyGems additional load paths to $LOAD_PATH.') {|x| require 'rubygems' $:.unshift *Gem.latest_load_paths } parser.on('--rdefs', 'Output RDEFS file.') {|x| $> = open("RDEFS", "w") } parser.on('-a', '--all', 'Output RDEFS file, including -nHPd --dev --rdefs options.') {|x| print_line_number_p = true print_filename = true print_prefix_p = true include_dependencies = true $:.unshift "lib", "bin" require 'rubygems' $:.unshift *Gem.latest_load_paths $> = open("RDEFS", "w") } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message exit 1 end # rule out nonexistent files ARGV.reject! {|file| ! File.exist?(file) } f = Preprocessor.new(ARGF, tab_width) re = DEF_RE processed = ARGV.uniq while line = f.gets begin if re =~ line f.register_line if CLASS_METH_RE === line printf '%s:', f.path if print_filename printf '%-6s', "#{f.lineno}:" if print_line_number_p print format_line(getdef(line,f), f.prefix(line), print_prefix_p) elsif additional_regexp and additional_regexp =~ line f.register_line if CLASS_METH_RE === line printf '%s:', f.path if print_filename printf '%-6s', "#{f.lineno}:" if print_line_number_p print format_line(line, f.prefix(line), print_prefix_p) elsif include_dependencies and /require ["'](.+?)["']/ === line next if $1 == 'rubygems' basename = File.basename $1, ".rb" lib = which_library $1 (REQUIRE_ALL_LIBS.include?(basename) ? add_all_libraries(lib) : [lib]).each do |lib2| if lib2 and /\.rb$/ === lib2 and not processed.include? lib2 $stderr.puts "Add #{lib2}" ARGV << lib2 processed << lib2 end end end rescue $stderr.puts $! $stderr.puts "Continuing..." end end end def add_all_libraries(baselib) dir = File.dirname(baselib) Dir["#{dir}/**/*.rb"] end # borrowed from devel/which require 'rbconfig' def which_library(lib) unless lib.is_a? String raise TypeError, "wrong argument type #{lib.type} (expected String)" end ext = ["rb", Config::CONFIG["DLEXT"], Config::CONFIG["DLEXT2"]] ext.map!{|i| i.length > 0 ? ".#{i}" : nil} ext.compact! ext.push("") at = with = nil at = $:.find{|path| file = "#{File::expand_path(path)}/#{lib}" begin with = ext.find{|i| test(?f, file+i) && test(?r, file+i) } rescue next end } "#{at}/#{lib}#{with}" if at end def getdef(str, f) until balanced?(str) str << f.gets end str end def balanced?(str) str.count('(') == str.count(')') end def format_line(line, prefix, print_prefix_p) if print_prefix_p and !prefix.empty? prefix + " / " + line.lstrip else line end end class Preprocessor def initialize(f, tab_width) @f = f @tab_width = tab_width @hierarchy = Array.new(7) @line = nil end def gets line = @f.gets if /\A=begin\s/ === line while line = @f.gets break if /\A=end\s/ === line end line = @f.gets end @line = line end def indent(line) line[/^ */].length end private :indent def register_line line = @line.gsub(/\t/, ' ' * @tab_width) indent = indent(line) @hierarchy[indent] = line.strip @hierarchy.fill(nil, indent+1) end def prefix(line) hierarchy = @hierarchy.dup.fill(nil, indent(line)) hierarchy.compact.map{|l| l.sub(/ *#.+$/, '').gsub(/ +/, ' ') }.join(" / ") end def lineno @f.file.lineno end def path @f.path end end main
-- rubikitch Blog: http://d.hatena.ne.jp/rubikitch/ Site: http://www.rubyist.net/~rubikitch/ Twit: http://twitter.com/rubikitch/ 『Ruby逆引きハンドブック』 http://d.hatena.ne.jp/rubikitch/20090525/rubybook