# # bitclust/rdcompiler.rb # # Copyright (C) 2006-2008 Minero Aoki # # This program is free software. # You can distribute/modify this program under the Ruby License. # require 'bitclust/methodsignature' require 'bitclust/lineinput' require 'bitclust/htmlutils' require 'bitclust/textutils' require 'bitclust/messagecatalog' require 'bitclust/syntax_highlighter' require 'stringio' module BitClust # Compiles doc into HTML. class RDCompiler include HTMLUtils include TextUtils include Translatable def initialize(urlmapper, hlevel = 1, opt = {}) @urlmapper = urlmapper @catalog = opt[:catalog] @hlevel = hlevel @type = nil @library = nil @class = nil @method = nil @option = opt.dup init_message_catalog(@catalog) end def compile(src) setup(src) { library_file } end def compile_function(f, opt = nil) @opt = opt @type = :function setup(f.source, f) { entry } ensure @opt = nil end # FIXME def compile_method(m, opt = nil) @opt = opt @type = :method @method = m setup(m.source, m) { entry } ensure @opt = nil end private def setup(src, entry = nil) @f = LineInput.new(StringIO.new(src), entry) @out = StringIO.new yield @out.string end def library_file while @f.next? case @f.peek when /\A---/ entry_chunk when /\A=+/ headline @f.gets when /\A(\s+)\*\s/, /\A(\s+)\(\d+\)\s/ @item_stack = [] item_list($1.size) raise "@item_stack should be empty. #{@item_stack.inspect}" unless @item_stack.empty? when %r<\A//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{> emlist when /\A:\s/ dlist when /\A\s+\S/ list else if @f.peek.strip.empty? @f.gets else paragraph end end end end def entry while @f.next? entry_chunk end end def entry_chunk @out.puts '
' line compile_text(text_node_from_lines(@f.span(/\A[ \t]/))) line '
' when %r!\A//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{! emlist else raise 'must not happen' end end line '"
line ""
src = ""
@f.until_terminator(%r<\A//\}>) do |line|
src << line
end
if lang == "ruby"
begin
filename = (caption&.size || 0) > 2 ? caption : @f.name
string BitClust::SyntaxHighlighter.new(src, filename).highlight
rescue BitClust::SyntaxHighlighter::Error => ex
$stderr.puts ex.message
if stop_on_syntax_error?
exit(false)
else
string src
end
end
else
string src
end
line '
'
else
line ' " if caption
line "' @f.until_terminator(%r<\A//\}>) do |line| line escape_html(line.rstrip) end line '' end end def list lines = unindent_block(canonicalize(@f.break(/\A\S/))) while lines.last.empty? lines.pop end line '
' lines.each do |line| line escape_html(line) end line '' end def canonicalize(lines) lines.map {|line| detab(line.rstrip) } end def paragraph line '
' line compile_text(text_node_from_lines(read_paragraph(@f))) line '
' end def read_paragraph(f) f.span(%r<\A(?!---|=|//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{)\S>) end def see header = @f.gets header.slice!(/\A\@\w+/) body = [header] + @f.span(/\A\s+\S/) line '' line '[SEE_ALSO] ' + compile_text(text_node_from_lines(body)) line '
' end def todo header = @f.gets header.slice!(/\A\@\w+/) body = header line '' line '[TODO]' + body line '
' end def entry_info line '' line compile_text(text_node_from_lines(read_entry_paragraph(@f))) line '
' end def read_entry_paragraph(f) f.span(%r<\A(?!---|=|//emlist(?:\[(?:[^\[\]]+?)?\]\[\w+?\])?\{|@[a-z])\S>) end def method_signature(sig_line, first) # FIXME: check parameters, types, etc. sig = MethodSignature.parse(sig_line) string %Q('
string @method.klass.name + @method.typemark if @opt
string escape_html(sig.friendly_string)
string '
'
if first
string '['
string a_href(@urlmapper.method_url(methodid2specstring(@method.id)), "permalink")
string ']['
string rdoc_link(@method.id, @option[:database].properties["version"])
string ']'
end
if @method and not @method.defined?
line %Q( [#{@method.kind} by #{library_link(@method.library.name)}])
end
line '