[ruby-list:46794] Re: Emacsでメソッドのアウトライン表示
From:
rubikitch@...
Date:
2010-01-19 13:03:13 UTC
List:
ruby-list #46794
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