├── data └── bitclust │ ├── template.epub │ ├── mimetype │ ├── rd_file │ ├── nav.xhtml │ ├── container.xml │ ├── doc │ ├── function-index │ ├── function │ ├── class-index │ ├── method │ ├── layout │ ├── contents │ ├── library-index │ ├── library │ └── class │ ├── template.lillia │ ├── rd_file │ ├── class-index │ ├── method │ ├── layout │ ├── library-index │ ├── doc │ ├── library │ └── class │ ├── template.offline │ ├── rd_file │ ├── doc │ ├── function-index │ ├── function │ ├── class-index │ ├── layout │ ├── method │ ├── library-index │ ├── library │ └── class │ ├── template │ ├── doc │ ├── function-index │ ├── function │ ├── class-index │ ├── method │ ├── layout │ ├── opensearchdescription │ ├── library-index │ ├── search │ ├── library │ └── class │ └── catalog │ └── ja_JP.UTF-8 ├── lib ├── bitclust │ ├── version.rb │ ├── progress_bar.rb │ ├── silent_progress_bar.rb │ ├── subcommands │ │ ├── query_command.rb │ │ ├── preproc_command.rb │ │ ├── init_command.rb │ │ ├── update_command.rb │ │ ├── extract_command.rb │ │ ├── list_command.rb │ │ ├── property_command.rb │ │ ├── classes_command.rb │ │ ├── epub_command.rb │ │ ├── htmlfile_command.rb │ │ ├── ancestors_command.rb │ │ └── setup_command.rb │ ├── compat.rb │ ├── textutils.rb │ ├── server.rb │ ├── subcommand.rb │ ├── parseutils.rb │ ├── exception.rb │ ├── docentry.rb │ ├── functionentry.rb │ ├── refsdatabase.rb │ ├── interface.rb │ ├── htmlutils.rb │ ├── functionreferenceparser.rb │ ├── messagecatalog.rb │ ├── simplesearcher.rb │ ├── crossrubyutils.rb │ ├── methodsignature.rb │ ├── functiondatabase.rb │ ├── lineinput.rb │ ├── generators │ │ └── epub.rb │ ├── database.rb │ ├── methodentry.rb │ ├── app.rb │ ├── methodid.rb │ ├── libraryentry.rb │ └── runner.rb └── bitclust.rb ├── theme ├── lillia │ ├── rurema.png │ └── test.css └── default │ ├── rurema.png │ ├── images │ └── external.png │ ├── test.css │ └── style.css ├── .travis.yml ├── view.cgi ├── Gemfile ├── bin ├── bitclust └── refe ├── test ├── run_test.rb ├── test_methodsignature.rb ├── test_rrdparser.rb ├── test_libraryentry.rb ├── test_simplesearcher.rb ├── test_functionreferenceparser.rb ├── test_functiondatabase.rb ├── test_entry.rb ├── test_bitclust.rb ├── test_refsdatabase.rb ├── test_preprocessor.rb ├── test_methoddatabase.rb └── test_runner.rb ├── server.rb ├── tools ├── forall-ruby.rb ├── check-signature.rb ├── gencatalog.rb ├── statrefm.rb ├── stattodo.rb ├── update-database.rb └── bc-convert.rb ├── Rakefile ├── config.ru.sample ├── config.in ├── README ├── refe2.gemspec ├── bitclust-dev.gemspec ├── config.ru └── bitclust.gemspec /data/bitclust/template.epub/mimetype: -------------------------------------------------------------------------------- 1 | application/epub+zip -------------------------------------------------------------------------------- /lib/bitclust/version.rb: -------------------------------------------------------------------------------- 1 | module BitClust 2 | VERSION = "0.9.1" 3 | end 4 | -------------------------------------------------------------------------------- /theme/lillia/rurema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby/bitclust/master/theme/lillia/rurema.png -------------------------------------------------------------------------------- /theme/default/rurema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby/bitclust/master/theme/default/rurema.png -------------------------------------------------------------------------------- /theme/default/images/external.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby/bitclust/master/theme/default/images/external.png -------------------------------------------------------------------------------- /data/bitclust/template.epub/rd_file: -------------------------------------------------------------------------------- 1 | <% 2 | headline_init 3 | headline_push 4 | headline_push 5 | %> 6 | <%= compile_rd(@source) %> 7 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/rd_file: -------------------------------------------------------------------------------- 1 | <% 2 | headline_init 3 | headline_push 4 | headline_push 5 | %> 6 | <%= compile_rd(@source) %> 7 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/rd_file: -------------------------------------------------------------------------------- 1 | <% 2 | headline_init 3 | headline_push 4 | headline_push 5 | %> 6 | <%= compile_rd(@source) %> 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | rvm: 2 | - 2.0.0 3 | # - ruby-head 4 | notifications: 5 | recipients: 6 | - ruby-reference-manual-diff@ml.fdiary.net 7 | -------------------------------------------------------------------------------- /view.cgi: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/ruby -Ke 2 | 3 | load './config' 4 | setup_environment 5 | require 'bitclust/interface' 6 | BitClust::Interface.new { bitclust_context() }.main 7 | -------------------------------------------------------------------------------- /lib/bitclust/progress_bar.rb: -------------------------------------------------------------------------------- 1 | 2 | begin 3 | require 'progressbar' 4 | rescue LoadError 5 | require 'bitclust/silent_progress_bar' 6 | ProgressBar = BitClust::SilentProgressBar 7 | end 8 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/nav.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "rake" 4 | gem "progressbar" 5 | 6 | # Specify your gem's dependencies in bitclust.gemspec 7 | gemspec :name => "bitclust" 8 | gemspec :name => "bitclust-dev" 9 | gemspec :name => "refe2" 10 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /lib/bitclust.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust/requesthandler' 2 | require 'bitclust/screen' 3 | require 'bitclust/server' 4 | require 'bitclust/searcher' 5 | require 'bitclust/methoddatabase' 6 | require 'bitclust/functiondatabase' 7 | require 'bitclust/rrdparser' 8 | require 'bitclust/exception' 9 | require 'bitclust/version' 10 | -------------------------------------------------------------------------------- /bin/bitclust: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # bitclust.rb 4 | # 5 | # Copyright (c) 2006-2008 Minero Aoki 6 | # 7 | # This program is free software. 8 | # You can distribute/modify this program under the Ruby License. 9 | # 10 | 11 | require 'bitclust' 12 | require 'bitclust/runner' 13 | 14 | BitClust::Runner.new.run(ARGV) 15 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/doc: -------------------------------------------------------------------------------- 1 | <% @title = @entry.title %> 2 |

3 | <% if @entry.name == 'index' %> 4 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 5 | <% else %> 6 | <%= manual_home_link() %> 7 | > <%=h breadcrumb_title %> 8 | <% end %> 9 |

10 | <% headline_init %> 11 | <%= headline(@entry.title) %> 12 | <% headline_push %> 13 | <%= compile_rd(@entry.source) %> 14 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/doc: -------------------------------------------------------------------------------- 1 | <% @title = @entry.title %> 2 |

3 | <% if @entry.name == 'index' %> 4 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 5 | <% else %> 6 | <%= manual_home_link() %> 7 | > <%=h breadcrumb_title %> 8 | <% end %> 9 |

10 | <% headline_init %> 11 | <%= headline(@entry.title) %> 12 | <% headline_push %> 13 | <%= compile_rd(@entry.source) %> 14 | -------------------------------------------------------------------------------- /lib/bitclust/silent_progress_bar.rb: -------------------------------------------------------------------------------- 1 | 2 | module BitClust 3 | # Null-object version of ProgressBar. 4 | class SilentProgressBar 5 | 6 | attr_reader :title 7 | 8 | def initialize(title, total, out = $stderr) 9 | @title, @total, @out = title, total, out 10 | end 11 | 12 | def inc(step = 1) 13 | end 14 | 15 | def finish 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /data/bitclust/template/doc: -------------------------------------------------------------------------------- 1 | <% @title = @entry.title %> 2 |

3 | <% if @entry.name == 'index' %> 4 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 5 | <% else %> 6 | <%= manual_home_link() %> 7 | > <%=h breadcrumb_title %> 8 | <% end %> 9 |

10 | <%= search_form() %> 11 | <% headline_init %> 12 | <%= headline(@entry.title) %> 13 | <% headline_push %> 14 | <%= compile_rd(@entry.source) %> 15 | -------------------------------------------------------------------------------- /test/run_test.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | gem 'test-unit' 5 | require 'test/unit' 6 | require 'test/unit/notify' 7 | require 'test/unit/rr' 8 | 9 | base_dir = Pathname.new(__FILE__).dirname.expand_path 10 | top_dir = (base_dir + '..').expand_path 11 | lib_dir = top_dir + 'lib' 12 | 13 | $LOAD_PATH.unshift(lib_dir.to_s) 14 | 15 | exit Test::Unit::AutoRunner.run(true, base_dir) 16 | -------------------------------------------------------------------------------- /server.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby -Ku 2 | require 'pathname' 3 | lib_dir = Pathname(File.dirname(__FILE__)) + "lib" 4 | $LOAD_PATH.unshift(lib_dir.expand_path.to_s) 5 | require "bitclust" 6 | require "bitclust/runner" 7 | argv = [ 8 | "server", 9 | "--bind-address=127.0.0.1", 10 | "--baseurl=", 11 | "--debug", 12 | "--auto", 13 | "--capi" 14 | ] 15 | BitClust::Runner.new.run(argv) 16 | -------------------------------------------------------------------------------- /tools/forall-ruby.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pathname' 4 | 5 | bindir = Pathname.new(__FILE__).realpath.dirname 6 | $LOAD_PATH.unshift((bindir + '../lib').realpath) 7 | 8 | require 'bitclust/crossrubyutils' 9 | 10 | include BitClust::CrossRubyUtils 11 | 12 | def main 13 | ENV.delete('GEM_HOME') 14 | forall_ruby(ENV['PATH']) do |ruby, ver| 15 | puts ver 16 | system ruby, *ARGV 17 | end 18 | end 19 | 20 | main 21 | -------------------------------------------------------------------------------- /tools/check-signature.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pathname' 4 | $LOAD_PATH.unshift Pathname($0).realpath.dirname.dirname + 'lib' 5 | 6 | require 'bitclust/methodsignature' 7 | 8 | st = 0 9 | ARGF.each do |line| 10 | if /\A---/ =~ line 11 | begin 12 | BitClust::MethodSignature.parse(line) 13 | rescue => err 14 | $stderr.puts "#{ARGF.filename}:#{ARGF.file.lineno}: #{line.strip.inspect}" 15 | st = 1 16 | end 17 | end 18 | end 19 | exit st 20 | -------------------------------------------------------------------------------- /test/test_methodsignature.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust/methodsignature' 3 | 4 | class TestMethodSignature < Test::Unit::TestCase 5 | 6 | data("special var" => ["$_ -> String | nil", "--- $_ -> String | nil"], 7 | "backquote" => ["`command` -> String", "--- `(command) -> String"]) 8 | def test_friendlyname(data) 9 | friendly_string, method_signature = data 10 | assert_equal(friendly_string, BitClust::MethodSignature.parse(method_signature).friendly_string) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | task :release do 3 | puts "do not use this task. use gem push." 4 | exit 5 | end 6 | 7 | $:.push File.expand_path("../lib", __FILE__) 8 | require "bundler/gem_helper" 9 | require 'bitclust/version' 10 | 11 | task :default => [:test] 12 | 13 | desc "run test" 14 | task :test do 15 | sh 'ruby test/run_test.rb' 16 | end 17 | 18 | Bundler::GemHelper.install_tasks(:name => "bitclust") 19 | Bundler::GemHelper.install_tasks(:name => "bitclust-dev") 20 | Bundler::GemHelper.install_tasks(:name => "refe2") 21 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/function-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Function Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Functions') %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Function Index")) %> 10 | 11 | <% 12 | headline_push 13 | @entries.each do |f| 14 | %> 15 | 16 | 17 | 18 | 19 | <% 20 | end 21 | headline_pop 22 | %> 23 |
<%= function_link(f.name) %><%= compile_rd(f.synopsis_source) %>
24 | 25 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/function-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Function Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Functions') %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Function Index")) %> 10 | 11 | <% 12 | headline_push 13 | @entries.each do |f| 14 | %> 15 | 16 | 17 | 18 | 19 | <% 20 | end 21 | headline_pop 22 | %> 23 |
<%= function_link(f.name) %><%= compile_rd(f.synopsis_source) %>
24 | 25 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/function: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entry 3 | @title = "#{entry.type_label} #{entry.label}" 4 | %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Functions') %> 8 | > <%=h entry.name %> 9 | <% unless entry.public? %>(static)<% end %> 10 |

11 | 12 | <% headline_init %> 13 | <%= headline("#{entry.type_label} #{entry.label}") %> 14 |
15 |
16 | <%= entry.header %> 17 |
18 |
19 | <%= compile_function(entry) %> 20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/function: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entry 3 | @title = "#{entry.type_label} #{entry.label}" 4 | %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Functions') %> 8 | > <%=h entry.name %> 9 | <% unless entry.public? %>(static)<% end %> 10 |

11 | 12 | <% headline_init %> 13 | <%= headline("#{entry.type_label} #{entry.label}") %> 14 |
15 |
16 | <%= entry.header %> 17 |
18 |
19 | <%= compile_function(entry) %> 20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /data/bitclust/template/function-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Function Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Functions') %> 5 |

6 | <%= search_form() %> 7 | <% 8 | headline_init 9 | %> 10 | <%= headline(_("Function Index")) %> 11 | 12 | <% 13 | headline_push 14 | @entries.each do |f| 15 | %> 16 | 17 | 18 | 19 | 20 | <% 21 | end 22 | headline_pop 23 | %> 24 |
<%= function_link(f.name) %><%= compile_rd(f.synopsis_source) %>
25 | 26 | -------------------------------------------------------------------------------- /test/test_rrdparser.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust/rrdparser' 2 | require 'test/unit' 3 | 4 | class TestRRDParser < Test::Unit::TestCase 5 | def test_title 6 | result = BitClust::RRDParser.split_doc < 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Classes') %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Class Index")) %> 10 |

11 | <%= @entries.size %> classes/modules in database 12 |

13 | <% 14 | headline_push 15 | @entries.each do |c| 16 | %> 17 | <%= headline_noescape("#{c.type} #{class_link(c.name)}") %> 18 |

19 | <% 20 | c.entries.sort.each do |m| %> 21 | <%= link_to_method(m) %> 22 | <% end %> 23 |

24 | <% 25 | end 26 | headline_pop 27 | %> 28 | 29 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/class-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Class Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Classes') %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Class Index")) %> 10 |

11 | <%= @entries.size %> classes/modules in database 12 |

13 | <% 14 | headline_push 15 | @entries.each do |c| 16 | %> 17 | <%= headline_noescape("#{c.type} #{class_link(c.name)}") %> 18 |

19 | <% 20 | c.entries.sort.each do |m| %> 21 | <%= link_to_method(m) %> 22 | <% end %> 23 |

24 | <% 25 | end 26 | headline_pop 27 | %> 28 | 29 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/class-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Class Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Classes') %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Class Index")) %> 10 |

11 | <%= @entries.size %> classes/modules in database 12 |

13 | <% 14 | headline_push 15 | @entries.each do |c| 16 | %> 17 | <%= headline_noescape("#{c.type} #{class_link(c.name)}") %> 18 |

19 | <% 20 | c.entries.sort.each do |m| %> 21 | <%= link_to_method(m) %> 22 | <% end %> 23 |

24 | <% 25 | end 26 | headline_pop 27 | %> 28 | 29 | -------------------------------------------------------------------------------- /data/bitclust/template/function: -------------------------------------------------------------------------------- 1 | <% @title = "#{@entry.type_label} #{@entry.label}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Functions') %> 5 | > <%=h @entry.name %> 6 | <% unless @entry.public? %>(static)<% end %> 7 |

8 | <%= search_form() %> 9 | 10 | <% headline_init %> 11 | <%= headline("#{@entry.type_label} #{@entry.label}") %> 12 |
13 |
14 | <%=h @entry.header %> 15 | <% if @entry.macro? then %>[MACRO]<% end %> 16 |
17 |
18 | <%= compile_function(@entry) %> 19 |
20 |
21 | 22 | -------------------------------------------------------------------------------- /data/bitclust/template/class-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Class Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Classes') %> 5 |

6 | <%= search_form() %> 7 | <% 8 | headline_init 9 | %> 10 | <%= headline(_("Class Index")) %> 11 |

12 | <%= @entries.size %> classes/modules in database 13 |

14 | <% 15 | headline_push 16 | @entries.each do |c| 17 | %> 18 | <%= headline_noescape("#{c.type} #{class_link(c.name)}") %> 19 |

20 | <% 21 | c.entries.sort.each do |m| %> 22 | <%= link_to_method(m) %> 23 | <% end %> 24 |

25 | <% 26 | end 27 | headline_pop 28 | %> 29 | 30 | 31 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/query_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class QueryCommand < Subcommand 14 | 15 | def initialize 16 | super 17 | @parser.banner = "Usage: #{File.basename($0, '.*')} query " 18 | end 19 | 20 | def exec(argv, options) 21 | argv.each do |query| 22 | # pp eval(query) # FIXME: causes ArgumentError 23 | p eval(query) 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /config.ru.sample: -------------------------------------------------------------------------------- 1 | # -*- ruby -*- 2 | 3 | a = File.dirname(File.expand_path(__FILE__)) 4 | $LOAD_PATH.unshift(File.join(a, 'lib')) 5 | require 'bitclust/app' 6 | 7 | app = BitClust::App.new( 8 | :baseurl => 'http://localhost:9292', 9 | :dbpath => Dir.glob('db-*'), 10 | :datadir => File.join(a, 'data', 'bitclust'), 11 | :encoding => 'utf-8', 12 | :viewpath => '/view', 13 | :rack => true 14 | ) 15 | 16 | use Rack::ShowExceptions 17 | # use Rack::ShowStatus 18 | use Rack::Lint 19 | use Rack::CommonLogger 20 | use Rack::Static, :urls => ['/theme'], :root => '.' 21 | 22 | map "/" do 23 | run app 24 | end 25 | 26 | app.interfaces.each do |viewpath, interface| 27 | map '/'+viewpath do 28 | run interface 29 | end 30 | end 31 | 32 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%=h @title %> (Ruby <%=h ruby_version %>) 9 | 10 | 11 | <%= yield %> 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /config.in: -------------------------------------------------------------------------------- 1 | # BitClust config sample file. 2 | # Edit this file and rename "config". 3 | 4 | @bc_urlroot = 'http://doc.loveruby.net' 5 | @bc_cgidir = File.dirname(File.expand_path(__FILE__)) 6 | @bc_vardir = @bc_cgidir 7 | @bc_srcdir = @bc_cgidir 8 | 9 | def setup_environment 10 | $KCODE = 'UTF-8' unless Object.const_defined?(:Encoding) 11 | $LOAD_PATH.unshift "#{@bc_srcdir}/lib" 12 | require 'bitclust' 13 | end 14 | 15 | def bitclust_context 16 | db = BitClust::MethodDatabase.new("#{@bc_vardir}/db") 17 | manager = BitClust::ScreenManager.new( 18 | :baseurl => "#{@bc_urlroot}/refm/api/view", 19 | :datadir => "#{@bc_srcdir}/data/bitclust", 20 | :encoding => 'utf-8' 21 | ) 22 | BitClust::RequestHandler.new(db, manager) 23 | end 24 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/method: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entries.sort.first 3 | %> 4 | <% @title = "#{entry.type_label} #{entry.label}" %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Libraries') %> 8 | > library <%= library_link(entry.library.name) %> 9 | > <%= entry.klass.type %> <%= class_link(entry.klass.name) %> 10 | > <% if entry.typename == :special_variable %>$<% end %><%=h entry.name %> 11 | <% unless entry.really_public? %>(<%= entry.visibility %>)<% end %> 12 |

13 | 14 | <% headline_init %> 15 | <%= headline("#{entry.type_label} #{entry.label}") %> 16 |
17 | <% @entries.sort.each do |ent| %> 18 | <%= compile_method(ent) %> 19 | <% end %> 20 |
21 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/method: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entries.sort.first 3 | @title = "#{entry.type_label} #{entry.label}" 4 | %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Libraries') %> 8 | > <%= friendly_library_link(entry.library.name) %> 9 | > <%= class_link(entry.klass.name, _(entry.klass.type.to_s + ' %s', entry.klass.name)) %> 10 | > <% if entry.typename == :special_variable %>$<% end %><%=h entry.name %> 11 | <% unless entry.really_public? %>(<%= entry.visibility %>)<% end %> 12 |

13 | 14 | <% headline_init %> 15 | <%= headline("#{entry.type_label} #{entry.label}") %> 16 |
17 | <% @entries.sort.each do |ent| %> 18 | <%= compile_method(ent) %> 19 | <% end %> 20 |
21 | 22 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/method: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entries.sort.first 3 | @title = "#{entry.type_label} #{entry.label}" 4 | %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Libraries') %> 8 | > <%= friendly_library_link(entry.library.name) %> 9 | > <%= class_link(entry.klass.name, _(entry.klass.type.to_s + ' %s', entry.klass.name)) %> 10 | > <% if entry.typename == :special_variable %>$<% end %><%=h entry.name %> 11 | <% unless entry.really_public? %>(<%= entry.visibility %>)<% end %> 12 |

13 | 14 | <% headline_init %> 15 | <%= headline("#{entry.type_label} #{entry.label}") %> 16 |
17 | <% @entries.sort.each do |ent| %> 18 | <%= compile_method(ent) %> 19 | <% end %> 20 |
21 | 22 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%=h @title %> (Ruby <%=h ruby_version %>) 10 | 11 | 12 | <%= yield %> 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%=h @title %> (Ruby <%=h ruby_version %>) 10 | 11 | 12 | <%= yield %> 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /bin/refe: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pathname' 4 | 5 | bindir = Pathname.new(__FILE__).realpath.dirname 6 | $LOAD_PATH.unshift((bindir + '../lib').realpath) 7 | 8 | unless defined?(::Encoding) 9 | # Ruby 1.8 10 | $KCODE = 'UTF-8' 11 | end 12 | 13 | require 'bitclust/searcher' 14 | 15 | def main 16 | Signal.trap(:PIPE, 'IGNORE') rescue nil # Win32 does not have SIGPIPE 17 | Signal.trap(:INT) { exit 1 } 18 | _main 19 | rescue Errno::EPIPE 20 | exit 0 21 | end 22 | 23 | def _main 24 | refe = BitClust::Searcher.new 25 | refe.parse ARGV 26 | refe.exec ARGV 27 | rescue OptionParser::ParseError => err 28 | $stderr.puts err.message 29 | $stderr.puts refe.parser.help 30 | exit 1 31 | rescue BitClust::UserError => err 32 | $stderr.puts err.message 33 | exit 1 34 | end 35 | 36 | main 37 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | BitClust README 2 | =============== 3 | 4 | About 5 | ----- 6 | 7 | BitClust is the Next-Generation Ruby reference manual interface. 8 | For more details, see https://bugs.ruby-lang.org/projects/rurema/wiki/BitClust . 9 | 10 | 11 | License 12 | ------- 13 | 14 | Ruby License. 15 | 16 | 17 | Glossary 18 | -------- 19 | 20 | * "entry" 21 | * Each target of documentation. 22 | * classes: Entry LibraryEntry ClassEntry MethodEntry DocEntry FunctionEntry 23 | * "screen" 24 | * View class. 25 | * Used by BitClust server, chm_command, statichtml_command, etc. 26 | * "singleton object" 27 | * ARGF, main, etc. 28 | * "BitClust server" 29 | * HTTP server application to view refenence on browser. 30 | * "Refe server" 31 | * DRb server spawned by refe --server. 32 | * Expose BitClust DB via DRuby. 33 | -------------------------------------------------------------------------------- /data/bitclust/template/method: -------------------------------------------------------------------------------- 1 | <% 2 | entry = @entries.sort.first 3 | @title = "#{entry.type_label} #{entry.label}" 4 | %> 5 |

6 | <%= manual_home_link() %> 7 | > <%= _('All Libraries') %> 8 | > <%= friendly_library_link(entry.library.name) %> 9 | > <%= class_link(entry.klass.name, _(entry.klass.type.to_s + ' %s', entry.klass.name)) %> 10 | > <% if entry.typename == :special_variable %>$<% end %><%=h entry.name %> 11 | <% unless entry.really_public? %>(<%= entry.visibility %>)<% end %> 12 |

13 | <%= search_form() %> 14 | 15 | 16 | <% headline_init %> 17 | <%= headline("#{entry.type_label} #{entry.label}") %> 18 | <% headline_push %> 19 |
20 | <% @entries.sort.each do |ent| %> 21 | <%= compile_method(ent) %> 22 | <% end %> 23 |
24 | 25 | -------------------------------------------------------------------------------- /lib/bitclust/compat.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Compatibility for older rubies 3 | # 4 | 5 | unless Object.method_defined?(:__send) 6 | class Object 7 | alias __send __send__ 8 | end 9 | end 10 | 11 | unless Object.method_defined?(:funcall) 12 | class Object 13 | alias funcall __send 14 | end 15 | end 16 | 17 | unless Fixnum.method_defined?(:ord) 18 | class Fixnum 19 | def ord 20 | self 21 | end 22 | end 23 | end 24 | 25 | unless String.method_defined?(:lines) 26 | class String 27 | alias lines to_a 28 | end 29 | end 30 | 31 | unless String.method_defined?(:bytesize) 32 | class String 33 | alias bytesize size 34 | end 35 | end 36 | 37 | def fopen(*args, &block) 38 | option = args[1] 39 | if option and !Object.const_defined?(:Encoding) 40 | args[1] = option.sub(/:.*\z/, '') 41 | end 42 | File.open(*args, &block) 43 | end 44 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/library-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Library Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _("All Libraries") %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Library Index")) %> 10 | 11 | <% 12 | headline_push 13 | lib = @entries.detect {|lib| lib.id == '_builtin' } 14 | %> 15 | 16 | 17 | 18 | 19 | <% 20 | @entries.each do |lib| 21 | next if lib.id == '_builtin' 22 | next if lib.is_sublibrary 23 | %> 24 | 25 | 26 | 27 | 28 | <% 29 | end 30 | headline_pop 31 | %> 32 |
<%= library_link(lib.name, _('Builtin Library')) %><%= compile_rd(lib.synopsis_source) %>
<%= library_link(lib.name) %><%= compile_rd(lib.synopsis_source) %>
33 | -------------------------------------------------------------------------------- /data/bitclust/template/layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%=h @title %> (Ruby <%=h ruby_version %>) 9 | 10 | 11 | 12 | <%= yield %> 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/preproc_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'optparse' 3 | 4 | require 'bitclust/rrdparser' 5 | 6 | module BitClust 7 | module Subcommands 8 | class PreprocCommand < Subcommand 9 | 10 | def initialize 11 | super 12 | @params = { "version" => "2.0.0" } 13 | @parser.banner = "Usage: #{File.basename($0, '.*')} ..." 14 | @parser.on('--param=KVPAIR', 'Set parameter by key/value pair.') {|pair| 15 | key, value = pair.split('=', 2) 16 | params[key] = value 17 | } 18 | end 19 | 20 | def exec(argv, options) 21 | argv.each do |path| 22 | File.open(path) {|file| 23 | Preprocessor.wrap(file, @params).each do |line| 24 | puts line 25 | end 26 | } 27 | end 28 | rescue WriterError => err 29 | $stderr.puts err.message 30 | exit 1 31 | end 32 | 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /refe2.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require 'rake' 4 | require "bitclust/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "refe2" 8 | s.version = BitClust::VERSION 9 | s.authors = ["http://bugs.ruby-lang.org/projects/rurema"] 10 | s.email = [""] 11 | s.homepage = "http://doc.ruby-lang.org/ja/" 12 | s.summary = %Q!BitClust is a rurema document processor.! 13 | s.description =< 2 | 3 | 4 | d715e247-34f4-4a0e-9a3f-b3d2c747cfb0-2.0.0 5 | Ruby リファレンスマニュアル 6 | ja 7 | <%= last_modified %> 8 | 9 | 10 | 11 | 12 | <% items.each do |item| -%> 13 | 14 | <% end -%> 15 | 16 | 17 | 18 | 19 | <% items.each do |item| -%> 20 | 21 | <% end -%> 22 | 23 | 24 | -------------------------------------------------------------------------------- /data/bitclust/template/opensearchdescription: -------------------------------------------------------------------------------- 1 | "?> 2 | 4 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 5 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 6 | data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%02%00%00%00%90%91h6%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%06tRNS%00%FF%00%FF%00%FF7X%1B%7D%00%00%00TIDAT(%CFc%F8%0F%06%0C%C4%01%90J%88jc%E3U%10%84%A9%08Y%0AC%03P%08%03%19%C3d%D150%80%A5%D3PUC%B8%08%05%98%1A%D0%7C%F3%9FH%0D%98%8C%A1%A7%81%04O%93%10%AC%24G%1CiI%83%E4%C4w%86%10%80%A8%86j%20)y%03%00%B7%DF%E3P%F6%95%E2%F8%00%00%00%00IEND%AEB%60%82 7 | <%=h encoding() %> 8 | 9 | <%=h search_full_url() %> 10 | 11 | -------------------------------------------------------------------------------- /lib/bitclust/textutils.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/textutils 3 | # 4 | # Copyright (c) 2006 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | module BitClust 11 | 12 | # Utility for tweaking text. 13 | module TextUtils 14 | 15 | module_function 16 | 17 | def detab(str, ts = 8) 18 | add = 0 19 | str.gsub(/\t/) { 20 | len = ts - ($~.begin(0) + add) % ts 21 | add += len - 1 22 | ' ' * len 23 | } 24 | end 25 | 26 | def unindent_block(lines) 27 | n = n_minimum_indent(lines) 28 | lines.map {|line| unindent(line, n) } 29 | end 30 | 31 | def n_minimum_indent(lines) 32 | lines.reject {|line| line.strip.empty? }.map {|line| n_indent(line) }.min 33 | end 34 | 35 | def n_indent(line) 36 | line.slice(/\A\s*/).size 37 | end 38 | 39 | INDENT_RE = { 40 | 2 => /\A {2}/, 41 | 4 => /\A {4}/, 42 | 8 => /\A {8}/ 43 | } 44 | 45 | def unindent(line, n) 46 | re = (INDENT_RE[n] ||= /\A {#{n}}/) 47 | line.sub(re, '') 48 | end 49 | 50 | end 51 | 52 | end 53 | -------------------------------------------------------------------------------- /test/test_simplesearcher.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust' 3 | require 'bitclust/simplesearcher' 4 | require 'optparse' 5 | 6 | 7 | class TestSearcher < Test::Unit::TestCase 8 | 9 | def setup 10 | s = < 'db', 22 | :viewpath => "/view/", 23 | :rack => true, 24 | :capi => true 25 | ) 26 | app.interfaces.each do |viewpath, interface| 27 | map viewpath do 28 | run interface 29 | end 30 | end 31 | else 32 | app = BitClust::App.new( 33 | :dbpath => dbpath, 34 | :rack => true, 35 | :capi => true 36 | ) 37 | app.interfaces.each do |version, interface| 38 | map "#{basepath}/#{version}/" do 39 | run interface 40 | end 41 | end 42 | end 43 | 44 | map "#{basepath}/" do 45 | run app 46 | end 47 | 48 | map File.join(basepath, 'theme/') do 49 | run Rack::File.new(themedir) 50 | end 51 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/doc: -------------------------------------------------------------------------------- 1 | <% @title = @entry.title %> 2 |
3 |

4 | <% if @entry.name == 'index' %> 5 | <%= _('Ruby %s Reference Manual', ruby_version()) %> 6 | <% else %> 7 | <%= manual_home_link() %> 8 | > <%=h breadcrumb_title %> 9 | <% end %> 10 |

11 | <% headline_init %> 12 | <%= headline(@entry.title) %> 13 | <% headline_push %> 14 | <%= compile_rd(@entry.source) %> 15 |
16 | 17 |
18 | 19 | 20 |
21 |

Libraries

22 |
23 | 24 |
25 |
    26 | <% 27 | @entry.libraries.sort.each do |lib| 28 | %> 29 |
  • <%= library_link(lib.name) %>
  • 30 | <% end %> 31 |
32 |
33 | 34 |
35 |

Classes

36 |
37 | 38 |
39 |
    40 | <% 41 | ((@entry.classes - @entry.error_classes).sort + @entry.error_classes.sort).each do |c| 42 | %> 43 |
  • <%= class_link(c.name, "#{c.name}") %>
  • 44 | <% end %> 45 |
46 |
47 | 48 |
49 | -------------------------------------------------------------------------------- /lib/bitclust/server.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/server.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/methoddatabase' 11 | require 'bitclust/functiondatabase' 12 | require 'bitclust/libraryentry' 13 | require 'bitclust/classentry' 14 | require 'bitclust/methodentry' 15 | require 'bitclust/docentry' 16 | require 'drb' 17 | require 'webrick/server' 18 | 19 | module BitClust 20 | 21 | # Body of Refe server (spanwed by `refe --server`). 22 | class Server 23 | 24 | def initialize(db) 25 | @db = db 26 | end 27 | 28 | def listen(url, foreground = false) 29 | WEBrick::Daemon.start unless foreground 30 | DRb.start_service url, @db 31 | DRb.thread.join 32 | end 33 | 34 | end 35 | 36 | class Database # reopen 37 | include DRb::DRbUndumped 38 | end 39 | 40 | class Entry # reopen 41 | include DRb::DRbUndumped 42 | end 43 | 44 | class SearchResult # reopen 45 | include DRb::DRbUndumped 46 | end 47 | 48 | end 49 | 50 | class Object 51 | def _remote_object? 52 | false 53 | end 54 | end 55 | 56 | class DRb::DRbObject 57 | def _remote_object? 58 | true 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/library-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Library Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _("All Libraries") %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Library Index")) %> 10 | <% 11 | headline_push 12 | weight = {"Builtin" => "", "" => "\x7f\x7f"} 13 | entry_table = @entries.group_by{|lib| lib.category }.to_a 14 | entry_table.sort_by! do |pair| 15 | category = pair[0] 16 | pair.last.sort! 17 | pair[0] = "Other" if category == "" 18 | weight[category] || category 19 | end 20 | first = true 21 | entry_table.each do |category, libs| 22 | %> 23 | <% if first then first = false else %> / <% end %> 24 | <%= h _(category) %> 25 | <% 26 | end 27 | entry_table.each do |category, libs| 28 | %> 29 | <%= headline(_(category)) %> 30 | 31 | <% 32 | libs.each do |lib| 33 | next if lib.is_sublibrary 34 | label = _('Builtin Library') if lib.id == '_builtin' 35 | %> 36 | 37 | 38 | 39 | 40 | <% 41 | end 42 | %> 43 |
<%= library_link(lib.name, label) %><%= compile_rd(lib.synopsis_source) %>
44 | <% 45 | end 46 | headline_pop 47 | %> 48 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/library-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Library Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _("All Libraries") %> 5 |

6 | <% 7 | headline_init 8 | %> 9 | <%= headline(_("Library Index")) %> 10 | <% 11 | headline_push 12 | weight = {"Builtin" => "", "" => "\x7f\x7f"} 13 | entry_table = @entries.group_by{|lib| lib.category }.to_a 14 | entry_table.sort_by! do |pair| 15 | category = pair[0] 16 | pair.last.sort! 17 | pair[0] = "Other" if category == "" 18 | weight[category] || category 19 | end 20 | first = true 21 | entry_table.each do |category, libs| 22 | %> 23 | <% if first then first = false else %> / <% end %> 24 | <%= h _(category) %> 25 | <% 26 | end 27 | entry_table.each do |category, libs| 28 | %> 29 | <%= headline(_(category)) %> 30 | 31 | <% 32 | libs.each do |lib| 33 | next if lib.is_sublibrary 34 | label = _('Builtin Library') if lib.id == '_builtin' 35 | %> 36 | 37 | 38 | 39 | 40 | <% 41 | end 42 | %> 43 |
<%= library_link(lib.name, label) %><%= compile_rd(lib.synopsis_source) %>
44 | <% 45 | end 46 | headline_pop 47 | %> 48 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/init_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class InitCommand < Subcommand 14 | def initialize 15 | super 16 | @parser.banner = "Usage: #{File.basename($0, '.*')} init [KEY=VALUE ...]" 17 | end 18 | 19 | STANDARD_PROPERTIES = %w( encoding version ) 20 | 21 | def exec(argv, options) 22 | prefix = options[:prefix] 23 | db = MethodDatabase.new(prefix) 24 | db.init 25 | db.transaction { 26 | argv.each do |kv| 27 | k, v = kv.split('=', 2) 28 | db.propset k, v 29 | end 30 | } 31 | fail = false 32 | STANDARD_PROPERTIES.each do |key| 33 | unless db.propget(key) 34 | $stderr.puts "#{File.basename($0, '.*')}: warning: standard property `#{key}' not given" 35 | fail = true 36 | end 37 | end 38 | if fail 39 | $stderr.puts "---- Current Properties ----" 40 | db.properties.each do |key, value| 41 | $stderr.puts "#{key}=#{value}" 42 | end 43 | end 44 | end 45 | end 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /bitclust.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | $:.push File.expand_path("../lib", __FILE__) 3 | require 'rake' 4 | require "bitclust/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "bitclust-core" 8 | s.version = BitClust::VERSION 9 | s.authors = ["http://bugs.ruby-lang.org/projects/rurema"] 10 | s.email = [""] 11 | s.homepage = "http://doc.ruby-lang.org/ja/" 12 | s.summary = %Q!BitClust is a rurema document processor.! 13 | s.description =<= 2.3.0" 30 | s.add_development_dependency "test-unit-notify" 31 | s.add_development_dependency "test-unit-rr" 32 | s.add_runtime_dependency "rack" 33 | s.add_runtime_dependency "progressbar" 34 | end 35 | -------------------------------------------------------------------------------- /data/bitclust/template/library-index: -------------------------------------------------------------------------------- 1 | <% @title = _('Library Index') %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _("All Libraries") %> 5 |

6 | <%= search_form() %> 7 | 8 | <% 9 | headline_init 10 | %> 11 | <%= headline(_("Library Index")) %> 12 | <% 13 | headline_push 14 | weight = {"Builtin" => "", "" => "\x7f\x7f"} 15 | entry_table = @entries.group_by{|lib| lib.category }.to_a 16 | entry_table.sort_by! do |pair| 17 | category = pair[0] 18 | pair.last.sort! 19 | pair[0] = "Other" if category == "" 20 | weight[category] || category 21 | end 22 | first = true 23 | entry_table.each do |category, libs| 24 | %> 25 | <% if first then first = false else %> / <% end %> 26 | <%= h _(category) %> 27 | <% 28 | end 29 | entry_table.each do |category, libs| 30 | %> 31 | <%= headline(_(category)) %> 32 | 33 | <% 34 | libs.each do |lib| 35 | next if lib.is_sublibrary 36 | label = _('Builtin Library') if lib.id == '_builtin' 37 | %> 38 | 39 | 40 | 41 | 42 | <% 43 | end 44 | %> 45 |
<%= library_link(lib.name, label) %><%= compile_rd(lib.synopsis_source) %>
46 | <% 47 | end 48 | headline_pop 49 | %> 50 | 51 | -------------------------------------------------------------------------------- /test/test_functionreferenceparser.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust' 3 | require 'bitclust/functionreferenceparser' 4 | 5 | class TestFunctionReferenceParser < Test::Unit::TestCase 6 | def setup 7 | prefix = 'db' 8 | src = "test.rd" 9 | 10 | @pwd = Dir.pwd 11 | Dir.chdir(@tmpdir = Dir.mktmpdir) 12 | File.open(src, 'w') do |file| 13 | file.puts <<'HERE' 14 | --- VALUE func() 15 | #@since 2.0.0 16 | some text 1 17 | #@else 18 | some text 2 19 | #@end 20 | HERE 21 | end 22 | @path = File.join(@tmpdir, src) 23 | @db = BitClust::FunctionDatabase.new(prefix) 24 | @parser = BitClust::FunctionReferenceParser.new(@db) 25 | end 26 | 27 | def teardown 28 | Dir.chdir @pwd 29 | FileUtils.rm_r(@tmpdir, :force => true) 30 | end 31 | 32 | data("1.9.3" => { 33 | :version => "1.9.3", 34 | :expected => ["some text 2\n"], 35 | }, 36 | "2.0.0" => { 37 | :version => "2.0.0", 38 | :expected => ["some text 1\n"], 39 | }, 40 | "2.1.0" => { 41 | :version => "2.1.0", 42 | :expected => ["some text 1\n"], 43 | }) 44 | def test_parse_file(data) 45 | @db.transaction { 46 | result = 47 | @parser.parse_file(@path, "test.c", {"version" => data[:version]}) 48 | assert_equal data[:expected], result.collect(&:source) 49 | } 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/test_functiondatabase.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust' 3 | require 'bitclust/functiondatabase' 4 | require 'tmpdir' 5 | require 'fileutils' 6 | 7 | class TesFunctionDatabase < Test::Unit::TestCase 8 | def setup 9 | prefix = 'db' 10 | src = "test.rd" 11 | 12 | @pwd = Dir.pwd 13 | Dir.chdir(@tmpdir = Dir.mktmpdir) 14 | File.open(src, 'w') do |file| 15 | file.puts <<'HERE' 16 | --- VALUE func1() 17 | 18 | some text 19 | 20 | --- VALUE func2() 21 | 22 | some text 23 | HERE 24 | end 25 | @db = BitClust::FunctionDatabase.new(prefix) 26 | @db.transaction { 27 | @db.update_by_file(src, src) 28 | } 29 | end 30 | 31 | def teardown 32 | Dir.chdir @pwd 33 | FileUtils.rm_r(@tmpdir, :force => true) 34 | end 35 | 36 | def test_search_functions__function 37 | result = @db.search_functions('func1') 38 | assert_not_nil result.first 39 | assert_equal 1, result.size 40 | assert_equal 'func1', result.first.name 41 | end 42 | 43 | def test_search_functions__functions 44 | result = @db.search_functions('func') 45 | assert_not_nil result.first 46 | assert_equal 2, result.size 47 | assert_equal %w[func1 func2], result.map(&:name) 48 | end 49 | 50 | def test_search_functions__nonexistent 51 | assert_raise(BitClust::FunctionNotFound) do 52 | @db.search_functions('nonexistent') 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/bitclust/subcommand.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | require 'bitclust' 4 | require 'erb' 5 | require 'find' 6 | require 'pp' 7 | require 'optparse' 8 | require 'yaml' 9 | 10 | module BitClust 11 | 12 | # Base class for bitclust subcommands. 13 | class Subcommand 14 | def initialize 15 | @parser = OptionParser.new 16 | @parser.on_tail("-h", "--help", "Print this message and quit."){ 17 | $stderr.puts help 18 | exit 0 19 | } 20 | end 21 | 22 | def parse(argv) 23 | @parser.parse! argv 24 | end 25 | 26 | def help 27 | @parser.help 28 | end 29 | 30 | def exec(argv, options) 31 | prefix = options[:prefix] 32 | error("no database given. Use --database option") unless prefix 33 | if options[:capi] 34 | @db = BitClust::FunctionDatabase.new(prefix) 35 | else 36 | @db = BitClust::MethodDatabase.new(prefix) 37 | end 38 | end 39 | 40 | # TODO refactor 41 | def error(message) 42 | $stderr.puts "#{File.basename($0, '.*')}: error: #{message}" 43 | exit 1 44 | end 45 | 46 | def option_error(message) 47 | $stderr.puts message 48 | $stderr.puts help 49 | exit 1 50 | end 51 | 52 | def srcdir_root 53 | Pathname.new(__FILE__).realpath.dirname.parent.parent 54 | end 55 | end 56 | end 57 | 58 | module BitClust 59 | module Subcommands 60 | end 61 | end 62 | 63 | -------------------------------------------------------------------------------- /lib/bitclust/parseutils.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/parseutils.rb 3 | # 4 | # Copyright (c) 2006-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/exception' 11 | 12 | class String # reopen 13 | attr_accessor :location 14 | end 15 | 16 | module BitClust 17 | 18 | # Provides line-wise access to a file with :file and :line. 19 | # Used by Preprocessor 20 | class LineStream 21 | def initialize(f) 22 | @f = f 23 | end 24 | 25 | def gets 26 | line = @f.gets 27 | return nil unless line 28 | if @f.respond_to?(:path) 29 | path = @f.path 30 | else 31 | path = nil 32 | end 33 | line.location = Location.new(path, @f.lineno) 34 | line 35 | end 36 | end 37 | 38 | # Encapsulates :file and :line. 39 | # Used by LineStream(above) 40 | class Location 41 | def initialize(file, line) 42 | @file = file 43 | @line = line 44 | end 45 | 46 | attr_reader :file 47 | attr_reader :line 48 | 49 | def to_s 50 | "#{@file}:#{@line}" 51 | end 52 | 53 | def inspect 54 | "\#<#{self.class} #{@file}:#{@line}>" 55 | end 56 | end 57 | 58 | # Utilities for parsing 59 | module ParseUtils 60 | def parse_error(msg, line) 61 | raise ParseError, "#{line.location}: #{msg}: #{line.inspect}" 62 | end 63 | end 64 | 65 | end 66 | -------------------------------------------------------------------------------- /lib/bitclust/exception.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/exception.rb 3 | # 4 | # Copyright (c) 2006-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | module BitClust 11 | class Error < StandardError; end 12 | class RequestError < Error; end 13 | class NotInTransaction < Error; end 14 | class DocumentError < Error; end 15 | class ScanError < DocumentError; end 16 | class ParseError < DocumentError; end 17 | class WrongInclude < DocumentError; end 18 | class InvalidLink < DocumentError; end 19 | class InvalidAncestor < DocumentError; end 20 | class InvalidLibrary < DocumentError; end 21 | class UserError < Error; end 22 | class InvalidDatabase < UserError; end 23 | class InvalidKey < UserError; end 24 | class InvalidScheme < UserError; end 25 | class NotFoundError < UserError; end 26 | class LibraryNotFound < NotFoundError; end 27 | class ClassNotFound < NotFoundError; end 28 | class MethodNotFound < NotFoundError; end 29 | class FunctionNotFound < NotFoundError; end 30 | class DocNotFound < NotFoundError; end 31 | 32 | module WriterError; end 33 | class DocumentError 34 | include WriterError 35 | end 36 | class UserError 37 | include WriterError 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/bitclust/docentry.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/docentry.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/entry' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | # Entry for general documents (doc/**/*.rd, etc.) 16 | class DocEntry < Entry 17 | 18 | def self.type_id 19 | :doc 20 | end 21 | 22 | def initialize(db, id) 23 | super db 24 | @id = id 25 | init_properties 26 | end 27 | 28 | attr_reader :id 29 | 30 | def ==(other) 31 | @id == other.id 32 | end 33 | 34 | alias eql? == 35 | 36 | def hash 37 | @id.hash 38 | end 39 | 40 | def <=>(other) 41 | @id.casecmp(other.id) 42 | end 43 | 44 | def name 45 | libid2name(@id) 46 | end 47 | 48 | alias label name 49 | 50 | def labels 51 | [label()] 52 | end 53 | 54 | def name?(n) 55 | name() == n 56 | end 57 | 58 | persistent_properties { 59 | property :title, 'String' 60 | property :source, 'String' 61 | } 62 | 63 | def inspect 64 | "#" 65 | end 66 | 67 | def classes 68 | @db.classes 69 | end 70 | 71 | def error_classes 72 | classes.select{|c| c.error_class? } 73 | end 74 | 75 | def methods 76 | @db.methods 77 | end 78 | 79 | def libraries 80 | @db.libraries 81 | end 82 | end 83 | 84 | end 85 | -------------------------------------------------------------------------------- /test/test_entry.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust' 2 | require 'test/unit' 3 | 4 | class TestClassEntry < Test::Unit::TestCase 5 | def setup 6 | s = < err 27 | $stderr.puts err 28 | $stderr.puts parser.help 29 | exit 1 30 | end 31 | 32 | h = collect_messages(ARGF) 33 | h.update load_catalog(catalog_path) if catalog_path 34 | print_catalog h 35 | end 36 | 37 | def print_catalog(h) 38 | h.keys.sort.each do |key| 39 | puts key 40 | puts h[key] 41 | end 42 | end 43 | 44 | def collect_messages(f) 45 | h = {} 46 | f.each do |line| 47 | line.scan(/_\( 48 | (?: "( (?:[^"]+|\\.)* )" 49 | | '( (?:[^']+|\\.)* )' 50 | ) 51 | /x) do 52 | text = ($1 || $2).strip 53 | h[text] = text unless text.empty? 54 | end 55 | end 56 | h 57 | end 58 | 59 | def load_catalog(path) 60 | h = {} 61 | File.open(path) {|f| 62 | f.each do |line| 63 | h[line.chomp] = f.gets.chomp 64 | end 65 | } 66 | h 67 | end 68 | 69 | main 70 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/update_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class UpdateCommand < Subcommand 14 | 15 | def initialize 16 | super 17 | @root = nil 18 | @library = nil 19 | @parser.banner = "Usage: #{File.basename($0, '.*')} update [...]" 20 | @parser.on('--stdlibtree=ROOT', 'Process stdlib source directory tree.') {|path| 21 | @root = path 22 | } 23 | @parser.on('--library-name=NAME', 'Use NAME for library name in file mode.') {|name| 24 | @library = name 25 | } 26 | end 27 | 28 | def parse(argv) 29 | super 30 | if not @root and argv.empty? 31 | error "no input file given" 32 | end 33 | end 34 | 35 | def exec(argv, options) 36 | super 37 | @db.transaction { 38 | if @root 39 | @db.update_by_stdlibtree @root 40 | end 41 | argv.each do |path| 42 | @db.update_by_file path, @library || guess_library_name(path) 43 | end 44 | } 45 | end 46 | 47 | private 48 | 49 | def guess_library_name(path) 50 | if %r<(\A|/)src/> =~ path 51 | path.sub(%r<.*(\A|/)src/>, '').sub(/\.rd\z/, '') 52 | else 53 | path 54 | end 55 | end 56 | 57 | def get_c_filename(path) 58 | File.basename(path, '.rd') 59 | end 60 | 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/extract_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'optparse' 3 | 4 | require 'bitclust' 5 | require 'bitclust/subcommand' 6 | 7 | module BitClust 8 | module Subcommands 9 | class ExtractCommand < Subcommand 10 | 11 | def initialize 12 | super 13 | @parser.banner = "Usage: #{File.basename($0, '.*')} ..." 14 | @parser.on('-c', '--check-only', 'Check syntax and output status.') { 15 | @check_only = true 16 | } 17 | end 18 | 19 | def exec(argv, options) 20 | success = true 21 | argv.each do |path| 22 | begin 23 | lib = RRDParser.parse_stdlib_file(path) 24 | if @check_only 25 | $stderr.puts "#{path}: OK" 26 | else 27 | show_library lib 28 | end 29 | rescue WriterError => err 30 | raise if $DEBUG 31 | $stderr.puts "#{File.basename($0, '.*')}: FAIL: #{err.message}" 32 | success = false 33 | end 34 | end 35 | exit success 36 | end 37 | 38 | def show_library(lib) 39 | puts "= Library #{lib.name}" 40 | lib.classes.each do |c| 41 | puts "#{c.type} #{c.name}" 42 | c.each do |m| 43 | puts "\t* #{m.klass.name}#{m.typemark}#{m.names.join(',')}" 44 | end 45 | end 46 | unless lib.methods.empty? 47 | puts "Additional Methods:" 48 | lib.methods.each do |m| 49 | puts "\t* #{m.klass.name}#{m.typemark}#{m.names.join(',')}" 50 | end 51 | end 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /data/bitclust/catalog/ja_JP.UTF-8: -------------------------------------------------------------------------------- 1 | Abstract 2 | 要約 3 | Added Methods 4 | 追加されるメソッド 5 | Added/Redefined Methods 6 | 追加・再定義されるメソッド 7 | All Classes 8 | クラス一覧 9 | All Functions 10 | 関数一覧 11 | All Libraries 12 | ライブラリ一覧 13 | Builtin 14 | 組み込み 15 | Builtin Library 16 | 組み込みライブラリ 17 | Classes 18 | クラス 19 | CharacterEncoding 20 | 文字コード 21 | Class Index 22 | クラス一覧 23 | Classes/Modules 24 | クラスとモジュール 25 | CommandLine 26 | コマンドライン 27 | Constants 28 | 定数 29 | Database 30 | データベース 31 | Date/Time 32 | 日付・時間 33 | Description 34 | 説明 35 | DesignPattern 36 | デザインパターン 37 | Development 38 | 開発 39 | Entry 40 | エントリ 41 | Exception Classes 42 | 例外クラス 43 | File 44 | ファイル 45 | FileFormat 46 | ファイルフォーマット 47 | Function Index 48 | 関数一覧 49 | Index 50 | 目次 51 | I/O 52 | 入出力 53 | Inherited Methods 54 | 継承したメソッド 55 | Instance Methods 56 | インスタンスメソッド 57 | Library 58 | ライブラリ 59 | Library Index 60 | ライブラリ一覧 61 | Math 62 | 数学 63 | Method 64 | メソッド 65 | Modules 66 | モジュール 67 | Module Functions 68 | モジュール関数 69 | Network 70 | ネットワーク 71 | Other 72 | その他 73 | Objects 74 | オブジェクト 75 | Private Instance Methods 76 | privateメソッド 77 | Private Singleton Methods 78 | private特異メソッド 79 | Required Libraries 80 | 同時にrequireされるライブラリ 81 | %d Results 82 | %d件ヒット 83 | Ruby %s Reference Manual 84 | Ruby %s リファレンスマニュアル 85 | Search 86 | 検索 87 | Search Results 88 | 検索結果 89 | Signature 90 | 定義 91 | Singleton Methods 92 | 特異メソッド 93 | Special Variables 94 | 特殊変数 95 | Sub-Libraries 96 | サブライブラリ 97 | Text 98 | テキスト 99 | Thread 100 | スレッド 101 | ancestors 102 | クラスの継承リスト 103 | class %s 104 | %sクラス 105 | library %s 106 | %sライブラリ 107 | module %s 108 | %sモジュール 109 | object %s 110 | %sオブジェクト 111 | -------------------------------------------------------------------------------- /data/bitclust/template/search: -------------------------------------------------------------------------------- 1 |

2 | <%= manual_home_link() %> 3 | > <%=h _('Search Results') %> 4 |

5 | <% 6 | headline_init 7 | %> 8 | <%= headline(_('Search Results')) %> 9 |
10 | 11 | 12 |
13 |

14 | 15 | 16 | 17 | 18 | 21 | 22 | <% unless @entries.empty? %> 23 | 24 | 25 | 26 | 27 | <% end %> 28 | <% 29 | headline_push 30 | @entries.each do |e| 31 | foreach_method_chunk(e.source) do |sigs, src| %> 32 | 33 | <% case e.type_id 34 | when :method %> 35 | 45 | 46 | <% when :class %> 47 | 48 | 49 | <% end %> 50 | 51 | <% 52 | end 53 | end 54 | headline_pop 55 | %> 56 |
19 | <%=h _('%d Results', @entries.size) %> <%=h '(%.3f sec)' % @elapsed_time %> 20 |
<%=h _('Entry') %><%=h _('Description') %>
<%= 36 | case e.type 37 | when :special_variable 38 | e.names.map {|name| method_link(e.spec_string, '$' + name) } 39 | else 40 | sigs.map {|sig| 41 | method_link(e.spec_string, 42 | e.klass.name + e.typemark + sig.to_s) 43 | } 44 | end.join("
\n") %>
<%= compile_rd(src) %><%= class_link e.name %><%= compile_rd(src) %>
57 | 58 | -------------------------------------------------------------------------------- /lib/bitclust/functionentry.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/functionentry.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/entry' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | class FunctionEntry < Entry 16 | 17 | def FunctionEntry.type_id 18 | :function 19 | end 20 | 21 | def initialize(db, id) 22 | super db 23 | @id = id 24 | init_properties 25 | end 26 | 27 | def inspect 28 | "\#" 29 | end 30 | 31 | def name_match?(re) 32 | re =~ name() 33 | end 34 | 35 | def <=>(other) 36 | @id.casecmp(other.id) 37 | end 38 | 39 | persistent_properties { 40 | property :filename, 'String' 41 | property :macro, 'bool' 42 | property :private, 'bool' 43 | property :type, 'String' 44 | property :name, 'String' 45 | property :params, 'String' 46 | property :source, 'String' 47 | } 48 | 49 | attr_reader :id 50 | remove_method :name 51 | alias name id 52 | alias label id 53 | 54 | alias macro? macro 55 | alias private? private 56 | 57 | def public? 58 | not private? 59 | end 60 | 61 | def callable? 62 | not params().empty? 63 | end 64 | 65 | def type_label 66 | macro? ? 'macro' : 'function' 67 | end 68 | alias kind type_label 69 | 70 | def header 71 | if callable? 72 | base = "#{type()} #{name()}#{params()}" 73 | else 74 | base = "#{type()} #{name()}" 75 | end 76 | "#{private? ? 'static ' : ''}#{base}" 77 | end 78 | 79 | end 80 | 81 | end 82 | -------------------------------------------------------------------------------- /lib/bitclust/refsdatabase.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/refsdatabase.rb 3 | # 4 | # This program is free software. 5 | # You can distribute this program under the Ruby License. 6 | # 7 | 8 | module BitClust 9 | 10 | # Corresponds to db-x.y.z/refs file. 11 | class RefsDatabase 12 | def self.load(src) 13 | if src.respond_to?(:to_str) 14 | buf = fopen(src.to_str, 'r:UTF-8'){|f| f.read} 15 | elsif src.respond_to?(:to_io) 16 | buf = src.to_io.read 17 | else 18 | buf = src.read 19 | end 20 | 21 | refs = self.new 22 | buf.each_line{|l| 23 | if /((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+),((?:\\,|[^,])+)\n/ =~ l 24 | type, id, linkid, desc = [$1, $2, $3, $4].map{|e| e.gsub(/\\(.)/){|s| $1 == ',' ? ',' : s } } 25 | refs[type, id, linkid] = desc 26 | end 27 | } 28 | refs 29 | end 30 | 31 | def initialize 32 | @h = {} 33 | end 34 | 35 | def []=(type, mid, linkid, desc) 36 | @h[[type.to_s, mid, linkid]] = desc 37 | end 38 | 39 | def [](type, mid, linkid) 40 | @h[[type.to_s, mid, linkid]] 41 | end 42 | 43 | def save(s) 44 | if s.respond_to?(:to_str) 45 | path = s.to_str 46 | io = fopen(path, 'w:UTF-8') 47 | elsif s.respond_to?(:to_io) 48 | io = s.to_io 49 | else 50 | io = s 51 | end 52 | 53 | @h.each{|k, v| 54 | io.write( [k, v].flatten.map{|e| e.gsub(/,/, '\\,') }.join(',') + "\n" ) 55 | } 56 | io.close 57 | end 58 | 59 | def extract(entry) 60 | entry.source.each_line{|l| 61 | if /\A={1,6}\[a:(\w+)\] *(.*)/ =~ l 62 | entry.labels.each{|name| 63 | self[entry.class.type_id, name, $1] = $2 64 | } 65 | end 66 | } 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/list_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class ListCommand < Subcommand 14 | def initialize 15 | super 16 | @mode = nil 17 | @parser.banner = "Usage: #{File.basename($0, '.*')} list (--library|--class|--method|--function)" 18 | @parser.on('--library', 'List libraries.') { 19 | @mode = :library 20 | } 21 | @parser.on('--class', 'List classes.') { 22 | @mode = :class 23 | } 24 | @parser.on('--method', 'List methods.') { 25 | @mode = :method 26 | } 27 | @parser.on('--function', 'List functions (C API).') { 28 | @mode = :function 29 | } 30 | end 31 | 32 | def parse(argv) 33 | super 34 | unless @mode 35 | error 'one of (--library|--class|--method|--function) is required' 36 | end 37 | end 38 | 39 | def exec(argv, options) 40 | super 41 | case @mode 42 | when :library 43 | @db.libraries.map {|lib| lib.name }.sort.each do |name| 44 | puts name 45 | end 46 | when :class 47 | @db.classes.map {|c| c.name }.sort.each do |name| 48 | puts name 49 | end 50 | when :method 51 | @db.classes.sort_by {|c| c.name }.each do |c| 52 | c.entries.sort_by {|m| m.id }.each do |m| 53 | puts m.label 54 | end 55 | end 56 | when :function 57 | @db.functions.sort_by {|f| f.name }.each do |f| 58 | puts f.name 59 | end 60 | else 61 | raise "must not happen: @mode=#{@mode.inspect}" 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/property_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class PropertyCommand < Subcommand 14 | def initialize 15 | super 16 | @mode = nil 17 | @parser.banner = "Usage: #{File.basename($0, '.*')} property [options]" 18 | @parser.on('--list', 'List all properties.') { 19 | @mode = :list 20 | } 21 | @parser.on('--get', 'Get property value.') { 22 | @mode = :get 23 | } 24 | @parser.on('--set', 'Set property value.') { 25 | @mode = :set 26 | } 27 | end 28 | 29 | def parse(argv) 30 | super 31 | unless @mode 32 | error "one of (--list|--get|--set) is required" 33 | end 34 | case @mode 35 | when :list 36 | unless argv.empty? 37 | error "--list requires no argument" 38 | end 39 | when :get 40 | ; 41 | when :set 42 | unless argv.size == 2 43 | error "--set requires just 2 arguments" 44 | end 45 | else 46 | raise "must not happen: #{@mode}" 47 | end 48 | end 49 | 50 | def exec(argv, options) 51 | prefix = options[:prefix] 52 | db = MethodDatabase.new(prefix) 53 | case @mode 54 | when :list 55 | db.properties.each do |key, val| 56 | puts "#{key}=#{val}" 57 | end 58 | when :get 59 | argv.each do |key| 60 | puts db.propget(key) 61 | end 62 | when :set 63 | key, val = *argv 64 | db.transaction { 65 | db.propset key, val 66 | } 67 | else 68 | raise "must not happen: #{@mode}" 69 | end 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/bitclust/interface.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/interface.rb 3 | # 4 | # Copyright (c) 2006-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'webrick/cgi' 11 | require 'webrick/httpservlet/abstract' 12 | begin 13 | require 'fcgi' 14 | rescue LoadError 15 | end 16 | 17 | module BitClust 18 | 19 | # Web server interface for BitClust server 20 | class Interface 21 | 22 | def initialize(webrick_conf = {}) 23 | @webrick_conf = webrick_conf 24 | @handler = ($bitclust_context_cache ||= yield) 25 | end 26 | 27 | # for WEBrick servlet 28 | def get_instance(server) 29 | WEBrickServlet.new(server, @handler) 30 | end 31 | 32 | def main 33 | if fastcgi? 34 | FCGI.new(@webrick_conf).main(@handler) 35 | else 36 | # CGI, mod_ruby 37 | CGI.new(@webrick_conf).main(@handler) 38 | end 39 | end 40 | 41 | # for rack 42 | def call(env) 43 | @handler.handle(Rack::Request.new(env)).rack_finish 44 | end 45 | 46 | private 47 | 48 | def fastcgi? 49 | defined?(::FCGI) and ::FCGI.fastcgi? 50 | end 51 | 52 | def mod_ruby? 53 | false # FIXME 54 | end 55 | 56 | class CGI < ::WEBrick::CGI 57 | def main(handler) 58 | @handler = handler 59 | start 60 | end 61 | 62 | def do_GET(wreq, wres) 63 | @handler.handle(wreq).update wres 64 | end 65 | 66 | alias do_POST do_GET 67 | end 68 | 69 | class FCGI < CGI 70 | def main(handler) 71 | @handler = handler 72 | ::FCGI.each_cgi_request do |req| 73 | start req.env, req.in, req.out 74 | end 75 | end 76 | end 77 | 78 | class WEBrickServlet < ::WEBrick::HTTPServlet::AbstractServlet 79 | def do_GET(wreq, wres) 80 | @options.first.handle(wreq).update wres 81 | end 82 | 83 | alias do_POST do_GET 84 | end 85 | 86 | end 87 | 88 | end 89 | -------------------------------------------------------------------------------- /test/test_bitclust.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust' 2 | require 'bitclust/subcommand' 3 | require 'stringio' 4 | require 'tmpdir' 5 | require 'fileutils' 6 | 7 | class TestBitClust < Test::Unit::TestCase 8 | def setup 9 | @tmpdir = Dir.mktmpdir 10 | src = "#{@tmpdir}/function/public_func" 11 | @srcdir = Dir.mkdir File.dirname(src) 12 | File.open(src, 'w') do |file| 13 | file.puts <<'HERE' 14 | filename=test.c 15 | macro=false 16 | private=false 17 | type=VALUE 18 | name=public_func 19 | params=() 20 | 21 | 22 | This is public function. 23 | HERE 24 | end 25 | 26 | @out = StringIO.new 27 | end 28 | 29 | def teardown 30 | FileUtils.rm_r(@tmpdir, :force => true) 31 | end 32 | 33 | def search_capi(command, *argv) 34 | db = BitClust::FunctionDatabase.new(@tmpdir) 35 | cmd = case command 36 | when "lookup" 37 | BitClust::Subcommands::LookupCommand.new 38 | when "list" 39 | BitClust::Subcommands::ListCommand.new 40 | else 41 | raise "must not happen! command=#{command}" 42 | end 43 | @out.string = "" 44 | $stdout = @out 45 | begin 46 | cmd.parse(argv) 47 | cmd.exec(argv, {:prefix => @tmpdir, :capi => true}) 48 | ensure 49 | $stdout = STDOUT 50 | end 51 | @out.string 52 | end 53 | 54 | def test_list 55 | assert_equal("public_func\n", search_capi("list", "--function")) 56 | end 57 | 58 | def test_lookup 59 | assert_equal(<<-EOS, search_capi("lookup", "--function=public_func").chomp) 60 | kind: function 61 | header: VALUE public_func() 62 | filename: test.c 63 | 64 | 65 | This is public function. 66 | EOS 67 | end 68 | 69 | def test_lookup_html 70 | assert_equal(<<-EOS, search_capi("lookup", "--function=public_func", "--html").chomp) 71 |

72 |
kind
function
73 |
header
VALUE public_func()
74 |
filename
test.c
75 |
76 |

77 | This is public function. 78 |

79 | EOS 80 | end 81 | end 82 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/library: -------------------------------------------------------------------------------- 1 | <% @title = "library #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_name(@entry.name) %> 6 |

7 | <% 8 | headline_init 9 | %> 10 | <%= headline(@entry.id == '_builtin' ? _('Builtin Library') : "library #{@entry.name}") %> 11 | 12 | <% 13 | headline_push 14 | all_classes = @entry.all_classes 15 | err_classes = @entry.all_error_classes.sort 16 | modules = @entry.all_modules 17 | objects = @entry.all_objects 18 | classes = all_classes - err_classes - modules - objects 19 | %> 20 | 21 | 22 | <%= headline(_("Abstract")) %> 23 | <%= compile_rd(@entry.source) %> 24 | <% 25 | [[classes, _('Classes')], 26 | [modules, _('Modules')], 27 | [objects, _('Objects')], 28 | [err_classes, _('Exception Classes')]].each do |cs, msg| 29 | unless cs.empty? 30 | %> 31 | <%= headline(msg) %> 32 | 33 | <% draw_tree(cs) do |c, indent| %> 34 | 35 | 39 | 40 | 41 | <% end %> 42 |
36 | <%= " " * indent %> 37 | <%= class_link(c.name, c.name) %> 38 | <%= compile_rd(c.synopsis_source) %>
43 | <% 44 | end 45 | end 46 | %> 47 | <% 48 | [[@entry.requires.sort, _('Required Libraries')], 49 | [(@entry.sublibraries - @entry.requires).sort, _('Sub-Libraries')]].each do |cs, msg| 50 | unless cs.empty? 51 | %> 52 | <%= headline(msg) %> 53 | 54 | <% cs.each do |c| %> 55 | 56 | 57 | 58 | 59 | <% end %> 60 |
<%= library_link(c.name) %><%= compile_rd(c.synopsis_source) %>
61 | <% 62 | end 63 | end 64 | %> 65 | <% 66 | ents = @entry.methods.sort 67 | unless ents.empty? %> 68 | <%= headline(_("Added/Redefined Methods")) %> 69 |

70 | <% ents.each do |m| %> 71 | <%= link_to_method(m, true) %> 72 | <% end %> 73 |

74 | <% end %> 75 | 76 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/library: -------------------------------------------------------------------------------- 1 | <% @title = "library #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_name(@entry.name) %> 6 |

7 | <% 8 | headline_init 9 | %> 10 | <%= headline(@entry.id == '_builtin' ? _('Builtin Library') : "library #{@entry.name}") %> 11 | 12 | <% 13 | headline_push 14 | all_classes = @entry.all_classes 15 | err_classes = @entry.all_error_classes.sort 16 | modules = @entry.all_modules 17 | objects = @entry.all_objects 18 | classes = all_classes - err_classes - modules - objects 19 | %> 20 | 21 | 22 | <%= headline(_("Abstract")) %> 23 | <%= compile_rd(@entry.source) %> 24 | <% 25 | [[classes, _('Classes')], 26 | [modules, _('Modules')], 27 | [objects, _('Objects')], 28 | [err_classes, _('Exception Classes')]].each do |cs, msg| 29 | unless cs.empty? 30 | %> 31 | <%= headline(msg) %> 32 | 33 | <% draw_tree(cs) do |c, indent| %> 34 | 35 | 39 | 40 | 41 | <% end %> 42 |
36 | <%= " " * indent %> 37 | <%= class_link(c.name, c.name) %> 38 | <%= compile_rd(c.synopsis_source) %>
43 | <% 44 | end 45 | end 46 | %> 47 | <% 48 | [[@entry.requires.sort, _('Required Libraries')], 49 | [(@entry.sublibraries - @entry.requires).sort, _('Sub-Libraries')]].each do |cs, msg| 50 | unless cs.empty? 51 | %> 52 | <%= headline(msg) %> 53 | 54 | <% cs.each do |c| %> 55 | 56 | 57 | 58 | 59 | <% end %> 60 |
<%= library_link(c.name) %><%= compile_rd(c.synopsis_source) %>
61 | <% 62 | end 63 | end 64 | %> 65 | <% 66 | ents = @entry.methods.sort 67 | unless ents.empty? %> 68 | <%= headline(_("Added/Redefined Methods")) %> 69 |

70 | <% ents.each do |m| %> 71 | <%= link_to_method(m, true) %> 72 | <% end %> 73 |

74 | <% end %> 75 | 76 | -------------------------------------------------------------------------------- /data/bitclust/template/library: -------------------------------------------------------------------------------- 1 | <% @title = "library #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_name(@entry.name) %> 6 |

7 | <%= search_form() %> 8 | 9 | <% 10 | headline_init 11 | %> 12 | <%= headline(@entry.id == '_builtin' ? _('Builtin Library') : "library #{@entry.name}") %> 13 | 14 | <% 15 | headline_push 16 | all_classes = @entry.all_classes 17 | err_classes = @entry.all_error_classes.sort 18 | modules = @entry.all_modules 19 | objects = @entry.all_objects 20 | classes = all_classes - err_classes - modules - objects 21 | %> 22 | 23 | 24 | <%= headline(_("Abstract")) %> 25 | <%= compile_rd(@entry.source) %> 26 | <% 27 | [[classes, _('Classes')], 28 | [modules, _('Modules')], 29 | [objects, _('Objects')], 30 | [err_classes, _('Exception Classes')]].each do |cs, msg| 31 | unless cs.empty? 32 | %> 33 | <%= headline(msg) %> 34 | 35 | <% draw_tree(cs) do |c, indent| %> 36 | 37 | 41 | 42 | 43 | <% end %> 44 |
38 | <%= " " * indent %> 39 | <%= class_link(c.name, c.name) %> 40 | <%= compile_rd(c.synopsis_source) %>
45 | <% 46 | end 47 | end 48 | %> 49 | <% 50 | [[@entry.requires.sort, _('Required Libraries')], 51 | [(@entry.sublibraries - @entry.requires).sort, _('Sub-Libraries')]].each do |cs, msg| 52 | unless cs.empty? 53 | %> 54 | <%= headline(msg) %> 55 | 56 | <% cs.each do |c| %> 57 | 58 | 59 | 60 | 61 | <% end %> 62 |
<%= library_link(c.name) %><%= compile_rd(c.synopsis_source) %>
63 | <% 64 | end 65 | end 66 | %> 67 | <% 68 | ents = @entry.methods.sort 69 | unless ents.empty? %> 70 | <%= headline(_("Added/Redefined Methods")) %> 71 |

72 | <% ents.each do |m| %> 73 | <%= link_to_method(m, true) %> 74 | <% end %> 75 |

76 | <% end %> 77 | 78 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/classes_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'optparse' 3 | 4 | require 'bitclust/crossrubyutils' 5 | 6 | module BitClust 7 | module Subcommands 8 | class ClassesCommand < Subcommand 9 | include CrossRubyUtils 10 | 11 | def initialize 12 | super 13 | @rejects = [] 14 | @verbose = false 15 | @parser.banner = "Usage: #{File.basename($0, '.*')} [-r] " 16 | @parser.on('-r', '--reject=LIB', 'Reject library LIB') {|lib| 17 | @rejects.concat lib.split(',') 18 | } 19 | @parser.on('-v', '--verbose', 'Show all ruby version.') { 20 | @verbose = true 21 | } 22 | end 23 | 24 | def parse(argv) 25 | super 26 | option_error('wrong number of arguments') unless argv.size == 1 27 | end 28 | 29 | def exec(argv, options) 30 | lib = argv[0] 31 | print_crossruby_table {|ruby| defined_classes(ruby, lib, @rejects) } 32 | end 33 | 34 | def defined_classes(ruby, lib, rejects) 35 | script = <<-SCRIPT 36 | def class_extent 37 | result = [] 38 | ObjectSpace.each_object(Module) do |c| 39 | result.push c 40 | end 41 | result 42 | end 43 | 44 | %w(#{rejects.join(" ")}).each do |lib| 45 | begin 46 | require lib 47 | rescue LoadError 48 | end 49 | end 50 | if "#{lib}" == "_builtin" 51 | class_extent().each do |c| 52 | puts c 53 | end 54 | else 55 | before = class_extent() 56 | begin 57 | require "#{lib}" 58 | rescue LoadError 59 | $stderr.puts "\#{RUBY_VERSION} (\#{RUBY_RELEASE_DATE}): library not exist: #{lib}" 60 | exit 61 | end 62 | after = class_extent() 63 | (after - before).each do |c| 64 | puts c 65 | end 66 | end 67 | SCRIPT 68 | output = `#{ruby} -e '#{script}'` 69 | output.split 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /test/test_refsdatabase.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust/database' 3 | require 'bitclust/refsdatabase' 4 | require 'bitclust/rrdparser' 5 | require 'stringio' 6 | 7 | class Test_RefsDatabase < Test::Unit::TestCase 8 | 9 | S1 = <#{escape_html(label)}) 58 | end 59 | 60 | ESC = { 61 | '&' => '&', 62 | '"' => '"', 63 | '<' => '<', 64 | '>' => '>' 65 | } 66 | 67 | def escape_html(str) 68 | table = ESC # optimize 69 | str.gsub(/[&"<>]/) {|s| table[s] } 70 | end 71 | 72 | ESCrev = ESC.invert 73 | 74 | def unescape_html(str) 75 | table = ESCrev # optimize 76 | str.gsub(/&\w+;/) {|s| table[s] } 77 | end 78 | 79 | end 80 | 81 | end 82 | -------------------------------------------------------------------------------- /test/test_preprocessor.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust/preprocessor' 3 | require 'stringio' 4 | 5 | class TestPreprocessor < Test::Unit::TestCase 6 | include BitClust 7 | def test_eval_cond 8 | params = { 'version' => '1.8.7' } 9 | 10 | [ 11 | ['#@if( version > "1.8.0")', true ], 12 | ['#@if( version < "1.8.0")', false], 13 | ['#@if( version <= "1.8.7")', true ], 14 | ['#@if( version >= "1.9.1")', false], 15 | ['#@if( version == "1.8.7")', true ], 16 | ['#@if( version != "1.9.0")', true ], 17 | ['#@if( "1.9.0" != version)', true ], 18 | 19 | ['#@since 1.8.0', true ], 20 | ['#@since 1.8.7', true ], 21 | ['#@until 1.8.7', false], 22 | ['#@until 1.9.0', true ], 23 | 24 | ['#@if( version > "1.8.0" and version < "1.9.0")', true ], 25 | ['#@if( version > "1.8.9" and version < "1.9.0")', false], 26 | ['#@if( version > "1.8.9" or version < "1.9.0")', true ], 27 | ['#@if( version < "1.8.0" or version > "1.9.0")', false], 28 | ['#@if( version > "1.8.0" and version < "1.9.0" and version < "1.9.1")', true ], 29 | ['#@if( version > "1.8.0" and version < "1.9.0" and version > "1.9.1")', false], 30 | ['#@if( version < "1.8.0" and version > "1.9.0" or "1.9.1" != version)', true ], 31 | ].each{|cond, expected_result| 32 | s = < '1.8.7' } 50 | src = < String 52 | \#@todo 53 | description 54 | HERE 55 | expected = < String 57 | @todo 58 | description 59 | HERE 60 | ret = Preprocessor.wrap(StringIO.new(src), params).to_a 61 | assert_equal(expected, ret.join) 62 | end 63 | 64 | def test_todo_with_condition 65 | params = { 'version' => '1.9.2' } 66 | src = < String 68 | \#@since 1.9.2 69 | \#@todo 1.9.2 70 | \#@else 71 | \#@todo old 72 | \#@end 73 | description 74 | HERE 75 | expected = < String 77 | @todo 1.9.2 78 | description 79 | HERE 80 | ret = Preprocessor.wrap(StringIO.new(src), params).to_a 81 | assert_equal(expected, ret.join) 82 | end 83 | end 84 | 85 | -------------------------------------------------------------------------------- /tools/statrefm.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | class Error < StandardError; end 4 | 5 | def main 6 | entries = parse(find_basedir(ARGV[0])) 7 | puts '--- Status' 8 | done = entries.select {|ent| ent.done? }.size 9 | puts "#{done}/#{entries.size} files done (#{percent_str(done, entries.size)})" 10 | puts 11 | puts '--- Ranking by number of files' 12 | ranking_table(entries) {|ent| 1 }.each_with_index do |(owner, n), idx| 13 | printf "%2d %4d %-s\n", idx + 1, n, (owner || '(not yet)') 14 | end 15 | puts 16 | puts '--- Ranking by Kbytes' 17 | ranking_table(entries) {|ent| ent.size }.each_with_index do |(owner,n), idx| 18 | printf "%2d %4d %-s\n", idx + 1, kb(n), (owner || '(not yet)') 19 | end 20 | rescue Error => err 21 | $stderr.puts err.message 22 | exit 1 23 | end 24 | 25 | def find_basedir(dir) 26 | [ dir, "#{dir}/api", "#{dir}/refm/api", "#{dir}/.." ].each do |basedir| 27 | if File.file?("#{basedir}/ASSIGN") 28 | return basedir 29 | end 30 | end 31 | raise Error, "error: wrong directory: #{dir}" 32 | end 33 | 34 | def percent_str(n, base) 35 | sprintf('%0.2f%%', percent(n, base)) 36 | end 37 | 38 | def percent(n, base) 39 | n * 100 / base.to_f 40 | end 41 | 42 | def kb(bytes) 43 | if bytes % 1024 == 0 44 | bytes / 1024 45 | else 46 | (bytes / 1024) + 1 47 | end 48 | end 49 | 50 | def ranking_table(entries) 51 | h = Hash.new(0) 52 | entries.each do |ent| 53 | h[ent.done? ? ent.owner : nil] += yield(ent) 54 | end 55 | h.to_a.sort_by {|owner, n| -n } 56 | end 57 | 58 | def parse(basedir) 59 | File.open("#{basedir}/ASSIGN") {|f| 60 | f.map {|line| 61 | next if line.strip.empty? 62 | next if /\A\#/ =~ line 63 | Entry.new(basedir, *line.split) 64 | }.compact 65 | } 66 | end 67 | 68 | class Entry 69 | def initialize(basedir, name, owner = nil, status = nil) 70 | @basedir = basedir 71 | @name = name 72 | @owner = owner 73 | @status = status 74 | @path = nil 75 | end 76 | 77 | attr_reader :name 78 | attr_reader :owner 79 | attr_reader :status 80 | 81 | def done? 82 | @status == 'done' 83 | end 84 | 85 | def size 86 | File.size(path()) 87 | rescue Error 88 | 0 89 | end 90 | 91 | def path 92 | @path ||= ["#{@basedir}/src/#{@name}.rd", 93 | "#{@basedir}/src/#{@name}"].detect {|path| File.file?(path) } or 94 | raise Error, "file not found: library #{@name}" 95 | end 96 | end 97 | 98 | main 99 | -------------------------------------------------------------------------------- /lib/bitclust/functionreferenceparser.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/functionreferenceparser.rb 3 | # 4 | # Copyright (c) 2006-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/exception' 11 | 12 | module BitClust 13 | 14 | # Parser for C API reference file (refm/capi/src/*) 15 | # Much simpler than Ruby API reference parser(RRDParser) 16 | # because C APIs does not have library, class, etc. 17 | class FunctionReferenceParser 18 | 19 | # Returns an array of FunctionEntry 20 | def FunctionReferenceParser.parse_file(path, params = {"version" => "1.9.0"}) 21 | parser = new(FunctionDatabase.dummy(params)) 22 | parser.parse_file(path, File.basename(path, ".rd"), params) 23 | end 24 | 25 | def initialize(db) 26 | @db = db 27 | end 28 | 29 | def parse_file(path, filename, properties) 30 | fopen(path, 'r:UTF-8') {|f| 31 | return parse(f, filename, properties) 32 | } 33 | end 34 | 35 | def parse(f, filename, params = {}) 36 | @filename = filename 37 | s = Preprocessor.read(f, params) 38 | file_entries LineInput.for_string(s) 39 | @db.functions 40 | end 41 | 42 | private 43 | 44 | def file_entries(f) 45 | f.skip_blank_lines 46 | f.while_match(/\A---/) do |header| 47 | entry header.sub(/\A---/, '').strip, f.break(/\A---/) 48 | f.skip_blank_lines 49 | end 50 | end 51 | 52 | def entry(header, body) 53 | h = parse_header(header) 54 | @db.open_function(h.name) {|f| 55 | f.filename = @filename 56 | f.macro = h.macro 57 | f.private = h.private 58 | f.type = h.type 59 | f.name = h.name 60 | f.params = h.params 61 | f.source = body.join('') 62 | } 63 | end 64 | 65 | def parse_header(header) 66 | h = FunctionHeader.new 67 | m = header.match(/\A\s*(MACRO\s+)?(static\s+)?(.+?\W)(\w+)(\(.*\))?\s*\z/) 68 | raise ParseError, "syntax error: #{header.inspect}" unless m 69 | h.macro = m[1] ? true : false 70 | h.private = m[2] ? true : false 71 | h.type = m[3].strip 72 | h.name = m[4] 73 | h.params = m[5].strip if m[5] 74 | h 75 | end 76 | 77 | end 78 | 79 | # Represents C function signature 80 | # (Used internally in this file) 81 | FunctionHeader = Struct.new(:macro, :private, :type, :name, :params) 82 | 83 | end 84 | -------------------------------------------------------------------------------- /lib/bitclust/messagecatalog.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/messagecatalog.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | module BitClust 11 | 12 | # Provides methods to use MessageCatalog 13 | module Translatable 14 | 15 | private 16 | 17 | def init_message_catalog(catalog) 18 | @__message_catalog = catalog 19 | end 20 | 21 | def message_catalog 22 | @__message_catalog 23 | end 24 | 25 | def _(key, *args) 26 | @__message_catalog.translate(key, *args) 27 | end 28 | 29 | end 30 | 31 | # Tiny implementation of message catalog (I18N) 32 | # see data/bitclust/catalog/ja_JP.UTF-8 33 | # 34 | # FIXME: support automatic encoding-conversion 35 | class MessageCatalog 36 | 37 | ENCODING_MAP = { 38 | 'utf-8' => 'UTF-8', 39 | 'euc-jp' => 'EUC-JP', 40 | 'shift_jis' => 'Shift_JIS' 41 | } 42 | 43 | # FIXME: support non ja_JP locales 44 | def MessageCatalog.encoding2locale(enc) 45 | newenc = ENCODING_MAP[enc.downcase] 46 | newenc ? "ja_JP.#{newenc}" : "C" 47 | end 48 | 49 | def MessageCatalog.load(prefix) 50 | load_with_locales(prefix, env_locales()) 51 | end 52 | 53 | def MessageCatalog.load_with_locales(prefix, locales) 54 | path, loc = find_catalog(prefix, locales) 55 | path ? load_file(path, loc) : new({}, 'C') 56 | end 57 | 58 | def MessageCatalog.env_locales 59 | [ENV['LC_MESSAGES'], ENV['LC_ALL'], ENV['LANG'], 'C']\ 60 | .compact.uniq.reject {|loc| loc.empty? } 61 | end 62 | private_class_method :env_locales 63 | 64 | def MessageCatalog.find_catalog(prefix, locales) 65 | locales.each do |locale| 66 | path = "#{prefix}/#{locale}" 67 | return path, locale if File.file?(path) 68 | end 69 | nil 70 | end 71 | private_class_method :find_catalog 72 | 73 | def MessageCatalog.load_file(path, locale) 74 | h = {} 75 | fopen(path, 'r:UTF-8') {|f| 76 | f.each do |key| 77 | h[key.chomp] = f.gets.chomp 78 | end 79 | } 80 | new(h, locale) 81 | end 82 | 83 | def initialize(msgs, locale) 84 | @msgs = msgs 85 | @locale = locale 86 | end 87 | 88 | def inspect 89 | "\#<#{self.class} #{@locale}>" 90 | end 91 | 92 | def translate(key, *args) 93 | str = @msgs[key] || key 94 | sprintf(str, *args) 95 | end 96 | 97 | end 98 | 99 | end 100 | -------------------------------------------------------------------------------- /lib/bitclust/simplesearcher.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust/nameutils' 2 | require 'bitclust/methodid' 3 | 4 | module BitClust 5 | # Tiny search engine for BitClust server. 6 | # Note: loads all the entries onto memory, so first search will be slow. 7 | module SimpleSearcher 8 | include NameUtils 9 | 10 | module_function 11 | 12 | def search_pattern(db, pat) 13 | pat = to_pattern(pat) 14 | return [] if pat.empty? or /\A\s+\z/ =~ pat 15 | cname, type, mname = parse_method_spec_pattern(pat) 16 | ret = cs = ms = [] 17 | if cname and not cname.empty? 18 | if mname 19 | ms = find_class_method(db, cname, type, mname) 20 | cs += find_class(db, cname + '::' + mname) if /\A[A-Z]/ =~ mname 21 | else 22 | cs = find_class(db, cname) 23 | end 24 | elsif type == '$' 25 | ms = find_special_vars(db, mname) 26 | else 27 | ms = find_methods(db, mname) 28 | end 29 | ms = ms.sort_by{|e| [e.library.name, e.klass.name] } 30 | cs = cs.sort_by{|e| [e.library.name] } 31 | cs + ms 32 | end 33 | 34 | def find_class(db, cname) 35 | db.classes.find_all{|c| /\b#{Regexp.escape(cname)}\w*\z/ =~ c.name } 36 | end 37 | 38 | def find_class_method(db, cname, type, mname) 39 | ret = [] 40 | db.classes.each{|c| 41 | if /\b#{Regexp.escape(cname)}/ =~ c.name 42 | ret += c.methods.find_all{|m| 43 | m.names.any?{|n| /\A#{Regexp.escape(mname)}/ =~ n } 44 | } 45 | end 46 | } 47 | ret 48 | end 49 | 50 | def find_methods(db, mname) 51 | db.methods.find_all{|m| 52 | m.names.any?{|n| /\A#{Regexp.escape(mname)}/ =~ n } 53 | } 54 | end 55 | 56 | def find_special_vars(db, mname) 57 | db.get_class('Kernel').special_variables.find_all{|m| 58 | m.names.any?{|n| /\A#{Regexp.escape(mname)}/ =~ n } 59 | } 60 | end 61 | 62 | def to_pattern(pat) 63 | pat = pat.to_str 64 | pat = pat[/\A\s*(.*?)\s*\z/, 1] 65 | end 66 | 67 | def parse_method_spec_pattern(pat) 68 | if /\s/ =~ pat 69 | return parse_method_spec_pattern0(pat) 70 | end 71 | return pat, nil, nil if /\A[A-Z]\w*\z/ =~ pat 72 | return nil, '$', $1 if /\$(\S*)/ =~ pat 73 | _m, _t, _c = pat.reverse.split(/(::|[\#,]\.|\.[\#,]|[\#\.\,])/, 2) 74 | c = _c.reverse if _c 75 | t = _t.tr(',', '#').sub(/\#\./, '.#') if _t 76 | m = _m.reverse 77 | return c, t, m 78 | end 79 | 80 | def parse_method_spec_pattern0(q) 81 | q = q.scan(/\S+/)[0..1] 82 | q = q.reverse unless /\A[A-Z]/ =~ q[0] 83 | return q[0], nil, q[1] 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/bitclust/crossrubyutils.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/crossrubyutils.rb 3 | # 4 | # Copyright (c) 2006 Minero Aoki 5 | # 6 | 7 | module BitClust 8 | 9 | # Provides utility methods like print_crossruby_table. 10 | # Used by subcommands "ancestors" "classes" etc. 11 | module CrossRubyUtils 12 | 13 | private 14 | 15 | def print_crossruby_table(&block) 16 | print_table(*build_crossruby_table(&block)) 17 | end 18 | 19 | def print_table(vers, table) 20 | thcols = ([20] + table.keys.map {|s| s.size }).max 21 | versions = vers.map {|ver| version_id(ver) } 22 | width_list = versions.map{|v| v.size + 1 } 23 | print_record thcols, '', versions, width_list 24 | crossrubyutils_sort_entries(table.keys).each do |c| 25 | print_record thcols, c, vers.map {|ver| table[c][ver] ? 'o' : '-' }, width_list 26 | end 27 | end 28 | 29 | def crossrubyutils_sort_entries(ents) 30 | ents.sort 31 | end 32 | 33 | def print_record(thcols, th, tds, width_list = nil) 34 | unless width_list 35 | width_list = Array.new(tds.size){ 4 } 36 | end 37 | printf "%-#{thcols}s ", th 38 | puts tds.zip(width_list).map {|td, width| "%#{width}s" % td }.join('') 39 | end 40 | 41 | def version_id(ver) 42 | ver.split[1].tr('.', '') 43 | end 44 | 45 | def get_ruby(version) 46 | forall_ruby(ENV['PATH']) do |ruby, | 47 | v = `#{ruby} -e 'print RUBY_VERSION'` 48 | patch = `#{ruby} -e 'print RUBY_PATCHLEVEL if defined? RUBY_PATCHLEVEL'` 49 | if version == v or ( version == v.succ and patch == '5000') 50 | return ruby 51 | end 52 | end 53 | return nil 54 | end 55 | 56 | def build_crossruby_table 57 | ENV.delete 'RUBYOPT' 58 | ENV.delete 'RUBYLIB' 59 | ENV.delete 'GEM_HOME' 60 | vers = [] 61 | table = {} 62 | forall_ruby(ENV['PATH']) do |ruby, ver| 63 | puts "#{version_id(ver)}: #{ver}" if @verbose 64 | vers.push ver 65 | yield(ruby).each do |c| 66 | (table[c] ||= {})[ver] = true 67 | end 68 | end 69 | return vers, table 70 | end 71 | 72 | def forall_ruby(path, &block) 73 | rubys(path)\ 74 | .map {|ruby| [ruby, `#{ruby} --version`] }\ 75 | .reject {|ruby, verstr| `which #{ruby}`.include?('@') }\ 76 | .sort_by {|ruby, verstr| verstr }\ 77 | .each(&block) 78 | end 79 | 80 | def rubys(path) 81 | parse_PATH(path).map {|bindir| 82 | Dir.glob("#{bindir}/ruby-[12t]*").map {|path| File.basename(path) } 83 | }\ 84 | .flatten.uniq + ['ruby'] 85 | end 86 | 87 | def parse_PATH(str) 88 | str.split(':').map {|path| path.empty? ? '.' : path } 89 | end 90 | 91 | end 92 | 93 | end 94 | -------------------------------------------------------------------------------- /lib/bitclust/methodsignature.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/methodsignature.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/nameutils' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | # Represents detailed signature of a method in a class/module. 16 | # Includes parameters, block specification and return type. 17 | class MethodSignature 18 | 19 | include NameUtils 20 | 21 | METHOD_SIGNATURE_RE = /\A 22 | --- \s* 23 | (?: (?:#{CLASS_PATH_RE} #{TYPEMARK_RE})? (#{METHOD_NAME_RE}) 24 | | (#{GVAR_RE}) 25 | ) \s* # method name ($1) or gvar name ($2) 26 | (?: \( (.*?) \) \s* )? # parameters (optional); $3=parameter_list 27 | (?: (\{ .* \}) \s* )? # block (optional); $4=block 28 | (?: -> \s* (\S.*) \s* )? # type declaration (optional); $5=return_type 29 | \z/x 30 | 31 | def MethodSignature.parse(line) 32 | m = METHOD_SIGNATURE_RE.match(line) or 33 | raise ParseError, %Q(unknown signature format: "#{line.strip}") 34 | method, gvar, params, block, type = m.captures 35 | new(method || gvar, params && params.strip, block && block.strip, type && type.strip) 36 | end 37 | 38 | def initialize(name, params, block, type) 39 | @name = name 40 | @params = params 41 | @block = block 42 | @type = type 43 | end 44 | 45 | attr_reader :name 46 | attr_reader :params 47 | attr_reader :block 48 | attr_reader :type 49 | 50 | def to_s 51 | @name + 52 | (@params ? "(#{@params})" : "") + 53 | (@block ? " #{@block}" : "") + 54 | (@type ? " -> #{@type}" : "") 55 | end 56 | 57 | def friendly_string 58 | case @name 59 | when /\A\$/ # gvar 60 | @name + (@type ? " -> #{@type}" : "") 61 | when "+@", "-@", "~", "!", "!@" # unary operator 62 | "#{@name.sub(/@/, '')} self" + (@type ? " -> #{@type}" : "") 63 | when "[]" # aref 64 | "self[#{@params}]" + (@type ? " -> #{@type}" : "") 65 | when "[]=" # aset 66 | params = @params.split(',') 67 | val = params.pop 68 | "self[#{params.join(',').strip}] = #{val.strip}" 69 | when "`" # `command` 70 | "`#{@params}`" + (@type ? " -> #{@type}" : "") 71 | when /\A\W/ # binary operator 72 | "self #{@name} #{@params}" + (@type ? " -> #{@type}" : "") 73 | else 74 | to_s() 75 | end 76 | end 77 | 78 | def inspect 79 | "\#<#{self.class} name=#{@name.inspect} params=#{@params.inspect} block=#{@block.inspect} type=#{@type.inspect}>" 80 | end 81 | 82 | end 83 | 84 | end 85 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/epub_command.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'date' 3 | 4 | require 'bitclust' 5 | require 'bitclust/subcommand' 6 | require 'bitclust/generators/epub' 7 | 8 | module BitClust 9 | module Subcommands 10 | class EPUBCommand < Subcommand 11 | def initialize 12 | super 13 | @verbose = true 14 | @catalogdir = nil 15 | @templatedir = srcdir_root + "data/bitclust/template.epub" 16 | @themedir = srcdir_root + "theme/default" 17 | @filename = "rurema-#{Date.today}.epub" 18 | @keep = false 19 | @parser.banner = "Usage: #{File.basename($0, '.*')} epub [options]" 20 | @parser.on('-o', '--outputdir=PATH', 'Output directory') do |path| 21 | begin 22 | @outputdir = Pathname.new(path).realpath 23 | rescue Errno::ENOENT 24 | FileUtils.mkdir_p(path, :verbose => @verbose) 25 | retry 26 | end 27 | end 28 | @parser.on('-f', '--filename=FILENAME', 29 | "Filename of generated EPUB file [#{@filename}]") do |filename| 30 | @filename = filename 31 | end 32 | @parser.on('--[no-]keep', 'Keep all generated files (for debug) [false]') do |keep| 33 | @keep = keep 34 | end 35 | @parser.on('--catalog=PATH', 'Catalog directory') do |path| 36 | @catalogdir = Pathname.new(path).realpath 37 | end 38 | @parser.on('--templatedir=PATH', 'Template directory') do |path| 39 | @templatedir = Pathname.new(path).realpath 40 | end 41 | @parser.on('--themedir=PATH', 'Theme directory') do |path| 42 | @themedir = Pathname.new(path).realpath 43 | end 44 | @parser.on('--fs-casesensitive', 'Filesystem is case-sensitive') do 45 | @fs_casesensitive = true 46 | end 47 | @parser.on('--[no-]quiet', 'Be quiet') do |quiet| 48 | @verbose = !quiet 49 | end 50 | end 51 | 52 | def exec(argv, options) 53 | generator = BitClust::Generators::EPUB.new(:prefix => options[:prefix], 54 | :capi => options[:capi], 55 | :outputdir => @outputdir, 56 | :catalog => @catalog, 57 | :templatedir => @templatedir, 58 | :themedir => @themedir, 59 | :fs_casesensitive => @fs_casesensitive, 60 | :verbose => @verbose, 61 | :keep => @keep, 62 | :filename => @filename) 63 | generator.generate 64 | end 65 | end 66 | end 67 | 68 | end 69 | -------------------------------------------------------------------------------- /lib/bitclust/functiondatabase.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/functiondatabase.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/database' 11 | require 'bitclust/functionentry' 12 | require 'bitclust/completion' 13 | require 'bitclust/functionreferenceparser' 14 | require 'bitclust/exception' 15 | 16 | module BitClust 17 | 18 | # Database for C API functions. 19 | class FunctionDatabase < Database 20 | 21 | include Completion 22 | 23 | def initialize(prefix) 24 | super 25 | @dirty_functions = {} 26 | @functionmap = {} 27 | @function_extent_loaded = false 28 | end 29 | 30 | def commit 31 | each_dirty_function do |f| 32 | f.save 33 | end 34 | clear_dirty 35 | #save_completion_index 36 | end 37 | private :commit 38 | 39 | def dirty_function(f) 40 | @dirty_functions[f] = true 41 | end 42 | 43 | def dirty? 44 | not @dirty_functions.empty? 45 | end 46 | 47 | def each_dirty_function(&block) 48 | @dirty_functions.each_key(&block) 49 | end 50 | 51 | def clear_dirty 52 | @dirty_functions.clear 53 | end 54 | 55 | def update_by_file(path, filename) 56 | check_transaction 57 | FunctionReferenceParser.new(self).parse_file(path, filename, properties()) 58 | end 59 | 60 | def search_functions(pattern) 61 | fs = _search_functions(pattern) 62 | if fs.empty? 63 | raise FunctionNotFound, "no such function: #{pattern}" 64 | end 65 | fs 66 | end 67 | 68 | def open_function(id) 69 | check_transaction 70 | if exist?("function/#{id}") 71 | f = load_function(id) 72 | f.clear 73 | else 74 | f = (@functionmap[id] ||= FunctionEntry.new(self, id)) 75 | end 76 | yield f 77 | dirty_function f 78 | f 79 | end 80 | 81 | def fetch_function(id) 82 | load_function(id) or 83 | raise FunctionNotFound, "function not found: #{id.inspect}" 84 | end 85 | 86 | def load_function(id) 87 | @functionmap[id] ||= 88 | begin 89 | return nil unless exist?("function/#{id}") 90 | FunctionEntry.new(self, id) 91 | end 92 | end 93 | private :load_function 94 | 95 | def functions 96 | functionmap().values 97 | end 98 | 99 | def functionmap 100 | return @functionmap if @function_extent_loaded 101 | id_extent(FunctionEntry).each do |id| 102 | @functionmap[id] ||= FunctionEntry.new(self, id) 103 | end 104 | @function_extent_loaded = true 105 | @functionmap 106 | end 107 | private :functionmap 108 | 109 | def id_extent(entry_class) 110 | entries(entry_class.type_id.to_s) 111 | end 112 | private :id_extent 113 | 114 | end 115 | 116 | end 117 | -------------------------------------------------------------------------------- /data/bitclust/template.epub/class: -------------------------------------------------------------------------------- 1 | <% @title = "#{@entry.type} #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_link(@entry.library.name) %> 6 | > <%=h _(@entry.type.to_s + ' %s', @entry.name) %> 7 |

8 | 9 | <% 10 | headline_init 11 | %> 12 | <%= headline("#{@entry.type} #{@entry.name}" + @entry.ancestors[1..@alevel].map{|c| " + #{c.name}" }.join) %> 13 |

14 | <% 15 | myself, *supers = @entry.ancestors 16 | n = 0 17 | %> 18 | <% unless @entry.alias? %> 19 | <%= _('ancestors') %>: <%= escape_html(myself.name) %> 20 | <% supers.each do |c| %> 21 | <%= @conf[:tochm_mode] ? "<" : a_href("?a=#{n}", "<") %> <%= class_link(c.name) %> 22 | <% n += 1 %> 23 | <% end %> 24 | <% end %> 25 | 26 | <% unless @entry.extended.empty? %> 27 |
extend: <%= @entry.extended.map {|c| class_link(c.name) }.join(', ') %> 28 | <% end %> 29 | <% unless @entry.aliases.empty? %> 30 |
aliases: <%=h @entry.aliases.map{|c| c.name}.join(', ') %> 31 | <% end %> 32 | <% unless @entry.dynamically_included.empty? %> 33 |
dynamic include: 34 | <%= @entry.dynamically_included.map{|m| 35 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 36 | }.join(", ") 37 | %> 38 | <% end %> 39 | <% unless @entry.dynamically_extended.empty? %> 40 |
dynamic extend: 41 | <%= @entry.dynamically_extended.map{|m| 42 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 43 | }.join(", ") 44 | %> 45 | <% end %> 46 |

47 | <% 48 | headline_push 49 | %> 50 | <%= headline(_("Abstract")) %> 51 | <%= compile_rd(@entry.source) %> 52 | 53 | <% 54 | ents = @entry.partitioned_entries(@alevel) 55 | items = 56 | [[_('Singleton Methods'), ents.singleton_methods ], 57 | [_('Instance Methods'), ents.instance_methods ], 58 | [_('Private Singleton Methods'), ents.private_singleton_methods ], 59 | [_('Private Instance Methods'), ents.private_instance_methods ], 60 | [_('Module Functions'), ents.module_functions ], 61 | [_('Constants'), ents.constants ], 62 | [_('Special Variables'), ents.special_variables ,'$']] %> 63 | <%= headline(_("Index")) %> 64 |
65 | <% items.each do |label, entries, prefix| next if entries.empty? %> 66 |
<%= label %>
67 |
68 | <% 69 | entries.each do |m| 70 | m.names.each do |mname| 71 | %> 72 | <%= "#{prefix}#{mname}" %> 73 | <% 74 | end 75 | end 76 | %> 77 |
78 | <% end %> 79 |
80 | 81 | <% 82 | items.each do |label, entries| 83 | unless entries.empty? %> 84 | <%= headline(label) %> 85 |
86 | <% 87 | headline_push 88 | entries.each do |m| 89 | %> 90 | <%= compile_method(m) %> 91 | <% 92 | end 93 | headline_pop 94 | %> 95 |
96 | <% 97 | end 98 | end 99 | headline_pop 100 | %> 101 | 102 | -------------------------------------------------------------------------------- /data/bitclust/template.offline/class: -------------------------------------------------------------------------------- 1 | <% @title = "#{@entry.type} #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_link(@entry.library.name) %> 6 | > <%=h _(@entry.type.to_s + ' %s', @entry.name) %> 7 |

8 | 9 | <% 10 | headline_init 11 | %> 12 | <%= headline("#{@entry.type} #{@entry.name}" + @entry.ancestors[1..@alevel].map{|c| " + #{c.name}" }.join) %> 13 |

14 | <% 15 | myself, *supers = @entry.ancestors 16 | n = 0 17 | %> 18 | <% unless @entry.alias? %> 19 | <%= _('ancestors') %>: <%= escape_html(myself.name) %> 20 | <% supers.each do |c| %> 21 | <%= @conf[:tochm_mode] ? "<" : a_href("?a=#{n}", "<") %> <%= class_link(c.name) %> 22 | <% n += 1 %> 23 | <% end %> 24 | <% end %> 25 | 26 | <% unless @entry.extended.empty? %> 27 |
extend: <%= @entry.extended.map {|c| class_link(c.name) }.join(', ') %> 28 | <% end %> 29 | <% unless @entry.aliases.empty? %> 30 |
aliases: <%=h @entry.aliases.map{|c| c.name}.join(', ') %> 31 | <% end %> 32 | <% unless @entry.dynamically_included.empty? %> 33 |
dynamic include: 34 | <%= @entry.dynamically_included.map{|m| 35 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 36 | }.join(", ") 37 | %> 38 | <% end %> 39 | <% unless @entry.dynamically_extended.empty? %> 40 |
dynamic extend: 41 | <%= @entry.dynamically_extended.map{|m| 42 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 43 | }.join(", ") 44 | %> 45 | <% end %> 46 |

47 | <% 48 | headline_push 49 | %> 50 | <%= headline(_("Abstract")) %> 51 | <%= compile_rd(@entry.source) %> 52 | 53 | <% 54 | ents = @entry.partitioned_entries(@alevel) 55 | items = 56 | [[_('Singleton Methods'), ents.singleton_methods ], 57 | [_('Instance Methods'), ents.instance_methods ], 58 | [_('Private Singleton Methods'), ents.private_singleton_methods ], 59 | [_('Private Instance Methods'), ents.private_instance_methods ], 60 | [_('Module Functions'), ents.module_functions ], 61 | [_('Constants'), ents.constants ], 62 | [_('Special Variables'), ents.special_variables ,'$']] %> 63 | <%= headline(_("Index")) %> 64 |
65 | <% items.each do |label, entries, prefix| next if entries.empty? %> 66 |
<%= label %>
67 |
68 | <% 69 | entries.each do |m| 70 | m.names.each do |mname| 71 | %> 72 | <%= "#{prefix}#{mname}" %> 73 | <% 74 | end 75 | end 76 | %> 77 |
78 | <% end %> 79 |
80 | 81 | <% 82 | items.each do |label, entries| 83 | unless entries.empty? %> 84 | <%= headline(label) %> 85 |
86 | <% 87 | headline_push 88 | entries.each do |m| 89 | %> 90 | <%= compile_method(m) %> 91 | <% 92 | end 93 | headline_pop 94 | %> 95 |
96 | <% 97 | end 98 | end 99 | headline_pop 100 | %> 101 | 102 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/library: -------------------------------------------------------------------------------- 1 | <% @title = "library #{@entry.name}" %> 2 |
3 | 4 |

5 | <%= manual_home_link() %> 6 | > <%= _('All Libraries') %> 7 | > library <%=h @entry.name %> 8 |

9 | <% 10 | headline_init 11 | %> 12 | <%= headline(@entry.id == '_builtin' ? '組み込みライブラリ' : "library #{@entry.name}") %> 13 | 14 | <% 15 | headline_push 16 | all_classes = @entry.all_classes 17 | err_classes = @entry.all_error_classes.sort 18 | modules = @entry.all_modules 19 | objects = @entry.all_objects 20 | classes = all_classes - err_classes - modules - objects 21 | %> 22 | 23 | 24 | <%= headline(_("Abstract")) %> 25 | <%= compile_rd(@entry.source) %> 26 | <% 27 | [[classes, _('Classes')], 28 | [modules, _('Modules')], 29 | [objects, _('Objects')], 30 | [err_classes, _('Exception Classes')]].each do |cs, msg| 31 | unless cs.empty? 32 | %> 33 | <%= headline(msg) %> 34 | 35 | <% draw_tree(cs) do |c, indent| %> 36 | 37 | 41 | 42 | 43 | <% end %> 44 |
38 | <%= " " * indent %> 39 | <%= class_link(c.name, c.name) %> 40 | <%= compile_rd(c.synopsis_source) %>
45 | <% 46 | end 47 | end 48 | %> 49 | <% 50 | [[@entry.requires.sort, _('Required Libraries')], 51 | [(@entry.sublibraries - @entry.requires).sort, _('Sub-Libraries')]].each do |cs, msg| 52 | unless cs.empty? 53 | %> 54 | <%= headline(msg) %> 55 | 56 | <% cs.each do |c| %> 57 | 58 | 59 | 60 | <% end %> 61 |
<%= library_link(c.name) %><%= compile_rd(c.synopsis_source) %>
62 | <% 63 | end 64 | end 65 | %> 66 | <% 67 | ents = @entry.methods.sort 68 | unless ents.empty? 69 | %> 70 | <%= headline(_("Added/Redefined Methods")) %> 71 |
72 | <% headline_push 73 | ents.each do |m| 74 | %> 75 | 76 | <%= compile_method(m, true) %> 77 | <% 78 | end 79 | headline_pop 80 | %> 81 |
82 | <% 83 | end 84 | %> 85 | 86 |
87 | 88 | 89 |
90 | 91 |
92 |

Classes

93 |
94 | 95 |
96 |
    97 | <% 98 | ((@entry.all_classes - @entry.all_error_classes).sort + @entry.all_error_classes.sort).each do |c| 99 | %> 100 |
  • <%= class_link(c.name, "#{c.name}") %>
  • 101 | <% end %> 102 |
103 |
104 | 105 |
106 |

Sublibraries

107 |
108 | 109 |
110 |
    111 | <% 112 | (@entry.sublibraries.sort + (@entry.requires - @entry.sublibraries).sort).each do |lib| 113 | %> 114 |
  • <%= library_link(lib.name) %>
  • 115 | <% end %> 116 |
117 |
118 | 119 |
120 | -------------------------------------------------------------------------------- /tools/stattodo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pathname' 4 | 5 | bindir = Pathname.new(__FILE__).realpath.dirname 6 | $LOAD_PATH.unshift((bindir + '../lib').realpath) 7 | 8 | require 'bitclust/preprocessor' 9 | require 'find' 10 | 11 | #ALL = 14349 12 | 13 | def main 14 | cmd, hist, prefix = ARGV[0], ARGV[1], ARGV[2] 15 | case cmd 16 | when 'count' 17 | count hist, prefix 18 | when 'update' 19 | update hist, prefix 20 | else 21 | $stderr.puts "unknown command: #{cmd}" 22 | exit 1 23 | end 24 | end 25 | 26 | def count(hist, prefix) 27 | total = 0 28 | curr = 0 29 | Table.parse_file(hist).each do |ent| 30 | n = count_todo_in_file(File.join(prefix, ent.name)) 31 | curr += n 32 | total += ent.total 33 | report_count ent.name, n, ent.total 34 | end 35 | report_count 'TOTAL', curr, total 36 | end 37 | 38 | def report_count(label, curr, all) 39 | done = all - curr 40 | printf "%-24s %5d/%5d (%6.2f%%)\n", label, done, all, percent(done, all) 41 | end 42 | 43 | def percent(done, all) 44 | return 0 if all == 0 45 | done * 100 / all.to_f 46 | end 47 | 48 | def update(hist, prefix) 49 | table = Table.parse_file(hist) 50 | table.each do |ent| 51 | ent.push count_todo(File.join(prefix, ent.name)) 52 | end 53 | File.open("#{hist}.tmp", 'w') {|f| 54 | f.puts table.header 55 | table.each do |ent| 56 | f.puts ent.serialize 57 | end 58 | } 59 | File.rename "#{hist}.tmp", hist 60 | end 61 | 62 | def count_todo(path) 63 | if File.directory?(path) 64 | count_todo_in_dir(path) 65 | else 66 | count_todo_in_file(path) 67 | end 68 | end 69 | 70 | def count_todo_in_dir(dir) 71 | n = 0 72 | Dir.entries(dir).each do |ent| 73 | next if /\A\./ =~ ent 74 | path = File.join(dir, ent) 75 | if File.extname(ent) == '.rd' and File.file?(path) 76 | n += count_todo_in_file(path) 77 | elsif File.directory?(path) 78 | n += count_todo_in_dir(path) 79 | end 80 | end 81 | n 82 | end 83 | 84 | def count_todo_in_file(path) 85 | n = 0 86 | File.open(path) {|f| 87 | pp = BitClust::LineCollector.wrap(f) 88 | pp.grep(/\A\#@todo/) { n += 1 } 89 | } 90 | n 91 | end 92 | 93 | class Table 94 | def Table.parse_file(path) 95 | File.open(path) {|f| 96 | _, *dates = f.gets.split 97 | ents = f.map {|line| 98 | name, *ns = line.split 99 | Entry.new(name, ns.map {|n| n.to_i }) 100 | } 101 | return new(dates, ents) 102 | } 103 | end 104 | 105 | include Enumerable 106 | 107 | def initialize(dates, ents) 108 | @dates = dates 109 | @entries = ents 110 | end 111 | 112 | attr_reader :dates 113 | attr_reader :entries 114 | 115 | def header 116 | (["-"] + @dates).join(' ') 117 | end 118 | 119 | def each(&block) 120 | @entries.each(&block) 121 | end 122 | end 123 | 124 | class Entry 125 | def initialize(name, ns) 126 | @name = name 127 | @counts = ns 128 | end 129 | 130 | attr_reader :name 131 | attr_reader :counts 132 | 133 | def total 134 | @counts.first 135 | end 136 | 137 | def count 138 | @counts.last 139 | end 140 | 141 | def push(n) 142 | @counts.push n 143 | end 144 | 145 | def serialize 146 | @name + "\t" + @counts.join("\t") 147 | end 148 | end 149 | 150 | main 151 | -------------------------------------------------------------------------------- /lib/bitclust/lineinput.rb: -------------------------------------------------------------------------------- 1 | # 2 | # $Id: lineinput.rb 3723 2007-03-28 21:34:13Z aamine $ 3 | # 4 | # Copyright (c) 2002-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the terms of 8 | # the GNU LGPL, Lesser General Public License version 2.1. 9 | # 10 | 11 | require 'stringio' 12 | 13 | # Utility class for line-wise file parsing 14 | class LineInput 15 | 16 | def LineInput.for_string(s) 17 | new(StringIO.new(s)) 18 | end 19 | 20 | def initialize(f) 21 | @input = f 22 | @buf = [] 23 | @lineno = 0 24 | @eof_p = false 25 | end 26 | 27 | def inspect 28 | "\#<#{self.class} file=#{@input.inspect} line=#{lineno()}>" 29 | end 30 | 31 | def eof? 32 | @eof_p 33 | end 34 | 35 | def path 36 | @input.path 37 | end 38 | 39 | def lineno 40 | @lineno 41 | end 42 | 43 | def gets 44 | unless @buf.empty? 45 | @lineno += 1 46 | return @buf.pop 47 | end 48 | return nil if @eof_p # to avoid ARGF blocking. 49 | line = @input.gets 50 | @eof_p = true unless line 51 | @lineno += 1 52 | line 53 | end 54 | 55 | def ungets(line) 56 | return unless line 57 | @lineno -= 1 58 | @buf.push line 59 | line 60 | end 61 | 62 | def peek 63 | line = gets() 64 | ungets line if line 65 | line 66 | end 67 | 68 | def next? 69 | peek() ? true : false 70 | end 71 | 72 | def skip_blank_lines 73 | n = 0 74 | while line = gets() 75 | unless line.strip.empty? 76 | ungets line 77 | return n 78 | end 79 | n += 1 80 | end 81 | n 82 | end 83 | 84 | def gets_if(re, index = nil) 85 | line = gets() 86 | if not line or not (re =~ line) 87 | ungets line 88 | return nil 89 | end 90 | return $~[index] if index 91 | line 92 | end 93 | 94 | def gets_unless(re) 95 | line = gets() 96 | if not line or re =~ line 97 | ungets line 98 | return nil 99 | end 100 | line 101 | end 102 | 103 | def each 104 | while line = gets() 105 | yield line 106 | end 107 | end 108 | 109 | def while_match(re) 110 | while line = gets() 111 | unless re =~ line 112 | ungets line 113 | return 114 | end 115 | yield line 116 | end 117 | nil 118 | end 119 | 120 | def getlines_while(re) 121 | buf = [] 122 | while_match(re) do |line| 123 | buf.push line 124 | end 125 | buf 126 | end 127 | 128 | alias span getlines_while # from Haskell 129 | 130 | def until_match(re) 131 | while line = gets() 132 | if re =~ line 133 | ungets line 134 | return 135 | end 136 | yield line 137 | end 138 | nil 139 | end 140 | 141 | def getlines_until(re) 142 | buf = [] 143 | until_match(re) do |line| 144 | buf.push line 145 | end 146 | buf 147 | end 148 | 149 | alias break getlines_until # from Haskell 150 | 151 | def until_terminator(re) 152 | while line = gets() 153 | return if re =~ line # discard terminal line 154 | yield line 155 | end 156 | nil 157 | end 158 | 159 | def getblock(term_re) 160 | buf = [] 161 | until_terminator(term_re) do |line| 162 | buf.push line 163 | end 164 | buf 165 | end 166 | 167 | end 168 | -------------------------------------------------------------------------------- /test/test_methoddatabase.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | require 'bitclust' 3 | require 'bitclust/methoddatabase' 4 | 5 | class TestMethodDatabase < Test::Unit::TestCase 6 | def setup 7 | @prefix = 'db' 8 | @root = 'src' 9 | setup_files 10 | @db = BitClust::MethodDatabase.new(@prefix) 11 | # init database 12 | @db.init 13 | @db.transaction { 14 | [ 15 | %w[version 1.9.1], 16 | %w[encoding utf-8] 17 | ].each do |k,v| 18 | @db.propset(k, v) 19 | end 20 | } 21 | # update database 22 | @db.transaction { 23 | @db.update_by_stdlibtree(@root) 24 | } 25 | end 26 | 27 | def teardown 28 | FileUtils.rm_r([@prefix, @root], :force => true) 29 | end 30 | 31 | def test_search_methods__method 32 | result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'at_exit')) 33 | assert_not_nil result.records.first.entry 34 | assert_equal 1, result.records.size 35 | assert_equal 'at_exit', result.records.first.entry.name 36 | end 37 | 38 | def test_search_methods__methods 39 | result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'foo')) 40 | assert_not_nil result.records.first.entry 41 | assert_equal 2, result.records.size 42 | assert_equal %w[Foo Bar], result.records.map(&:entry).map(&:klass).map(&:name) 43 | assert_equal 'foo', result.records.first.entry.name 44 | end 45 | 46 | def test_search_methods__constant 47 | result = @db.search_methods(BitClust::MethodNamePattern.new(nil, nil, 'AAA')) 48 | assert_not_nil result.records.first.entry 49 | assert_equal 1, result.records.size 50 | assert_equal 'AAA', result.records.first.entry.name 51 | end 52 | 53 | def test_dynamic_include 54 | assert_equal(["BazA"], 55 | @db.get_class("A").dynamically_included.map{|m| m.name}) 56 | assert_equal(["BazB"], 57 | @db.get_class("B").dynamically_included.map{|m| m.name}) 58 | end 59 | 60 | private 61 | def setup_files 62 | FileUtils.mkdir_p("#{@root}/_builtin") 63 | 64 | File.open("#{@root}/LIBRARIES", 'w+') do |file| 65 | file.puts '_builtin' 66 | file.puts 'dyn_include_open_a' 67 | file.puts 'dyn_include_reopen_a' 68 | file.puts 'dyn_include_reopen_b' 69 | file.puts 'dyn_include_open_b' 70 | end 71 | 72 | File.open("#{@root}/_builtin.rd", 'w+') do |file| 73 | file.puts <<'HERE' 74 | description 75 | 76 | = class Foo < Object 77 | desctiption 78 | == Instance Methods 79 | --- foo 80 | == Constants 81 | --- AAA 82 | = class Bar < Object 83 | == Instance Methods 84 | --- foo 85 | = module Kernel 86 | description 87 | == Module Functions 88 | --- at_exit{ ... } -> Proc 89 | aaa 90 | 91 | HERE 92 | end 93 | 94 | File.open("#{@root}/dyn_include_open_a.rd", 'w+') do |file| 95 | file.puts "= class A" 96 | end 97 | 98 | File.open("#{@root}/dyn_include_reopen_a.rd", 'w+') do |file| 99 | file.puts "= module BazA" 100 | file.puts "= reopen A" 101 | file.puts "include BazA" 102 | end 103 | 104 | File.open("#{@root}/dyn_include_open_b.rd", 'w+') do |file| 105 | file.puts "= class B" 106 | end 107 | 108 | File.open("#{@root}/dyn_include_reopen_b.rd", 'w+') do |file| 109 | file.puts "= module BazB" 110 | file.puts "= reopen B" 111 | file.puts "include BazB" 112 | end 113 | end 114 | end 115 | -------------------------------------------------------------------------------- /test/test_runner.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust' 2 | require 'bitclust/runner' 3 | 4 | class TestRunner < Test::Unit::TestCase 5 | def setup 6 | @runner = BitClust::Runner.new 7 | home_directory = Pathname(ENV['HOME']) 8 | @config_path = home_directory + ".bitclust/config" 9 | @config = { 10 | :default_version => "1.9.3", 11 | :database_prefix => "/home/user/.bitclust/db" 12 | } 13 | @prefix = "/home/user/.bitclust/db-1.9.3" 14 | @db = Object.new 15 | end 16 | 17 | def test_run_setup 18 | command = mock(Object.new) 19 | mock(::BitClust::Subcommands::SetupCommand).new.returns(command) 20 | mock(@runner).load_config.returns(@config) 21 | command.parse([]) 22 | command.exec([], {:prefix => @prefix, :capi => false}).returns(nil) 23 | @runner.run(["setup"]) 24 | end 25 | 26 | def test_run_server 27 | command = mock(Object.new) 28 | mock(::BitClust::Subcommands::ServerCommand).new.returns(command) 29 | mock(@runner).load_config.returns(@config) 30 | command.parse([]) 31 | command.exec([], {:prefix => @prefix, :capi => false}).returns(nil) 32 | @runner.run(["server"]) 33 | end 34 | 35 | def test_run_init 36 | command = mock(Object.new) 37 | mock(::BitClust::Subcommands::InitCommand).new.returns(command) 38 | mock(@runner).load_config.returns(@config) 39 | command.parse(["version=1.9.3", "encoding=utf-8"]) 40 | command.exec(["version=1.9.3", "encoding=utf-8"], {:prefix=>@prefix, :capi => false}).returns(nil) 41 | @runner.run(["init", "version=1.9.3", "encoding=utf-8"]) 42 | end 43 | 44 | def test_run_list 45 | command = mock(Object.new) 46 | mock(::BitClust::Subcommands::ListCommand).new.returns(command) 47 | mock(@runner).load_config.returns(@config) 48 | command.parse(["--library"]) 49 | command.exec(["--library"], {:prefix=>@prefix, :capi => false}) 50 | @runner.run(["list", "--library"]) 51 | end 52 | 53 | def test_run_lookup 54 | command = mock(Object.new) 55 | mock(::BitClust::Subcommands::ListCommand).new.returns(command) 56 | mock(@runner).load_config.returns(@config) 57 | command.parse(["--library=optparse"]) 58 | command.exec(["--library=optparse"], {:prefix=>@prefix, :capi => false}) 59 | @runner.run(["list", "--library=optparse"]) 60 | end 61 | 62 | def test_run_searcher 63 | command = mock(Object.new) 64 | mock(::BitClust::Searcher).new.returns(command) 65 | mock(@runner).load_config.returns(@config) 66 | command.parse(["String#gsub"]) 67 | command.exec(["String#gsub"], {:prefix=>@prefix, :capi => false}) 68 | @runner.run(["search", "String#gsub"]) 69 | end 70 | 71 | def test_run_query 72 | command = mock(Object.new) 73 | mock(::BitClust::Subcommands::QueryCommand).new.returns(command) 74 | mock(@runner).load_config.returns(@config) 75 | command.parse(["db.properties"]) 76 | command.exec(["db.properties"], {:prefix=>@prefix, :capi => false}) 77 | @runner.run(["query", "db.properties"]) 78 | end 79 | 80 | def test_run_update 81 | command = mock(Object.new) 82 | mock(::BitClust::Subcommands::UpdateCommand).new.returns(command) 83 | mock(@runner).load_config.returns(@config) 84 | command.parse(["_builtin/String"]) 85 | command.exec(["_builtin/String"], {:prefix=>@prefix, :capi => false}) 86 | @runner.run(["update", "_builtin/String"]) 87 | end 88 | 89 | def test_run_property 90 | command = mock(Object.new) 91 | mock(::BitClust::Subcommands::PropertyCommand).new.returns(command) 92 | mock(@runner).load_config.returns(@config) 93 | command.parse(["--list"]) 94 | command.exec(["--list"], {:prefix=>@prefix, :capi => false}) 95 | @runner.run(["property", "--list"]) 96 | end 97 | end 98 | -------------------------------------------------------------------------------- /data/bitclust/template.lillia/class: -------------------------------------------------------------------------------- 1 | <% @title = "#{@entry.type} #{@entry.name}" %> 2 |
3 | 4 |

5 | <%= manual_home_link() %> 6 | > <%= _('All Libraries') %> 7 | > <%= friendly_library_link(@entry.library.name) %> 8 | > <%=h _(@entry.type.to_s + ' %s', @entry.name) %> 9 |

10 | 11 | <% 12 | headline_init 13 | %> 14 | <%= headline("#{@entry.type} #{@entry.name}" + @entry.ancestors[1..@alevel].map{|c| " + #{c.name}" }.join) %> 15 |

16 | <% 17 | myself, *supers = @entry.ancestors 18 | n = 0 19 | %> 20 | <%= _('ancestors') %>: <%= escape_html(myself.name) %> 21 | <% supers.each do |c| %> 22 | <%= @conf[:tochm_mode] ? "<" : a_href("?a=#{n}", "<") %> <%= class_link(c.name) %> 23 | <% n += 1 %> 24 | <% end %> 25 | 26 | <% unless @entry.extended.empty? %> 27 |
extend: <%= @entry.extended.map {|c| class_link(c.name) }.join(', ') %> 28 | <% end %> 29 | <% unless @entry.aliases.empty? %> 30 |
aliases: <%=h @entry.aliases.map{|c| c.name}.join(', ') %> 31 | <% end %> 32 | <% unless @entry.dynamically_included.empty? %> 33 |
dynamic include: 34 | <%= @entry.dynamically_included.map{|m| 35 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 36 | }.join(", ") 37 | %> 38 | <% end %> 39 | <% unless @entry.dynamically_extended.empty? %> 40 |
dynamic extend: 41 | <%= @entry.dynamically_extended.map{|m| 42 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 43 | }.join(", ") 44 | %> 45 | <% end %> 46 |

47 | 48 | <% headline_push %> 49 | 50 | <%= headline("Abstract") %> 51 | <%= compile_rd(@entry.source) %> 52 | 53 | <% 54 | ents = @entry.partitioned_entries(@alevel) 55 | [[_('Singleton Methods'), ents.singleton_methods ], 56 | [_('Instance Methods'), ents.instance_methods ], 57 | [_('Private Singleton Methods'), ents.private_singleton_methods ], 58 | [_('Private Instance Methods'), ents.private_instance_methods ], 59 | [_('Module Functions'), ents.module_functions ], 60 | [_('Constants'), ents.constants ], 61 | [_('Special Variables'), ents.special_variables ], 62 | [_('Added Methods'), ents.added ] ]\ 63 | .each do |label, entries| 64 | unless entries.empty? %> 65 | <%= headline(label) %> 66 |
67 | <% 68 | headline_push 69 | entries.each do |m| 70 | %> 71 | 72 | <%= compile_method(m) %> 73 | <% 74 | end 75 | headline_pop 76 | %> 77 |
78 | <% 79 | end 80 | end 81 | headline_pop 82 | %> 83 |
84 | 85 |
86 | 87 |
88 |

<%= _('Methods') %>

89 |
90 | 91 |
92 |
    93 | <% @entry.entries(@alevel).sort.each do |m| %> 94 |
  • <%=h m.name %>
  • 95 | <% end %> 96 |
97 |
98 | 99 | 100 |
101 |

<%= _('Classes') %>

102 |
103 | 104 |
105 |
    106 | <% 107 | lib = @entry.library 108 | ((lib.all_classes - lib.all_error_classes).sort + lib.all_error_classes).each do |c| 109 | %> 110 |
  • <%= class_link(c.name, "#{c.name}") %>
  • 111 | <% end %> 112 |
113 |
114 | 115 |
116 | -------------------------------------------------------------------------------- /tools/update-database.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'pathname' 4 | 5 | def srcdir_root 6 | (Pathname.new(__FILE__).realpath.dirname + '..').cleanpath 7 | end 8 | 9 | $LOAD_PATH.unshift srcdir_root() + 'lib' 10 | 11 | require 'bitclust' 12 | require 'fileutils' 13 | require 'net/smtp' 14 | require 'socket' 15 | require 'time' 16 | require 'optparse' 17 | 18 | include FileUtils 19 | 20 | def main 21 | version = '1.9.0' 22 | host = nil 23 | port = Net::SMTP.default_port 24 | from = nil 25 | to = nil 26 | 27 | parser = OptionParser.new 28 | parser.banner = "Usage: #{File.basename($0, '.*')} --from=ADDR --to=ADDR --smtp-host=NAME [--smtp-port-NUM] " 29 | parser.on('--from=ADDR', 'From: address of error report.') {|addr| 30 | from = addr 31 | } 32 | parser.on('--to=ADDR', 'To: address of error report.') {|addr| 33 | to = addr 34 | } 35 | parser.on('--smtp-host=NAME', 'SMTP host to send mail.') {|name| 36 | host = name 37 | } 38 | parser.on('--smtp-port=NUM', 'SMTP port to send mail.') {|num| 39 | port = num.to_i 40 | } 41 | parser.on('--help', 'Prints this message and quit.') { 42 | puts parser.help 43 | exit 0 44 | } 45 | begin 46 | parser.parse! 47 | rescue OptionParser::ParseError => err 48 | $stderr.puts err.message 49 | $stderr.puts parser.help 50 | exit 1 51 | end 52 | unless ARGV.size == 1 53 | $stderr.puts "wrong number of argument (expected 1)" 54 | exit 1 55 | end 56 | reporter = SMTPReporter.new(:host => host, :port => port, 57 | :from => from, :to => to) 58 | cwd = ARGV[0] 59 | Dir.chdir cwd 60 | begin 61 | update_database "#{cwd}/var/#{version}", "#{cwd}/src", version 62 | clear_error 63 | rescue BitClust::Error, Errno::ENOENT => err 64 | reporter.report_error err if new_error?(err) 65 | save_error err 66 | end 67 | end 68 | 69 | def update_database(dbdir, doctree, version) 70 | tmpdir = 'db.tmp' 71 | build_database tmpdir, doctree, version 72 | begin 73 | File.rename dbdir, 'db.old' 74 | rescue Errno::ENOENT 75 | end 76 | mkdir_p File.dirname(dbdir) 77 | File.rename tmpdir, dbdir 78 | ensure 79 | rm_rf 'db.old' 80 | rm_rf tmpdir 81 | end 82 | 83 | def build_database(prefix, doctree, version) 84 | db = BitClust::MethodDatabase.new(prefix) 85 | db.init 86 | db.transaction { 87 | db.propset 'version', version 88 | db.propset 'encoding', 'utf-8' 89 | } 90 | db.transaction { 91 | db.update_by_stdlibtree doctree 92 | } 93 | end 94 | 95 | LASTLOG_FILE = 'lasterror.log' 96 | 97 | def new_error?(err) 98 | return true unless File.exist?(LASTLOG_FILE) 99 | serialize_error(err) != File.read(LASTLOG_FILE) 100 | end 101 | 102 | def save_error(err) 103 | File.open(LASTLOG_FILE, 'w') {|f| 104 | f.write serialize_error(err) 105 | } 106 | end 107 | 108 | def clear_error 109 | rm_f LASTLOG_FILE 110 | end 111 | 112 | def serialize_error(err) 113 | msgline = "#{err.message} (#{err.class})" 114 | backtraces = err.backtrace.map {|s| "\t#{s}" } 115 | ([msgline] + backtraces).join("\n") 116 | end 117 | 118 | class SMTPReporter 119 | def initialize(h) 120 | @host = h[:host] 121 | @port = h[:port] 122 | @from = h[:from] 123 | @to = h[:to] 124 | end 125 | 126 | def report_error(err) 127 | send_message "[build error] #{err.message}", serialize_error(err) 128 | end 129 | 130 | private 131 | 132 | def send_message(subject, body) 133 | Net::SMTP.start(@host, @port, Socket.gethostname) {|smtp| 134 | smtp.send_mail(<<-End, @from, @to) 135 | Date: #{Time.now.rfc2822} 136 | From: #{@from} 137 | To: #{@to} 138 | Subject: #{subject} 139 | 140 | #{body} 141 | End 142 | } 143 | end 144 | end 145 | 146 | main 147 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/htmlfile_command.rb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2006-2007 Minero Aoki 2 | # 3 | # This program is free software. 4 | # You can distribute/modify this program under the Ruby License. 5 | # 6 | 7 | require 'bitclust' 8 | require 'bitclust/subcommand' 9 | 10 | module BitClust 11 | module Subcommands 12 | class HtmlfileCommand < Subcommand 13 | def initialize 14 | super 15 | @target = nil 16 | @templatedir = srcdir_root + "data/bitclust/template.offline" 17 | @baseurl = "file://" + srcdir_root.to_s 18 | @version = "2.0.0" 19 | @parser.banner = "Usage: #{File.basename($0, '.*')} htmlfile [options] rdfile" 20 | @parser.on('--target=NAME', 'Compile NAME to HTML.') {|name| 21 | @target = name 22 | } 23 | @parser.on('--force', '-f', 'Force to use rd_file template.') {|name| 24 | @rd_file = true 25 | } 26 | @parser.on('--ruby_version=VER', '--ruby=VER', 'Set Ruby version') {|version| 27 | @version = version 28 | } 29 | @parser.on('--baseurl=URL', 'Base URL of generated HTML') {|url| 30 | @baseurl = url 31 | } 32 | @parser.on('--templatedir=PATH', 'Template directory') {|path| 33 | @templatedir = path 34 | } 35 | end 36 | 37 | def exec(argv, options) 38 | db = MethodDatabase.dummy({'version' => @version}) 39 | if options[:prefix] 40 | db = MethodDatabase.new(options[:prefix]) 41 | end 42 | @capi = options[:capi] 43 | target_file = argv[0] 44 | options = { 'version' => @version } 45 | manager = ScreenManager.new(:templatedir => @templatedir, 46 | :base_url => @baseurl, 47 | :cgi_url => @baseurl, 48 | :default_encoding => 'utf-8') 49 | 50 | unless @rd_file 51 | begin 52 | if @capi 53 | lib = FunctionReferenceParser.parse_file(target_file, options) 54 | unless @target 55 | raise NotImplementedError, "generating a C API html without --target=NAME is not implemented yet." 56 | end 57 | else 58 | lib = RRDParser.parse_stdlib_file(target_file, options) 59 | end 60 | entry = @target ? lookup(lib, @target) : lib 61 | puts manager.entry_screen(entry, { :database => db }).body 62 | return 63 | rescue ParseError => ex 64 | $stderr.puts ex.message 65 | $stderr.puts ex.backtrace[0], ex.backtrace[1..-1].map{|s| "\tfrom " + s} 66 | end 67 | end 68 | 69 | begin 70 | entry = DocEntry.new(db, target_file) 71 | source = Preprocessor.read(target_file, options) 72 | entry.source = source 73 | puts manager.doc_screen(entry, { :database => db }).body 74 | rescue WriterError => ex 75 | $stderr.puts ex.message 76 | exit 1 77 | end 78 | end 79 | 80 | private 81 | 82 | def lookup(lib, key) 83 | case 84 | when @capi && NameUtils.functionname?(key) 85 | lib.find {|func| func.name == key} 86 | when NameUtils.method_spec?(key) 87 | spec = MethodSpec.parse(key) 88 | if spec.constant? 89 | begin 90 | lib.fetch_class(key) 91 | rescue UserError 92 | lib.fetch_methods(spec) 93 | end 94 | else 95 | lib.fetch_methods(spec) 96 | end 97 | when NameUtils.classname?(key) 98 | lib.fetch_class(key) 99 | else 100 | raise InvalidKey, "wrong search key: #{key.inspect}" 101 | end 102 | end 103 | end 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /lib/bitclust/generators/epub.rb: -------------------------------------------------------------------------------- 1 | require 'fileutils' 2 | require 'tmpdir' 3 | require 'erb' 4 | 5 | require 'bitclust/subcommands/statichtml_command' 6 | 7 | module BitClust 8 | module Generators 9 | class EPUB 10 | def initialize(options = {}) 11 | @options = options.dup 12 | @prefix = options[:prefix] 13 | @capi = options[:capi] 14 | @outputdir = options[:outputdir] 15 | @filename = options[:filename] 16 | @templatedir = options[:templatedir] 17 | @catalog = options[:catalog] 18 | @themedir = options[:themedir] 19 | @fs_casesensitive = options[:fs_casesensitive] 20 | @keep = options[:keep] 21 | @verbose = options[:verbose] 22 | end 23 | 24 | CONTENTS_DIR_NAME = 'OEBPS' 25 | 26 | def generate 27 | make_epub_directory do |epub_directory| 28 | contents_directory = epub_directory + CONTENTS_DIR_NAME 29 | copy_static_files(epub_directory) 30 | generate_xhtml_files(contents_directory) 31 | generate_contents_opf(epub_directory) 32 | pack_epub(epub_directory) 33 | end 34 | end 35 | 36 | private 37 | 38 | def make_epub_directory 39 | dir = Dir.mktmpdir("epub-", @outputdir) 40 | yield Pathname.new(dir) 41 | ensure 42 | FileUtils.rm_rf(dir, :secure => true, :verbose => @verbose) unless @keep 43 | end 44 | 45 | def copy_static_files(epub_directory) 46 | FileUtils.cp(@templatedir + "mimetype", epub_directory, :verbose => @verbose) 47 | FileUtils.cp(@templatedir + "nav.xhtml", epub_directory, :verbose => @verbose) 48 | meta_inf_directory = epub_directory + "META-INF" 49 | FileUtils.mkdir_p(meta_inf_directory, :verbose => @verbose) 50 | FileUtils.cp(@templatedir + "container.xml", meta_inf_directory, :verbose => @verbose) 51 | end 52 | 53 | def generate_xhtml_files(contents_directory) 54 | argv = [ 55 | "--outputdir=#{contents_directory}", 56 | "--templatedir=#{@templatedir}", 57 | "--catalog=#{@catalog}", 58 | "--themedir=#{@themedir}", 59 | "--suffix=.xhtml", 60 | ] 61 | argv << "--fs-casesensitive" if @fs_casesensitive 62 | argv << "--quiet" unless @verbose 63 | options = { 64 | :prefix => @prefix, 65 | :capi => @capi, 66 | } 67 | cmd = BitClust::Subcommands::StatichtmlCommand.new 68 | cmd.parse(argv) 69 | cmd.exec(argv, options) 70 | end 71 | 72 | def generate_contents_opf(epub_directory) 73 | items = [] 74 | glob_relative_path(epub_directory, "#{CONTENTS_DIR_NAME}/class/*.xhtml").each do |path| 75 | items << { 76 | :id => decodename_package(path.basename(".*").to_s), 77 | :path => path 78 | } 79 | end 80 | items.sort_by!{|item| item[:path] } 81 | contents = ERB.new(File.read(@templatedir + "contents"), nil, "-").result(binding) 82 | File.open(epub_directory + "contents.opf", "w") do |f| 83 | f.write contents 84 | end 85 | end 86 | 87 | def pack_epub(epub_directory) 88 | epub_filename = @outputdir + @filename 89 | Dir.chdir(epub_directory.to_s) do 90 | system("zip -0 -X #{epub_filename} mimetype") 91 | system("zip -r #{epub_filename} ./* -x mimetype") 92 | end 93 | end 94 | 95 | def glob_relative_path(path, pattern) 96 | relative_paths = [] 97 | absolute_path_to_search = Pathname.new(path).realpath 98 | Dir.glob(absolute_path_to_search + pattern) do |absolute_path| 99 | absolute_path = Pathname.new(absolute_path) 100 | relative_paths << absolute_path.relative_path_from(absolute_path_to_search) 101 | end 102 | relative_paths 103 | end 104 | 105 | def decodename_package(str) 106 | if @fs_casesensitive 107 | NameUtils.decodename_url(str) 108 | else 109 | NameUtils.decodename_fs(str) 110 | end 111 | end 112 | 113 | def last_modified 114 | Time.now.iso8601 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/bitclust/database.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/database.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/nameutils' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | # Abstract class for BitClust DB. 16 | # Each entry is written in a file. 17 | # 18 | # Has subclass MethodDatabase (Ruby stuff) and FunctionDatabase (C stuff). 19 | class Database 20 | 21 | include NameUtils 22 | 23 | def Database.datadir?(dir) 24 | File.file?("#{dir}/properties") 25 | end 26 | 27 | def Database.connect(uri) 28 | case uri.scheme 29 | when 'file' 30 | new(uri.path) 31 | when 'druby' 32 | DRbObject.new_with_uri(uri.to_s) 33 | else 34 | raise InvalidScheme, "unknown database scheme: #{uri.scheme}" 35 | end 36 | end 37 | 38 | def Database.dummy(params = {}) 39 | db = new(nil) 40 | db.properties['version'] = params['version'] 41 | db 42 | end 43 | 44 | def initialize(prefix) 45 | @prefix = prefix 46 | @properties = nil 47 | @in_transaction = false 48 | @properties_dirty = false 49 | end 50 | 51 | def dummy? 52 | not @prefix 53 | end 54 | 55 | # 56 | # Transaction 57 | # 58 | 59 | def transaction 60 | @in_transaction = true 61 | yield 62 | return if dummy? 63 | if @properties_dirty 64 | save_properties 'properties', @properties 65 | @properties_dirty = false 66 | end 67 | commit if dirty? 68 | ensure 69 | @in_transaction = false 70 | end 71 | 72 | # abstract dirty? 73 | # abstract clear_dirty 74 | # abstract commit 75 | 76 | def check_transaction 77 | return if dummy? 78 | unless @in_transaction 79 | raise NotInTransaction, "database changed without transaction" 80 | end 81 | end 82 | private :check_transaction 83 | 84 | # 85 | # Properties 86 | # 87 | 88 | def properties 89 | @properties ||= 90 | begin 91 | h = load_properties('properties') 92 | h.delete 'source' if h['source'] and h['source'].strip.empty? 93 | h 94 | end 95 | end 96 | 97 | def propkeys 98 | properties().keys 99 | end 100 | 101 | def propget(key) 102 | properties()[key] 103 | end 104 | 105 | def propset(key, value) 106 | check_transaction 107 | properties()[key] = value 108 | @properties_dirty = true 109 | end 110 | 111 | def encoding 112 | propget('encoding') 113 | end 114 | 115 | # 116 | # Direct File Access Layer: BitClust internal use only 117 | # 118 | 119 | def exist?(rel) 120 | return false unless @prefix 121 | File.exist?(realpath(rel)) 122 | end 123 | 124 | def entries(rel) 125 | Dir.entries(realpath(rel))\ 126 | .reject {|ent| /\A[\.=]/ =~ ent }\ 127 | .map {|ent| decodeid(ent) } 128 | rescue Errno::ENOENT 129 | return [] 130 | end 131 | 132 | def makepath(rel) 133 | FileUtils.mkdir_p realpath(rel) 134 | end 135 | 136 | def load_properties(rel) 137 | h = {} 138 | fopen(realpath(rel), 'r:UTF-8') {|f| 139 | while line = f.gets 140 | k, v = line.strip.split('=', 2) 141 | break unless k 142 | h[k] = v 143 | end 144 | h['source'] = f.read 145 | } 146 | h 147 | rescue Errno::ENOENT 148 | return {} 149 | end 150 | 151 | def save_properties(rel, h) 152 | source = h.delete('source') 153 | atomic_write_open(rel) {|f| 154 | h.each do |key, val| 155 | f.puts "#{key}=#{val}" 156 | end 157 | f.puts 158 | f.puts source 159 | } 160 | end 161 | 162 | def read(rel) 163 | File.read(realpath(rel)) 164 | end 165 | 166 | def foreach_line(rel, &block) 167 | File.foreach(realpath(rel), &block) 168 | end 169 | 170 | def atomic_write_open(rel, &block) 171 | tmppath = realpath(rel) + '.writing' 172 | fopen(tmppath, 'wb', &block) 173 | File.rename tmppath, realpath(rel) 174 | ensure 175 | File.unlink tmppath rescue nil 176 | end 177 | 178 | def realpath(rel) 179 | "#{@prefix}/#{encodeid(rel)}" 180 | end 181 | private :realpath 182 | 183 | end 184 | 185 | end 186 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/ancestors_command.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust' 2 | require 'bitclust/crossrubyutils' 3 | 4 | require 'pathname' 5 | require 'optparse' 6 | require 'set' 7 | 8 | module BitClust 9 | module Subcommands 10 | class AncestorsCommand < Subcommand 11 | include CrossRubyUtils 12 | 13 | def initialize 14 | super 15 | @prefix = nil 16 | @requires = [] 17 | @version = RUBY_VERSION 18 | @all = false 19 | @verbose = false 20 | @parser.banner = <<-BANNER 21 | Usage: #{File.basename($0, '.*')} ancestors [-r] [--ruby=] --db=PATH 22 | #{File.basename($0, '.*')} ancestors [-r] [--ruby=] --db=PATH --all 23 | NG Sample: 24 | $ #{File.basename($0, '.*')} ancestors -rfoo --ruby=1.9.1 --db=./db Foo 25 | NG : Foo 26 | + FooModule (The Ruby have this class/module in ancestors of the class) 27 | - BarModule (The Database have this class/module in ancestors of the class) 28 | Options: 29 | BANNER 30 | @parser.on('-d', '--database=PATH', 'Database prefix.') {|path| 31 | @prefix = path 32 | } 33 | @parser.on('-r LIB', 'Requires library LIB') {|lib| 34 | @requires.push lib 35 | } 36 | @parser.on('--ruby=[VER]', "The version of Ruby interpreter"){|ver| 37 | @version = ver 38 | } 39 | @parser.on('-v', '--verbose', 'Show differences'){ 40 | @verbose = true 41 | } 42 | @parser.on('--all', 'Check anccestors for all classes'){ 43 | @all = true 44 | } 45 | end 46 | 47 | def exec(argv, options) 48 | classname = argv[0] 49 | db = MethodDatabase.new(@prefix) 50 | ruby = get_ruby(@version) 51 | if classname && !@all 52 | check_ancestors(db, ruby, @requires, classname) 53 | else 54 | $stderr.puts 'check all...' 55 | check_all_ancestors(db, ruby, @requires) 56 | end 57 | end 58 | 59 | private 60 | 61 | def ancestors(ruby, requires, classname) 62 | req = requires.map{|lib| 63 | unless '_builtin' == lib 64 | "-r#{lib}" 65 | else 66 | '' 67 | end 68 | }.join(" ") 69 | script = <<-SRC 70 | c = #{classname} 71 | puts c.ancestors.join("\n") 72 | SRC 73 | puts "#{ruby} #{req} -e '#{script}'" 74 | `#{ruby} #{req} -e '#{script}'`.split 75 | end 76 | 77 | def check_ancestors(db, ruby, requires, classname) 78 | a = ancestors(ruby, requires, classname) 79 | p a 80 | begin 81 | b = db.fetch_class(classname).ancestors.map(&:name) 82 | rescue ClassNotFound => ex 83 | $stderr.puts ex.backtrace 84 | $stderr.puts "class not found in database : #{classname}" 85 | b = [] 86 | end 87 | unless a.to_set == b.to_set 88 | puts "NG : #{classname}" 89 | puts (a-b).map{|c| "+ #{c}" }.join("\n") 90 | puts (b-a).map{|c| "- #{c}" }.join("\n") 91 | else 92 | puts "OK : #{classname}" if @verbose 93 | end 94 | end 95 | 96 | def check_all_ancestors(db, ruby, requires) 97 | classnames = [] 98 | requires.each do |lib| 99 | classnames.push(*defined_classes(ruby, lib, [])) 100 | end 101 | classnames.each do |classname| 102 | check_ancestors(db, ruby, requires, classname) 103 | end 104 | end 105 | 106 | def defined_classes(ruby, lib, rejects) 107 | script = <<-SCRIPT 108 | def class_extent 109 | result = [] 110 | ObjectSpace.each_object(Module) do |c| 111 | result.push c 112 | end 113 | result 114 | end 115 | 116 | %w(#{rejects.join(" ")}).each do |lib| 117 | begin 118 | require lib 119 | rescue LoadError 120 | end 121 | end 122 | if "#{lib}" == "_builtin" 123 | class_extent().each do |c| 124 | puts c 125 | end 126 | else 127 | before = class_extent() 128 | begin 129 | require "#{lib}" 130 | rescue LoadError 131 | $stderr.puts "\#{RUBY_VERSION} (\#{RUBY_RELEASE_DATE}): library not exist: #{lib}" 132 | exit 133 | end 134 | after = class_extent() 135 | (after - before).each do |c| 136 | puts c 137 | end 138 | end 139 | SCRIPT 140 | output = `#{ruby} -e '#{script}'` 141 | output.split 142 | end 143 | end 144 | end 145 | end 146 | -------------------------------------------------------------------------------- /data/bitclust/template/class: -------------------------------------------------------------------------------- 1 | <% @title = "#{@entry.type} #{@entry.name}" %> 2 |

3 | <%= manual_home_link() %> 4 | > <%= _('All Libraries') %> 5 | > <%= friendly_library_link(@entry.library.name) %> 6 | > <%=h _(@entry.type.to_s + ' %s', @entry.name) %> 7 |

8 | <%= search_form() %> 9 | 10 | <% 11 | headline_init 12 | %> 13 | <%= headline("#{@entry.type} #{@entry.name}" + @entry.ancestors[1..@alevel].map{|c| " + #{c.name}" }.join) %> 14 |

15 | <% 16 | myself, *supers = @entry.ancestors 17 | n = 0 18 | %> 19 | <% unless @entry.alias? %> 20 | <%= _('ancestors') %>: <%= escape_html(myself.name) %> 21 | <% supers.each do |c| %> 22 | <%= @conf[:tochm_mode] ? "<" : a_href("?a=#{n}", "<") %> <%= class_link(c.name) %> 23 | <% n += 1 %> 24 | <% end %> 25 | <% end %> 26 | 27 | <% unless @entry.extended.empty? %> 28 |
extend: <%= @entry.extended.map {|c| class_link(c.name) }.join(', ') %> 29 | <% end %> 30 | <% unless @entry.aliases.empty? %> 31 |
aliases: <%=h @entry.aliases.map{|c| c.name}.join(', ') %> 32 | <% end %> 33 | <% unless @entry.dynamically_included.empty? %> 34 |
dynamic include: 35 | <%= @entry.dynamically_included.map{|m| 36 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 37 | }.join(", ") 38 | %> 39 | <% end %> 40 | <% unless @entry.dynamically_extended.empty? %> 41 |
dynamic extend: 42 | <%= @entry.dynamically_extended.map{|m| 43 | class_link(m.name) + " (by " + library_link(m.library.name) + ")" 44 | }.join(", ") 45 | %> 46 | <% end %> 47 |

48 | <% 49 | headline_push 50 | %> 51 | <%= headline(_("Abstract")) %> 52 | <%= compile_rd(@entry.source) %> 53 | 54 | <% 55 | ents = @entry.partitioned_entries(@alevel) 56 | [[_('Singleton Methods'), ents.singleton_methods ], 57 | [_('Instance Methods'), ents.instance_methods ], 58 | [_('Private Singleton Methods'), ents.private_singleton_methods ], 59 | [_('Private Instance Methods'), ents.private_instance_methods ], 60 | [_('Module Functions'), ents.module_functions ], 61 | [_('Constants'), ents.constants ], 62 | [_('Special Variables'), ents.special_variables ]]\ 63 | .each do |label, entries| 64 | unless entries.empty? %> 65 | <%= headline(label) %> 66 | 67 | 68 | 69 | 70 | 71 | <% 72 | headline_push 73 | entries.each do |m| 74 | foreach_method_chunk(m.source) do |sigs, src| %> 75 | 76 | 81 | <% if m.redefined? %> 82 | 83 | 84 | <% elsif @entry.name != m.klass.name %> 85 | 86 | 87 | <% else %> 88 | 89 | <% end %> 90 | 91 | <% 92 | end 93 | end 94 | headline_pop 95 | %> 96 |
<%= _('Signature') %><%= _('Description') %>
77 | 78 | <%= sigs.map {|sig| h(sig.friendly_string) }.join("
") %> 79 |
80 |
<%= compile_rd(src) %><%= library_link(m.library.name) %><%= compile_rd(src) %><%= class_link(m.klass.name) %><%= compile_rd(src) %>
97 | <% 98 | end 99 | end 100 | %> 101 | 102 | <% specs = @entry.inherited_method_specs.sort 103 | unless specs.empty? %> 104 | <%= headline(_('Inherited Methods')) %> 105 |

106 | <% specs.each do |s| %> 107 | <%= method_link(s.to_s, (s.type == '#' ? s.method : s.type + s.method)) %> 108 | <% end %> 109 |

110 | <% end %> 111 | 112 | <% 113 | [[_('Added Methods'), ents.added]]\ 114 | .each do |label, entries| 115 | unless entries.empty? %> 116 | <%= headline(label) %> 117 | 118 | 119 | 120 | 121 | 122 | 123 | <% 124 | headline_push 125 | entries.each do |m| 126 | foreach_method_chunk(m.source) do |sigs, src| %> 127 | 128 | 133 | 134 | 135 | 136 | <% 137 | end 138 | end 139 | headline_pop 140 | %> 141 |
<%= _('Signature') %><%= _('Description') %><%= _('Library') %>
129 | 130 | <%= sigs.map {|sig| h(sig.friendly_string) }.join("
") %> 131 |
132 |
<%= compile_rd(src) %><%= library_link(m.library.name) %>
142 | <% 143 | end 144 | end 145 | headline_pop 146 | %> 147 | 148 | -------------------------------------------------------------------------------- /tools/bc-convert.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # -*- coding: utf-8 -*- 3 | 4 | $KCODE = 'UTF-8' unless Object.const_defined?(:Encoding) 5 | 6 | require 'stringio' 7 | require 'fileutils' 8 | require 'tmpdir' 9 | require 'optparse' 10 | 11 | def main 12 | mode = :output 13 | parser = OptionParser.new 14 | parser.banner = "Usage: #{File.basename($0, '.*')} [--diff] [file...]" 15 | parser.on('--diff', 'Show the diff between original file and output') { 16 | mode = :diff 17 | } 18 | parser.on('--inplace', 'edit input files in-place (make backup)') { 19 | mode = :inplace 20 | } 21 | parser.on('--help') { 22 | puts parser.help 23 | exit 24 | } 25 | begin 26 | parser.parse! 27 | rescue OptionParser::ParseError => err 28 | $stderr.puts err.message 29 | exit 1 30 | end 31 | case mode 32 | when :output 33 | do_convert ARGF 34 | when :diff 35 | ARGV.each do |path| 36 | diff_output path 37 | end 38 | when :inplace 39 | ARGV.each do |path| 40 | inplace_edit path 41 | end 42 | else 43 | raise "must not happen: mode=#{mode.inspect}" 44 | end 45 | end 46 | 47 | def inplace_edit(path) 48 | str = convert_file(path) 49 | File.rename path, path + '.bak' 50 | File.open(path, 'w') {|f| 51 | f.write str 52 | } 53 | end 54 | 55 | def diff_output(path) 56 | tmppath = "#{Dir.tmpdir}/bc-convert-diff" 57 | File.open(tmppath, 'w') {|f| 58 | f.write convert_file(path) 59 | } 60 | system 'diff', '-u', path, tmppath 61 | ensure 62 | FileUtils.rm_f tmppath 63 | end 64 | 65 | def convert_file(path) 66 | File.open(path) {|f| convert(f) } 67 | end 68 | 69 | def convert(f) 70 | buf = StringIO.new 71 | do_convert f, buf 72 | buf.string 73 | end 74 | 75 | def do_convert(f, out = $stdout) 76 | f.each do |line| 77 | case line 78 | when /\A\#@/ 79 | out.puts line 80 | when /\A\#/ 81 | out.puts '#@' + line 82 | when /\A---\s/ 83 | sig = convert_signature(line.sub(/\A---/, '').sub(/\(\(<.*?>\)\)/i, '').strip) 84 | out.puts "--- #{sig}" 85 | if meta = line.slice(/\(\(<.*?>\)\)/i) 86 | out.puts 87 | out.puts meta 88 | out.puts 89 | end 90 | else 91 | out.puts convert_link(line.rstrip) 92 | end 93 | end 94 | end 95 | 96 | def convert_signature(sig) 97 | case sig 98 | when /\A([\w:\.\#]+[?!]?)\s*(?:[\(\{]|--|->|\z)/ 99 | # name(arg), name{}, name, 100 | # name() -- obsolete 101 | # name() -> return value type 102 | sig 103 | when /\A[\w:]+[\.\#]([+\-<>=~*^&|%\/]+)/ # Complex#+ 104 | sig 105 | when /\Aself\s*(==|===|=~)\s*(\w+)/ # self == other 106 | "#{$1}(#{$2})" 107 | when /\A([\w:\.\#]+)\s*\=(\(|\s*\w+)?/ # name= 108 | "#{remove_class_spec($1)}=(#{remove_paren($2.to_s.strip)})" 109 | when /\A\w+\[(.*)\]=(.*)/ # self[key]= 110 | "[]=(#{$1}, #{$2.strip})" 111 | when /\A[\w\:]+\[(.*)\]/ # self[key] 112 | "[](#{$1})" 113 | when /\Aself\s*([+\-<>=~*^&|%\/]+)\s*(\w+)/ # self + other 114 | "#{$1}(#{$2})" 115 | when /\A([+\-~`])\s*\w+/ # ~ self 116 | case op = $1 117 | when '+', '-' then op + '@' 118 | else op 119 | end 120 | when /\A(?:[\w:]+[\.\#])?(\[\]=?)/ # Matrix.[](i) 121 | sig 122 | when /\A([+\-<>=~*^&|%]+)/ # +(m) 123 | sig 124 | when /\A([A-Z]\w+\*)/ # HKEY_* 125 | sig 126 | when /\Aself([+\-<>=~*^&|%\/\[\]]+)\(\w/ # self+(other) 127 | sig.sub(/\Aself/, '') 128 | else 129 | $stderr.puts "warning: unknown method signature: #{sig.inspect}" 130 | sig 131 | end 132 | end 133 | 134 | def remove_class_spec(str) 135 | str.sub(/\A[A-Z]\w*(?:::[A-Z]\w*)*[\.\#]/, '') 136 | end 137 | 138 | def remove_paren(str) 139 | str.sub(/\A\(/, '').sub(/\)\z/, '') 140 | end 141 | 142 | def convert_link(line) 143 | line.gsub(/\(\(\{(.*?)\}\)\)/) { $1 }\ 144 | .gsub(/\(\(\|(.*?)\|\)\)/) { $1 }\ 145 | .gsub(/\(\(<(.*?)>\)\)/) { convert_href($1) } 146 | end 147 | 148 | def convert_href(link) 149 | case link 150 | when /\Atrap::(.*)/ then "[[trap:#{$1}]]" 151 | when /\Aruby 1\.\S+ feature/ then "((<#{link}>))" 152 | when /\Aobsolete/ then "(())" 153 | when /\A組み込み変数\/(.*)/u then "[[m:#{$1}]]" 154 | when /\A組み込み定数\/(.*)/u then "[[m:Kernel::#{$1}]]" 155 | when /\A組み込み関数\/(.*)/u then "[[m:Kernel\##{$1}]]" 156 | when /\A([\w:]+[\#\.][^|]+)\|/ then "[[m:#{$1}]]" 157 | when /\A(.*?)\|manual page\z/ then "[[man:#{$1}]]" 158 | when /\A([\w:]+)\/(.*)\z/n then "[[m:#{$1}\##{$2}]]" 159 | when /\A([A-Z][\w:]*)\z/ then "[[c:#{$1}]]" 160 | else 161 | "[[unknown:#{link}]]" 162 | end 163 | end 164 | 165 | main 166 | -------------------------------------------------------------------------------- /lib/bitclust/methodentry.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/methodentry.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/entry' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | # Entry for methods(instance methods/singleton methods/module_functions), 16 | # constants and special variables(like $!). 17 | class MethodEntry < Entry 18 | 19 | def MethodEntry.type_id 20 | :method 21 | end 22 | 23 | def initialize(db, id) 24 | super db 25 | @id = id 26 | init_properties 27 | end 28 | 29 | attr_reader :id 30 | 31 | def ==(other) 32 | @id == other.id 33 | end 34 | 35 | alias eql? == 36 | 37 | def hash 38 | @id.hash 39 | end 40 | 41 | def <=>(other) 42 | sort_key() <=> other.sort_key 43 | end 44 | 45 | KIND_NUM = {:defined => 0, :redefined => 1, :added => 2} 46 | 47 | def sort_key 48 | [label(), KIND_NUM[kind()]] 49 | end 50 | 51 | def name 52 | methodid2mname(@id) 53 | end 54 | 55 | # typename = :singleton_method 56 | # | :instance_method 57 | # | :module_function 58 | # | :constant 59 | # | :special_variable 60 | def typename 61 | methodid2typename(@id) 62 | end 63 | 64 | alias type typename 65 | 66 | def typemark 67 | methodid2typemark(@id) 68 | end 69 | 70 | def typechar 71 | methodid2typechar(@id) 72 | end 73 | 74 | def type_label 75 | case typemark() 76 | when '.' then 'singleton method' 77 | when '#' then 'instance method' 78 | when '.#' then 'module function' 79 | when '::' then 'constant' 80 | when '$' then 'variable' 81 | end 82 | end 83 | 84 | def library 85 | @library ||= @db.fetch_library_id(methodid2libid(@id)) 86 | end 87 | 88 | attr_writer :library 89 | 90 | def klass 91 | @klass ||= @db.fetch_class_id(methodid2classid(@id)) 92 | end 93 | 94 | attr_writer :klass 95 | 96 | persistent_properties { 97 | property :names, '[String]' 98 | property :visibility, 'Symbol' # :public | :private | :protected 99 | property :kind, 'Symbol' # :defined | :added | :redefined 100 | property :source, 'String' 101 | } 102 | 103 | def inspect 104 | c, t, m, lib = methodid2specparts(@id) 105 | "\#" 106 | end 107 | 108 | def spec 109 | MethodSpec.new(*methodid2specparts(@id)) 110 | end 111 | 112 | def spec_string 113 | methodid2specstring(@id) 114 | end 115 | 116 | def label 117 | c, t, m, lib = methodid2specparts(@id) 118 | "#{t == '$' ? '' : c}#{t}#{m}" 119 | end 120 | 121 | def short_label 122 | c, t, m, lib = methodid2specparts(@id) 123 | "#{t == '#' ? '' : t}#{m}" 124 | end 125 | 126 | def index_id 127 | "#{methodid2typechar(@id)}_#{encodename_fs(name).gsub(/=/, '--')}".upcase 128 | end 129 | 130 | def labels 131 | c, t, m, lib = methodid2specparts(@id) 132 | names().map {|name| "#{c}#{t}#{name}" } 133 | end 134 | 135 | def name?(name) 136 | names().include?(name) 137 | end 138 | 139 | def name_match?(re) 140 | names().any? {|n| re =~ n } 141 | end 142 | 143 | def really_public? 144 | visibility() == :public 145 | end 146 | 147 | def public? 148 | visibility() != :private 149 | end 150 | 151 | def protected? 152 | visibility() == :protected 153 | end 154 | 155 | def private? 156 | visibility() == :private 157 | end 158 | 159 | def public_singleton_method? 160 | singleton_method? and public? 161 | end 162 | 163 | def private_singleton_method? 164 | singleton_method? and private? 165 | end 166 | 167 | def public_instance_method? 168 | instance_method? and public? 169 | end 170 | 171 | def private_instance_method? 172 | instance_method? and public? 173 | end 174 | 175 | def singleton_method? 176 | t = typename() 177 | t == :singleton_method or t == :module_function 178 | end 179 | 180 | def instance_method? 181 | t = typename() 182 | t == :instance_method or t == :module_function 183 | end 184 | 185 | def constant? 186 | typename() == :constant 187 | end 188 | 189 | def special_variable? 190 | typename() == :special_variable 191 | end 192 | 193 | def defined? 194 | kind() == :defined 195 | end 196 | 197 | def added? 198 | kind() == :added 199 | end 200 | 201 | def redefined? 202 | kind() == :redefined 203 | end 204 | 205 | end 206 | 207 | end 208 | -------------------------------------------------------------------------------- /lib/bitclust/app.rb: -------------------------------------------------------------------------------- 1 | require 'bitclust' 2 | require 'bitclust/interface' 3 | 4 | module BitClust 5 | 6 | # Main class of BitClust server application. 7 | # Actual actions are implemneted by RequestHandler. 8 | # 9 | # Supports Rack and WEBrick. 10 | class App 11 | 12 | def initialize(options) 13 | @options = options 14 | dbpath = options[:dbpath] 15 | baseurl = options[:baseurl] || '' 16 | datadir = options[:datadir] || File.expand_path('../../data/bitclust', File.dirname(__FILE__)) 17 | encoding = options[:encoding] || 'utf-8' 18 | viewpath = options[:viewpath] 19 | capi = options[:capi] 20 | if options[:rack] 21 | request_handler_class = BitClust::RackRequestHandler 22 | else 23 | request_handler_class = BitClust::RequestHandler 24 | end 25 | @interfaces = {} 26 | case dbpath 27 | when String 28 | dbpath = File.expand_path(dbpath) 29 | db = BitClust::MethodDatabase.new(dbpath) 30 | if capi 31 | db = [db, BitClust::FunctionDatabase.new(dbpath)] 32 | end 33 | manager = BitClust::ScreenManager.new( 34 | :base_url => baseurl, 35 | :cgi_url => File.join(baseurl, viewpath), 36 | :datadir => datadir, 37 | :templatedir => options[:templatedir], 38 | :theme => options[:theme], 39 | :encoding => encoding 40 | ) 41 | handler = request_handler_class.new(db, manager) 42 | @interfaces[viewpath] = BitClust::Interface.new { handler } 43 | when Array 44 | dbpaths = dbpath 45 | @versions = [] 46 | dbpaths.each do |dbpath| 47 | next unless /db-([\d_\.]+)/ =~ dbpath 48 | dbpath = File.expand_path(dbpath) 49 | version = $1.tr("_", ".") 50 | @versions << version 51 | if viewpath 52 | version_viewpath = File.join(version, viewpath) 53 | else 54 | version_viewpath = version 55 | end 56 | db = BitClust::MethodDatabase.new(dbpath) 57 | if capi 58 | db = [db, BitClust::FunctionDatabase.new(dbpath)] 59 | end 60 | manager = BitClust::ScreenManager.new( 61 | :base_url => baseurl, 62 | :cgi_url => File.join(baseurl, version_viewpath), 63 | :datadir => datadir, 64 | :templatedir => options[:templatedir], 65 | :theme => options[:theme], 66 | :encoding => encoding 67 | ) 68 | handler = request_handler_class.new(db, manager) 69 | @interfaces[version_viewpath] = BitClust::Interface.new { handler } 70 | $bitclust_context_cache = nil # clear cache 71 | end 72 | end 73 | end 74 | 75 | attr_reader :interfaces, :versions 76 | 77 | def index(req) 78 | case 79 | when @interfaces.size == 1 && viewpath = @options[:viewpath] 80 | # Redirect from '/' to "#{viewpath}/" 81 | @index = "" 82 | when 1 < @interfaces.size 83 | request_path = case 84 | when req.respond_to?(:path_info) 85 | req.path_info 86 | when req.respond_to?(:path) 87 | req.path_info 88 | end 89 | if @versions.any?{|version| %r|\A/?#{version}/?\z| =~ request_path } 90 | viewpath = File.join(request_path, @options[:viewpath]) 91 | @index = "" 92 | else 93 | links = "
    " 94 | @interfaces.keys.sort.each do |v| 95 | if @options[:viewpath] 96 | version = v.sub(@options[:viewpath], '') 97 | else 98 | version = v 99 | end 100 | url = v 101 | links << %Q(
  • #{version}
  • ) 102 | end 103 | links << "
" 104 | if File.exist?("readme.html") 105 | @index = File.read("readme.html").sub(%r!\./bitclust!, '').sub(//) { links } 106 | else 107 | @index = "bitclust#{links}" 108 | end 109 | end 110 | end 111 | end 112 | 113 | def get_instance(server) 114 | self 115 | end 116 | 117 | def service(req, res) 118 | unless %r|/#{File.basename(@options[:baseurl])}/?\z| =~ req.path 119 | raise WEBrick::HTTPStatus::NotFound 120 | end 121 | res.body = index(req) 122 | res['Content-Type'] = 'text/html; charset=utf-8' 123 | end 124 | 125 | def call(env) 126 | [ 127 | 200, 128 | {'Content-Type' => 'text/html; charset=utf-8'}, 129 | [index(Rack::Request.new(env))] 130 | ] 131 | end 132 | end 133 | end 134 | -------------------------------------------------------------------------------- /theme/lillia/test.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /* 4 | default.css 5 | */ 6 | 7 | body { 8 | margin-top: 20px; 9 | margin-left: 2%; 10 | margin-right: 2%; 11 | line-height: 1.3; 12 | text-align: left; 13 | color: black; 14 | font-family: osaka,'MS PGothic',sans-serif; 15 | background-color: white; 16 | } 17 | 18 | /* 19 | span.menuitem { 20 | width: 3.9em; 21 | text-align: center; 22 | padding: 0px 2px; 23 | margin: 0px; 24 | color: white; 25 | background-color: #ddd; 26 | margin-right: 2px; 27 | } 28 | div.menu a:link, 29 | div.menu a:visited { 30 | width: 3.9em; 31 | padding: 0px 2px; 32 | margin: 0px; 33 | color: #33a; 34 | background-color: #ddd; 35 | text-decoration: none; 36 | } 37 | div.menu a:active, 38 | div.menu a:focus, 39 | div.menu a:hover { 40 | width: 3.9em; 41 | padding: 0px 2px; 42 | margin: 0px; 43 | color: #fff; 44 | background-color: #33a; 45 | text-decoration: none; 46 | } 47 | span.menuseparator { 48 | width: 1em; 49 | padding: 0px; 50 | margin: 0px; 51 | } 52 | */ 53 | 54 | address { 55 | text-align: right; 56 | font-size: x-small; 57 | } 58 | 59 | h1 { 60 | font-size: 200%; 61 | text-align: left; 62 | color: white; 63 | background-color: #33a; 64 | padding: 0.4em; 65 | margin: 0.2em 0em 0.5em 0em; 66 | } 67 | 68 | img.sitelogo { 69 | vertical-align: middle; 70 | } 71 | 72 | h2 { 73 | font-size: 150%; 74 | text-align: left; 75 | border-left: 10px solid #33a; 76 | padding-left: 0.5em; 77 | margin-top: 1.5em; 78 | margin-bottom: 0.5em; 79 | } 80 | 81 | h3 { 82 | font-size: 150%; 83 | text-align: left; 84 | margin-top: 0.5em; 85 | margin-bottom: 0.5em; 86 | } 87 | 88 | /* tmp */ 89 | h4 { 90 | font-size: 150%; 91 | text-align: left; 92 | margin-top: 0.5em; 93 | margin-bottom: 0.5em; 94 | } 95 | 96 | /* never use */ 97 | h5, h6 { 98 | background-color: red; 99 | } 100 | 101 | p { 102 | line-height: 1.4; 103 | margin-top: 0.5em; 104 | margin-bottom: 0.5em; 105 | margin-left: 0px; 106 | } 107 | 108 | span.kindinfo { 109 | text-align: right; 110 | margin-left: 8em; 111 | } 112 | 113 | span.compileerror { 114 | color: red; 115 | font-weight: bold; 116 | } 117 | 118 | pre { 119 | line-height: 1.1; 120 | background-color: #eee; 121 | padding: 10px; 122 | font-weight: normal; 123 | } 124 | 125 | blockquote { 126 | background-color: transparent; 127 | padding: 4px; 128 | margin-left: 3em; 129 | font-weight: normal; 130 | } 131 | 132 | ul { 133 | margin-left: 0px; 134 | padding-left: 2em; 135 | } 136 | 137 | li { 138 | margin-left: 0px; 139 | padding-left: 0px; 140 | } 141 | 142 | dl { 143 | margin-left: 0em; 144 | } 145 | 146 | dt { 147 | font-weight: bold; 148 | } 149 | 150 | dl.methodlist dt { 151 | font-family: sans-serif; 152 | } 153 | 154 | dd { 155 | margin: 0.3em 0em 1em 4em; 156 | } 157 | 158 | table { 159 | border-collapse: collapse; 160 | } 161 | 162 | th { 163 | align: left; 164 | valign: top; 165 | text-align: left; 166 | vertical-align: top; 167 | } 168 | 169 | td { 170 | align: left; 171 | valign: top; 172 | text-align: left; 173 | vertical-align: top; 174 | } 175 | 176 | table.entries tr { 177 | /* border: 1px solid gray; */ 178 | align: left; 179 | valign: top; 180 | text-align: left; 181 | vertical-align: top; 182 | } 183 | 184 | td.signature { 185 | padding: 0.5em; 186 | background-color: #DDD; 187 | border: 3px solid white; 188 | width: 20em; 189 | } 190 | 191 | td.description { 192 | padding: 0.5em; 193 | background-color: #EEE; 194 | border: 3px solid white; 195 | } 196 | 197 | td.library { 198 | width: 6em; 199 | padding: 0.5em; 200 | background-color: #DDD; 201 | border: 3px solid white; 202 | } 203 | 204 | 205 | a { 206 | font-weight: bold; 207 | text-decoration: none; 208 | } 209 | a:link { 210 | color: #33a; 211 | background-color: transparent; 212 | } 213 | a:visited { 214 | /* color: #666666; */ 215 | color: #33a; 216 | background-color: transparent; 217 | } 218 | a:hover, a:focus, a:active { 219 | color: #fff; 220 | background-color: #33a; 221 | } 222 | 223 | form { 224 | padding: 0px; 225 | margin: 0px; 226 | } 227 | 228 | hr { 229 | color: #33a; 230 | height: 1px; 231 | } 232 | 233 | @media print { 234 | body { 235 | font-family: osaka,'MS Mincho',serif; 236 | line-height: 1.5; 237 | } 238 | div.menu { 239 | display: none; 240 | } 241 | div.footer { 242 | display: none; 243 | } 244 | h1 { 245 | color: black; 246 | background-color: white; 247 | border-bottom: 2px solid #33a; 248 | padding-bottom: 0em; 249 | } 250 | a, a:link, a:visited { 251 | color: #33a; 252 | font-weight: normal; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /theme/default/test.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /* 4 | default.css 5 | */ 6 | 7 | body { 8 | margin-top: 20px; 9 | margin-left: 2%; 10 | margin-right: 2%; 11 | line-height: 1.3; 12 | text-align: left; 13 | color: black; 14 | font-family: osaka,'MS PGothic',sans-serif; 15 | background-color: white; 16 | } 17 | 18 | /* 19 | span.menuitem { 20 | width: 3.9em; 21 | text-align: center; 22 | padding: 0px 2px; 23 | margin: 0px; 24 | color: white; 25 | background-color: #ddd; 26 | margin-right: 2px; 27 | } 28 | div.menu a:link, 29 | div.menu a:visited { 30 | width: 3.9em; 31 | padding: 0px 2px; 32 | margin: 0px; 33 | color: #33a; 34 | background-color: #ddd; 35 | text-decoration: none; 36 | } 37 | div.menu a:active, 38 | div.menu a:focus, 39 | div.menu a:hover { 40 | width: 3.9em; 41 | padding: 0px 2px; 42 | margin: 0px; 43 | color: #fff; 44 | background-color: #33a; 45 | text-decoration: none; 46 | } 47 | span.menuseparator { 48 | width: 1em; 49 | padding: 0px; 50 | margin: 0px; 51 | } 52 | */ 53 | 54 | address { 55 | text-align: right; 56 | font-size: x-small; 57 | } 58 | 59 | h1 { 60 | font-size: 200%; 61 | text-align: left; 62 | color: white; 63 | background-color: #33a; 64 | padding: 0.4em; 65 | margin: 0.2em 0em 0.5em 0em; 66 | } 67 | 68 | img.sitelogo { 69 | vertical-align: middle; 70 | } 71 | 72 | h2 { 73 | font-size: 150%; 74 | text-align: left; 75 | border-left: 10px solid #33a; 76 | padding-left: 0.5em; 77 | margin-top: 1.5em; 78 | margin-bottom: 0.5em; 79 | } 80 | 81 | h3 { 82 | font-size: 150%; 83 | text-align: left; 84 | margin-top: 0.5em; 85 | margin-bottom: 0.5em; 86 | } 87 | 88 | /* tmp */ 89 | h4 { 90 | font-size: 150%; 91 | text-align: left; 92 | margin-top: 0.5em; 93 | margin-bottom: 0.5em; 94 | } 95 | 96 | /* never use */ 97 | h5, h6 { 98 | background-color: red; 99 | } 100 | 101 | p { 102 | line-height: 1.4; 103 | margin-top: 0.5em; 104 | margin-bottom: 0.5em; 105 | margin-left: 0px; 106 | } 107 | 108 | span.kindinfo { 109 | text-align: right; 110 | margin-left: 8em; 111 | } 112 | 113 | span.compileerror { 114 | color: red; 115 | font-weight: bold; 116 | } 117 | 118 | pre { 119 | line-height: 1.1; 120 | background-color: #eee; 121 | padding: 10px; 122 | font-weight: normal; 123 | } 124 | 125 | blockquote { 126 | background-color: transparent; 127 | padding: 4px; 128 | margin-left: 3em; 129 | font-weight: normal; 130 | } 131 | 132 | ul { 133 | margin-left: 0px; 134 | padding-left: 2em; 135 | } 136 | 137 | li { 138 | margin-left: 0px; 139 | padding-left: 0px; 140 | } 141 | 142 | dl { 143 | margin-left: 0em; 144 | } 145 | 146 | dt { 147 | font-weight: bold; 148 | } 149 | 150 | dl.methodlist dt { 151 | font-family: sans-serif; 152 | } 153 | 154 | dd { 155 | margin: 0.3em 0em 1em 4em; 156 | } 157 | 158 | table { 159 | border-collapse: collapse; 160 | } 161 | 162 | th { 163 | align: left; 164 | valign: top; 165 | text-align: left; 166 | vertical-align: top; 167 | } 168 | 169 | td { 170 | align: left; 171 | valign: top; 172 | text-align: left; 173 | vertical-align: top; 174 | } 175 | 176 | table.entries tr { 177 | /* border: 1px solid gray; */ 178 | align: left; 179 | valign: top; 180 | text-align: left; 181 | vertical-align: top; 182 | } 183 | 184 | td.signature { 185 | padding: 0.5em; 186 | background-color: #DDD; 187 | border: 3px solid white; 188 | width: 20em; 189 | } 190 | 191 | td.description { 192 | padding: 0.5em; 193 | background-color: #EEE; 194 | border: 3px solid white; 195 | } 196 | 197 | td.library { 198 | width: 6em; 199 | padding: 0.5em; 200 | background-color: #DDD; 201 | border: 3px solid white; 202 | } 203 | 204 | 205 | a { 206 | font-weight: bold; 207 | text-decoration: none; 208 | } 209 | a:link { 210 | color: #33a; 211 | background-color: transparent; 212 | } 213 | a:visited { 214 | /* color: #666666; */ 215 | color: #33a; 216 | background-color: transparent; 217 | } 218 | a:hover, a:focus, a:active { 219 | color: #fff; 220 | background-color: #33a; 221 | } 222 | 223 | form { 224 | padding: 0px; 225 | margin: 0px; 226 | } 227 | 228 | hr { 229 | color: #33a; 230 | height: 1px; 231 | } 232 | 233 | @media print { 234 | body { 235 | font-family: osaka,'MS Mincho',serif; 236 | line-height: 1.5; 237 | } 238 | div.menu { 239 | display: none; 240 | } 241 | div.footer { 242 | display: none; 243 | } 244 | h1 { 245 | color: black; 246 | background-color: white; 247 | border-bottom: 2px solid #33a; 248 | padding-bottom: 0em; 249 | } 250 | a, a:link, a:visited { 251 | color: #33a; 252 | font-weight: normal; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /lib/bitclust/methodid.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/methodid.rb 3 | # 4 | # Copyright (c) 2006-2007 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/compat' 11 | require 'bitclust/nameutils' 12 | require 'bitclust/exception' 13 | 14 | module BitClust 15 | 16 | # Represents infomation to identify a method (library, class/module, 17 | # type and name.) 18 | # 19 | # A MethodID has #library, #klass, #typename, and method name. 20 | # #library, #klass, #typename must be an object. 21 | class MethodID 22 | include NameUtils 23 | 24 | def initialize(library = nil, klass = nil, type = nil, name = nil) 25 | @library = library 26 | @klass = klass 27 | @type = type 28 | @name = name 29 | end 30 | 31 | attr_accessor :library 32 | attr_accessor :klass 33 | attr_accessor :type 34 | attr_accessor :name 35 | 36 | def inspect 37 | "#" 38 | end 39 | 40 | def match?(m) 41 | m.name == @name and 42 | m.type == @type and 43 | m.library == @library 44 | end 45 | 46 | alias typename type 47 | 48 | def typechar 49 | typename2char(@type) 50 | end 51 | 52 | def typemark 53 | typename2mark(@type) 54 | end 55 | 56 | def idstring 57 | build_method_id(@library.id, @klass.id, @type, @name) 58 | end 59 | end 60 | 61 | 62 | # A MethodSpec has #klass, #type, #method and #library. 63 | # All attributes are string. 64 | # #library is optional. 65 | class MethodSpec 66 | 67 | def MethodSpec.parse(str) 68 | new(*NameUtils.split_method_spec(str)) 69 | end 70 | 71 | def initialize(c, t, m, library = nil) 72 | @klass = c 73 | @type = t 74 | @method = m 75 | @library = library 76 | end 77 | 78 | attr_reader :klass 79 | attr_reader :type 80 | attr_reader :method 81 | attr_reader :library 82 | 83 | def inspect 84 | "#" 85 | end 86 | 87 | def to_s 88 | "#{@klass}#{@type}#{@method}" 89 | end 90 | 91 | def display_name 92 | @type == '$' ? "$#{@method}" : to_s() 93 | end 94 | 95 | def ==(other) 96 | @klass == other.klass and 97 | @type == other.type and 98 | @method == other.method 99 | end 100 | 101 | alias eql? == 102 | 103 | def hash 104 | to_s().hash 105 | end 106 | 107 | def <=>(other) 108 | [@klass, @type, @method] <=> [other.klass, other.type, other.method] 109 | end 110 | 111 | def match?(m) 112 | (not @type or @type == m.typemark) and 113 | (not @method or m.name?(@method)) 114 | end 115 | 116 | def singleton_method? 117 | @type == '.' or @type == '.#' 118 | end 119 | 120 | def instance_method? 121 | @type == '#' or @type == '.#' 122 | end 123 | 124 | def module_function? 125 | @type == '.#' 126 | end 127 | 128 | def method? 129 | singleton_method? or instance_method? 130 | end 131 | 132 | def constant? 133 | @type == '::' 134 | end 135 | 136 | def special_variable? 137 | @type == '$' 138 | end 139 | 140 | end 141 | 142 | 143 | # A MethodNamePattern has #klass, #type, #method and #library. 144 | # All attributes are string. 145 | # All attributes are optional. 146 | class MethodNamePattern 147 | 148 | def initialize(c = nil, t = nil, m = nil, lib = nil) 149 | @klass = c 150 | if c and c.empty? 151 | @klass = nil 152 | end 153 | @type = t 154 | @method = m 155 | if m and m.empty? 156 | @method = nil 157 | end 158 | @library = library 159 | @crecache = [] 160 | @mrecache = [] 161 | end 162 | 163 | attr_reader :klass 164 | attr_reader :type 165 | attr_reader :method 166 | attr_reader :library 167 | 168 | def inspect 169 | "#" 170 | end 171 | 172 | def esc(s) 173 | s || '_' 174 | end 175 | private :esc 176 | 177 | def tesc(s) 178 | s || ' _ ' 179 | end 180 | private :tesc 181 | 182 | def match?(m) 183 | (not @library or m.library.name?(@library)) and 184 | (not @klass or m.klass.name?(@klass)) and 185 | (not @type or m.typemark == @type) and 186 | (not @method or m.name?(@method)) 187 | end 188 | 189 | def select_classes(cs) 190 | return cs unless @klass 191 | expand_ic(cs, @klass, @crecache) 192 | end 193 | 194 | def empty? 195 | not @klass and not @type and not @method 196 | end 197 | 198 | def class? 199 | @klass and (not @type and not @method) 200 | end 201 | 202 | def method? 203 | @method or (@type and @type != '$') 204 | end 205 | 206 | def special_variable? 207 | @type == '$' 208 | end 209 | 210 | end 211 | 212 | end 213 | -------------------------------------------------------------------------------- /lib/bitclust/subcommands/setup_command.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'erb' 3 | require 'find' 4 | require 'pp' 5 | require 'optparse' 6 | require 'yaml' 7 | 8 | require 'bitclust' 9 | require 'bitclust/subcommand' 10 | 11 | module BitClust 12 | module Subcommands 13 | class SetupCommand < Subcommand 14 | 15 | REPOSITORY_PATH = "https://github.com/rurema/doctree.git" 16 | 17 | def initialize 18 | super 19 | @prepare = nil 20 | @cleanup = nil 21 | @purge = nil 22 | @versions = ["2.0.0", "2.1.0", "2.2.0"] 23 | @parser.banner = "Usage: #{File.basename($0, '.*')} setup [options]" 24 | @parser.on('--prepare', 'Prepare config file and checkout repository. Do not create database.') { 25 | @prepare = true 26 | } 27 | @parser.on('--cleanup', 'Cleanup datebase before create database.') { 28 | @cleanup = true 29 | } 30 | @parser.on('--purge', 'Purge all downloaded and generated files and exit.') { 31 | @purge = true 32 | } 33 | @parser.on('--versions=V1,V2,...', "Specify versions. [#{@versions.join(',')}]") {|versions| 34 | @versions = versions.split(",") 35 | } 36 | end 37 | 38 | def exec(argv, options) 39 | purge if @purge 40 | prepare 41 | return if @prepare 42 | @config[:versions].each do |version| 43 | puts "Generating database for Ruby#{version}..." 44 | prefix = "#{@config[:database_prefix]}-#{version}" 45 | FileUtils.rm_rf(prefix) if @cleanup 46 | init_argv = ["version=#{version}", "encoding=#{@config[:encoding]}"] 47 | init_options = { :prefix => prefix } 48 | InitCommand.new.exec(init_argv, init_options) 49 | update_method_database(prefix, ["--stdlibtree=#{@config[:stdlibtree]}"]) 50 | update_argv = Pathname(@config[:capi_src]).children.select(&:file?).map{|v| v.realpath.to_s } 51 | update_function_database(prefix, update_argv) 52 | end 53 | end 54 | 55 | private 56 | 57 | def purge 58 | home_directory = Pathname(ENV["HOME"]) 59 | config_dir = home_directory + ".bitclust" 60 | print "Remove all generated files..." 61 | FileUtils.rm_rf(config_dir.to_s) 62 | puts "done!" 63 | exit 0 64 | end 65 | 66 | def prepare 67 | home_directory = Pathname(ENV["HOME"]).expand_path 68 | config_dir = home_directory + ".bitclust" 69 | config_dir.mkpath 70 | config_path = config_dir + "config" 71 | rubydoc_dir = config_dir + "rubydoc" 72 | @config = { 73 | :database_prefix => (config_dir + "db").to_s, 74 | :encoding => "utf-8", 75 | :versions => @versions, 76 | :default_version => @versions.max, 77 | :stdlibtree => (rubydoc_dir + "refm/api/src").to_s, 78 | :capi_src => (rubydoc_dir + "refm/capi/src/").to_s, 79 | :baseurl => "http://localhost:10080", 80 | :port => "10080", 81 | :pid_file => "/tmp/bitclust.pid", 82 | } 83 | if config_path.exist? 84 | @config = YAML.load_file(config_path) 85 | unless @config[:versions].sort == @versions.sort 86 | print("overwrite config file? > [y/N]") 87 | if /\Ay\z/i =~ $stdin.gets.chomp 88 | @config[:versions] = @versions 89 | @config[:default_version] = @versions.max 90 | generate_config(config_path, @config) 91 | end 92 | end 93 | else 94 | generate_config(config_path, @config) 95 | end 96 | checkout(rubydoc_dir) 97 | end 98 | 99 | def generate_config(path, config) 100 | path.open("w+", 0644) do |file| 101 | file.puts config.to_yaml 102 | end 103 | end 104 | 105 | def checkout(rubydoc_dir) 106 | if (rubydoc_dir + ".svn").exist? 107 | warn "Remove old repository data." 108 | warn "Use --purge option." 109 | help 110 | exit 1 111 | end 112 | 113 | succeeded = false 114 | if (rubydoc_dir + ".git").exist? 115 | Dir.chdir(rubydoc_dir) do 116 | succeeded = system("git", "pull", "--rebase") 117 | end 118 | else 119 | succeeded = system("git", "clone", "--depth", "10", REPOSITORY_PATH, rubydoc_dir.to_s) 120 | end 121 | 122 | unless succeeded 123 | warn "git command failed. Please install Git or check your PATH." 124 | exit 1 125 | end 126 | end 127 | 128 | def update_method_database(prefix, argv) 129 | options = { 130 | :prefix => prefix, 131 | :capi => false, 132 | } 133 | cmd = UpdateCommand.new 134 | cmd.parse(argv) 135 | cmd.exec(argv, options) 136 | end 137 | 138 | def update_function_database(prefix, argv) 139 | options = { 140 | :prefix => prefix, 141 | :capi => true, 142 | } 143 | cmd = UpdateCommand.new 144 | cmd.parse(argv) 145 | cmd.exec(argv, options) 146 | end 147 | end 148 | end 149 | end 150 | -------------------------------------------------------------------------------- /lib/bitclust/libraryentry.rb: -------------------------------------------------------------------------------- 1 | # 2 | # bitclust/libraryentry.rb 3 | # 4 | # Copyright (c) 2006-2008 Minero Aoki 5 | # 6 | # This program is free software. 7 | # You can distribute/modify this program under the Ruby License. 8 | # 9 | 10 | require 'bitclust/entry' 11 | require 'bitclust/exception' 12 | 13 | module BitClust 14 | 15 | # Entry for libraries ("_builtin", "yaml", etc.) 16 | class LibraryEntry < Entry 17 | 18 | include Enumerable 19 | 20 | def LibraryEntry.type_id 21 | :library 22 | end 23 | 24 | def initialize(db, id) 25 | super db 26 | @id = id 27 | @name = libid2name(@id) 28 | if saved? 29 | @classmap = nil 30 | @methodmap = nil 31 | @link_checked = true 32 | else 33 | @classmap = {} 34 | @methodmap = {} 35 | @link_checked = false 36 | end 37 | init_properties 38 | end 39 | 40 | attr_reader :id, :name 41 | 42 | alias label name 43 | 44 | def ==(other) 45 | @id == other.id 46 | end 47 | 48 | alias eql? == 49 | 50 | def hash 51 | @id.hash 52 | end 53 | 54 | def <=>(other) 55 | @id.casecmp(other.id) 56 | end 57 | 58 | def labels 59 | [label()] 60 | end 61 | 62 | def name?(n) 63 | name() == n 64 | end 65 | 66 | persistent_properties { 67 | property :requires, '[LibraryEntry]' 68 | property :classes, '[ClassEntry]' # :defined classes 69 | property :methods, '[MethodEntry]' # :added/:redefined entries 70 | property :source, 'String' 71 | property :sublibraries, '[LibraryEntry]' 72 | property :is_sublibrary, 'bool' 73 | property :category, 'String' 74 | } 75 | 76 | def inspect 77 | "#" 78 | end 79 | 80 | def check_link(path = []) 81 | return if @link_checked 82 | if path.include?(name()) 83 | raise InvalidLink, "looped require: #{path_string(path)}" 84 | end 85 | path.push name() 86 | requires().each do |lib| 87 | lib.check_link path 88 | end 89 | path.pop 90 | @link_checked = true 91 | end 92 | 93 | def all_requires(libs = {}) 94 | requires.each{|l| 95 | next if libs[l.name] 96 | libs[l.name] = l 97 | l.all_requires(libs) 98 | } 99 | libs.values 100 | end 101 | 102 | def all_classes 103 | return @all_classes if @all_classes 104 | required_classes = (sublibraries & requires).map{|l| l.classes }.flatten 105 | @all_classes = (classes() + required_classes).uniq.sort 106 | end 107 | 108 | def error_classes 109 | @error_classes ||= classes.select{|c| c.error_class? } 110 | end 111 | 112 | def all_error_classes 113 | @all_error_classes ||= all_classes.select{|c| c.error_class? } 114 | end 115 | 116 | def all_modules 117 | @all_modules ||= all_classes.select{|c| c.module? }.sort 118 | end 119 | 120 | def all_objects 121 | @all_objects ||= all_classes.select{|c| c.object? }.sort 122 | end 123 | 124 | def require(lib) 125 | requires().push lib 126 | end 127 | 128 | def sublibrary(lib) 129 | unless sublibraries().include?(lib) 130 | sublibraries().push lib 131 | lib.is_sublibrary = true 132 | end 133 | end 134 | 135 | def fetch_class(name) 136 | get_class(name) or 137 | raise ClassNotFound, "no such class in the library #{name()}: #{name}" 138 | end 139 | 140 | def get_class(name) 141 | classes().detect {|c| c.name == name } 142 | end 143 | 144 | def classnames 145 | classes().map {|c| c.name } 146 | end 147 | 148 | def each_class(&block) 149 | classes().each(&block) 150 | end 151 | 152 | def classmap 153 | @classmap ||= 154 | begin 155 | h = {} 156 | classes().each do |c| 157 | h[c.name] = c 158 | end 159 | h 160 | end 161 | end 162 | private :classmap 163 | 164 | def fetch_methods(spec) 165 | ms = if c = get_class(spec.klass) 166 | then c.fetch_methods(spec) 167 | else [] 168 | end + 169 | methods().select {|m| spec.match?(m) } 170 | if ms.empty? 171 | raise MethodNotFound, "no such method in the library #{name()}: #{name}" 172 | end 173 | ms 174 | end 175 | 176 | def fetch_method(spec) 177 | classes().each do |c| 178 | m = c.get_method(spec) 179 | return m if m 180 | end 181 | methods().detect {|m| spec.match?(m) } or 182 | raise MethodNotFound, "no such method in the library #{name()}: #{name}" 183 | end 184 | 185 | def each_method(&block) 186 | methods().each(&block) 187 | end 188 | 189 | def methodmap 190 | @methodmap ||= 191 | begin 192 | h = {} 193 | methods().each do |m| 194 | h[m] = m 195 | end 196 | h 197 | end 198 | end 199 | private :methodmap 200 | 201 | def add_class(c) 202 | unless classmap()[c.name] 203 | classes().push c 204 | classmap()[c.name] = c 205 | @db.dirty_library self 206 | end 207 | end 208 | 209 | def add_method(m) 210 | unless methodmap()[m] 211 | methods().push m 212 | methodmap()[m] = m 213 | @db.dirty_library self 214 | end 215 | end 216 | 217 | end 218 | 219 | end 220 | -------------------------------------------------------------------------------- /lib/bitclust/runner.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'optparse' 3 | 4 | unless Object.const_defined?(:Encoding) 5 | $KCODE = 'UTF-8' 6 | end 7 | 8 | def libdir 9 | Pathname.new(__FILE__).realpath.dirname.parent.cleanpath 10 | end 11 | 12 | $LOAD_PATH.unshift(libdir.to_s) 13 | 14 | require 'bitclust' 15 | require 'bitclust/subcommand' 16 | 17 | subcommands_dir = libdir + "bitclust/subcommands" 18 | Dir.glob(File.join(subcommands_dir.to_s, "*.rb")) do |entry| 19 | require "bitclust/subcommands/#{File.basename(entry, ".rb")}" 20 | end 21 | 22 | module BitClust 23 | # Body of bin/bitclust. 24 | class Runner 25 | def initialize 26 | end 27 | 28 | def run(argv) 29 | Signal.trap(:PIPE, 'IGNORE') rescue nil # Win32 does not have SIGPIPE 30 | Signal.trap(:INT) { exit 3 } 31 | prepare 32 | _run(argv) 33 | rescue Errno::EPIPE 34 | exit 0 35 | end 36 | 37 | def prepare 38 | @prefix = nil 39 | @version = nil 40 | @capi = false 41 | @parser = OptionParser.new 42 | @parser.banner = <<-EndBanner 43 | Usage: #{File.basename($0, '.*')} [global options] [options] [args] 44 | 45 | Subcommands(for users): 46 | init Initialize database. 47 | update Update database. 48 | setup Initialize and update database with default options. 49 | list List libraries/classes/methods in database. 50 | lookup Lookup a library/class/method from database. 51 | search Search classes/methods from database. 52 | 53 | Subcommands(for developers): 54 | ancestors Compare class/module's ancestors between Ruby and DB. 55 | htmlfile Generate a static HTML file for test. 56 | query Dispatch arbitrary query. 57 | property Handle database properties. 58 | preproc Preprocess source file. 59 | extract Extract method entries from source file. 60 | classes Display defined classes for all ruby. 61 | methods Display defined methods for all ruby. 62 | 63 | Subcommands(for packagers): 64 | statichtml Generate static HTML files. 65 | epub Generate EPUB file. 66 | chm Generate static HTML files for CHM. 67 | 68 | Global Options: 69 | EndBanner 70 | @parser.on('-d', '--database=PATH', 'Database prefix.') {|path| 71 | @prefix = path 72 | } 73 | @parser.on('-t', '--target=VERSION', 'Specify Ruby version.') {|v| 74 | @version = v 75 | } 76 | @parser.on('--capi', 'Process C API database.') { 77 | @capi = true 78 | } 79 | @parser.on('--version', 'Print version and quit.') { 80 | puts BitClust::VERSION 81 | exit 0 82 | } 83 | @parser.on('--help', 'Prints this message and quit.') { 84 | puts @parser.help 85 | exit 0 86 | } 87 | @subcommands = { 88 | 'init' => BitClust::Subcommands::InitCommand.new, 89 | 'list' => BitClust::Subcommands::ListCommand.new, 90 | 'lookup' => BitClust::Subcommands::LookupCommand.new, 91 | 'search' => BitClust::Searcher.new, 92 | 'query' => BitClust::Subcommands::QueryCommand.new, 93 | 'update' => BitClust::Subcommands::UpdateCommand.new, 94 | 'property' => BitClust::Subcommands::PropertyCommand.new, 95 | 'setup' => BitClust::Subcommands::SetupCommand.new, 96 | 'server' => BitClust::Subcommands::ServerCommand.new, 97 | 'statichtml' => BitClust::Subcommands::StatichtmlCommand.new, 98 | 'htmlfile' => BitClust::Subcommands::HtmlfileCommand.new, 99 | 'chm' => BitClust::Subcommands::ChmCommand.new, 100 | 'epub' => BitClust::Subcommands::EPUBCommand.new, 101 | 'ancestors' => BitClust::Subcommands::AncestorsCommand.new, 102 | 'preproc' => BitClust::Subcommands::PreprocCommand.new, 103 | 'extract' => BitClust::Subcommands::ExtractCommand.new, 104 | 'classes' => BitClust::Subcommands::ClassesCommand.new, 105 | 'methods' => BitClust::Subcommands::MethodsCommand.new, 106 | } 107 | end 108 | 109 | def _run(argv) 110 | begin 111 | @parser.order!(argv) 112 | if argv.empty? 113 | $stderr.puts 'no sub-command given' 114 | $stderr.puts @parser.help 115 | exit 1 116 | end 117 | name = argv.shift 118 | cmd = @subcommands[name] or error "no such sub-command: #{name}" 119 | rescue OptionParser::ParseError => err 120 | $stderr.puts err.message 121 | $stderr.puts @parser.help 122 | exit 1 123 | end 124 | begin 125 | cmd.parse(argv) 126 | rescue OptionParser::ParseError => err 127 | $stderr.puts err.message 128 | $stderr.puts cmd.help 129 | exit 1 130 | end 131 | config = load_config() 132 | if config 133 | @version ||= config[:default_version] 134 | @prefix ||= "#{config[:database_prefix]}-#{@version}" 135 | end 136 | options = { 137 | :prefix => @prefix, 138 | :capi => @capi 139 | } 140 | cmd.exec(argv, options) 141 | rescue BitClust::WriterError => err 142 | raise if $DEBUG 143 | error err.message 144 | end 145 | 146 | def load_config 147 | home_directory = Pathname(ENV['HOME']) 148 | config_path = home_directory + ".bitclust/config" 149 | if config_path.exist? 150 | YAML.load_file(config_path) 151 | else 152 | nil 153 | end 154 | end 155 | 156 | def error(message) 157 | $stderr.puts "#{File.basename($0, '.*')}: error: #{message}" 158 | exit 1 159 | end 160 | end 161 | end 162 | -------------------------------------------------------------------------------- /theme/default/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | default.css 3 | */ 4 | 5 | body { 6 | background-color: white; 7 | color: black; 8 | font-family: Meiryo, "Hiragino Kaku Gothic Pro", "MS Gothic", Osaka, sans-serif; 9 | text-align: left; 10 | margin-top: 20px; 11 | margin-left: 2%; 12 | margin-right: 2%; 13 | line-height: 1.3; 14 | } 15 | 16 | /* 17 | span.menuitem { 18 | width: 3.9em; 19 | text-align: center; 20 | padding: 0px 2px; 21 | margin: 0px; 22 | color: white; 23 | background-color: #ddd; 24 | margin-right: 2px; 25 | } 26 | div.menu a:link, 27 | div.menu a:visited { 28 | width: 3.9em; 29 | padding: 0px 2px; 30 | margin: 0px; 31 | color: #33a; 32 | background-color: #ddd; 33 | text-decoration: none; 34 | } 35 | div.menu a:active, 36 | div.menu a:focus, 37 | div.menu a:hover { 38 | width: 3.9em; 39 | padding: 0px 2px; 40 | margin: 0px; 41 | color: #fff; 42 | background-color: #33a; 43 | text-decoration: none; 44 | } 45 | span.menuseparator { 46 | width: 1em; 47 | padding: 0px; 48 | margin: 0px; 49 | } 50 | */ 51 | 52 | address { 53 | text-align: right; 54 | font-size: x-small; 55 | } 56 | 57 | h1 { 58 | font-size: 200%; 59 | text-align: left; 60 | color: white; 61 | background-color: #33a; 62 | padding: 0.4em; 63 | margin: 0.2em 0em 0.5em 0em; 64 | } 65 | 66 | img.sitelogo { 67 | vertical-align: middle; 68 | } 69 | 70 | h2 { 71 | font-size: 150%; 72 | text-align: left; 73 | border-left: 10px solid #33a; 74 | padding-left: 0.5em; 75 | margin-top: 1.5em; 76 | margin-bottom: 0.5em; 77 | } 78 | 79 | h3 { 80 | font-size: 150%; 81 | text-align: left; 82 | margin-top: 0.5em; 83 | margin-bottom: 0.5em; 84 | } 85 | 86 | /* tmp */ 87 | h4 { 88 | font-size: 120%; 89 | text-align: left; 90 | margin-top: 0.5em; 91 | margin-bottom: 0.5em; 92 | } 93 | 94 | /* never use */ 95 | h5, h6 { 96 | background-color: red; 97 | } 98 | 99 | p { 100 | line-height: 1.4; 101 | margin-top: 0.5em; 102 | margin-bottom: 0.5em; 103 | margin-left: 0px; 104 | } 105 | 106 | span.kindinfo { 107 | text-align: right; 108 | margin-left: 8em; 109 | } 110 | 111 | span.compileerror { 112 | color: red; 113 | font-weight: bold; 114 | } 115 | 116 | pre { 117 | line-height: 1.1; 118 | background-color: #eee; 119 | padding: 10px; 120 | font-weight: normal; 121 | } 122 | 123 | blockquote { 124 | background-color: transparent; 125 | padding: 4px; 126 | margin-left: 3em; 127 | font-weight: normal; 128 | } 129 | 130 | ul { 131 | margin-left: 0px; 132 | padding-left: 2em; 133 | } 134 | 135 | li { 136 | margin-left: 0px; 137 | padding-left: 0px; 138 | } 139 | 140 | ol li { 141 | margin-bottom: 10px; 142 | } 143 | 144 | dl { 145 | margin-left: 0em; 146 | } 147 | 148 | dt { 149 | font-weight: bold; 150 | } 151 | 152 | dl.methodlist dt { 153 | font-family: sans-serif; 154 | } 155 | 156 | dd { 157 | margin: 0.3em 0em 1em 4em; 158 | } 159 | 160 | table { 161 | border-collapse: collapse; 162 | } 163 | 164 | th { 165 | text-align: left; 166 | vertical-align: top; 167 | background-color: #AAC; 168 | border: 3px solid white; 169 | padding: 0.3em; 170 | } 171 | 172 | td { 173 | text-align: left; 174 | vertical-align: top; 175 | } 176 | 177 | table.entries { 178 | width: 100%; 179 | } 180 | 181 | table.entries tr { 182 | /* border: 1px solid gray; */ 183 | text-align: left; 184 | vertical-align: top; 185 | } 186 | 187 | td.signature { 188 | padding: 0; 189 | margin: 0; 190 | background-color: #DDD; 191 | border: 3px solid white; 192 | width: 30%; 193 | height: 100%; 194 | } 195 | 196 | td.signature a { 197 | display: block; 198 | padding: 0.3em; 199 | } 200 | 201 | td.description { 202 | padding: 0.3em; 203 | background-color: #EEE; 204 | border: 3px solid white; 205 | } 206 | 207 | td.library { 208 | width: 6em; 209 | padding: 0.5em; 210 | background-color: #DDD; 211 | border: 3px solid white; 212 | } 213 | 214 | code { 215 | font-family: monospace; 216 | } 217 | 218 | a { 219 | font-weight: bold; 220 | text-decoration: none; 221 | } 222 | a:link { 223 | color: #33a; 224 | background-color: transparent; 225 | } 226 | a:visited { 227 | /* color: #666666; */ 228 | color: #33a; 229 | background-color: transparent; 230 | } 231 | a:hover, a:focus, a:active { 232 | color: #fff; 233 | background-color: #33a; 234 | } 235 | 236 | a.external { 237 | background: transparent url(images/external.png) no-repeat scroll right center; 238 | padding: 0 27px 0 0; 239 | } 240 | 241 | a.external:visited { 242 | /* color: #666666; */ 243 | color: #33a; 244 | background-color: transparent; 245 | } 246 | a.external:hover, a.external:focus, a.external:active { 247 | color: #fff; 248 | background-color: #33a; 249 | } 250 | 251 | span.permalink { 252 | float: right; 253 | font-weight: normal; 254 | } 255 | 256 | form { 257 | padding: 0px; 258 | margin: 0px; 259 | } 260 | 261 | hr { 262 | color: #33a; 263 | height: 1px; 264 | } 265 | 266 | @media print { 267 | body { 268 | font-family: osaka,'MS Mincho',serif; 269 | line-height: 1.5; 270 | } 271 | div.menu { 272 | display: none; 273 | } 274 | div.footer { 275 | display: none; 276 | } 277 | h1 { 278 | color: black; 279 | background-color: white; 280 | border-bottom: 2px solid #33a; 281 | padding-bottom: 0em; 282 | } 283 | a, a:link, a:visited { 284 | color: #33a; 285 | font-weight: normal; 286 | } 287 | } 288 | 289 | #top_search { 290 | position: absolute; 291 | top: 15px; 292 | right: 10px; 293 | } 294 | --------------------------------------------------------------------------------