C0 code coverage information
Generated on Tue Aug 12 00:32:09 +0900 2008 with
rcov 0.8.1.2
Code reported as executed by Ruby looks like
this... and this: this line is also marked as
covered. Lines considered as run by rcov, but
not reported by Ruby, look like this, and
this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not
executed.
Name | Total lines | Lines of code |
Total coverage | Code coverage
|
lib/hikidoc.rb | 902 | 721
| | |
1 #
Copyright (c) 2005, Kazuhiko <kazuhiko@fdiary.net> 2 # Copyright (c) 2007 Minero Aoki
3 # All rights reserved.
4 # 5 # Redistribution and use in source
and binary forms, with or without 6 # modification, are permitted provided that the
following conditions are
7 # met: 8 #
9 # * Redistributions of
source code must retain the above copyright 10 # notice, this list of conditions
and the following disclaimer. 11 # * Redistributions in binary form must reproduce the
above 12 # copyright
notice, this list of conditions and the following 13 # disclaimer in the documentation
and/or other materials provided 14 # with the distribution. 15 # * Neither the name of the
HikiDoc nor the names of its 16 # contributors may be used to endorse or promote
products derived 17 #
from this software without specific prior written permission. 18 # 19 # THIS SOFTWARE IS PROVIDED BY
THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 # "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT 21 # LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR 22 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT 23
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 # SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 # LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY 27 #
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 # (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 # OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31
require "stringio" 32 require "strscan" 33 require "uri"
34 begin 35 require
"syntax/convertors/html" 36 rescue LoadError 37 end 38 39
class HikiDoc 40 VERSION
= "0.0.2" # FIXME 41 42
class Error < StandardError 43 end 44 45
class UnexpectedError < Error 46 end 47 48
def HikiDoc.to_html(src, options = {}) 49 new(HTMLOutput.new(">"),
options).compile(src)
50 end 51 52 def HikiDoc.to_xhtml(src, options =
{}) 53
new(HTMLOutput.new(" />"), options).compile(src) 54 end 55 56 def initialize(output, options = {}) 57 @output = output 58 @options =
default_options.merge(options) 59 @header_re = nil 60 @level = options[:level] || 1 61 @plugin_syntax =
options[:plugin_syntax] || method(:valid_plugin_syntax?) 62 end 63 64 def compile(src) 65 @output.reset 66 escape_plugin_blocks(src) {|escaped| 67 compile_blocks escaped 68 @output.finish 69 } 70 end 71 72 # for backward compatibility 73 def to_html 74 $stderr.puts("warning:
HikiDoc#to_html is deprecated. Please use HikiDoc.to_html or
HikiDoc.to_xhtml instead.") 75 self.class.to_html(@output, @options) 76 end 77 78 private 79 80
def default_options 81
{ 82
:allow_bracket_inline_image => true, 83 :use_wiki_name => true, 84 :use_not_wiki_name => true,
85 } 86 end 87 88 #
89 # Plugin 90 #
91 92 def valid_plugin_syntax?(code)
93 /['"]/ !~
code.gsub(/'(?:[^\\']+|\\.)*'|"(?:[^\\"]+|\\.)*"/m,
"") 94 end
95 96 def escape_plugin_blocks(text)
97 s =
StringScanner.new(text)
98 buf = "" 99
@plugin_blocks = [] 100
while chunk = s.scan_until(/\{\{/) 101 tail = chunk[-2, 2] 102 chunk[-2, 2] = "" 103 buf << chunk 104 # plugin 105 if block = extract_plugin_block(s)
106 @plugin_blocks.push
block 107 buf <<
"\0#{@plugin_blocks.size - 1}\0" 108 else 109 buf << "{{" 110 end 111 end 112 buf << s.rest 113 yield(buf) 114 end 115 116 def restore_plugin_block(str) 117 str.gsub(/\0(\d+)\0/) {
118 "{{" +
plugin_block($1.to_i) + "}}" 119 } 120 end 121 122 def evaluate_plugin_block(str, buf = nil)
123 buf ||=
@output.container 124
str.split(/(\0\d+\0)/).each do |s| 125 if s[0, 1] == "\0" and s[-1, 1] ==
"\0" 126 buf
<< @output.inline_plugin(plugin_block(s[1..-2].to_i)) 127 else 128 buf << @output.text(s)
129 end 130 end 131 buf 132 end 133 134 def plugin_block(id) 135 @plugin_blocks[id] or raise UnexpectedError,
"must not happen: #{id.inspect}" 136 end 137 138 def extract_plugin_block(s) 139 pos = s.pos 140 buf = "" 141 while chunk = s.scan_until(/\}\}/)
142 buf << chunk
143
buf.chomp!("}}") 144 if @plugin_syntax.call(buf) 145 return buf 146 end 147 buf << "}}"
148 end 149 s.pos = pos 150 nil 151 end 152 153 # 154 # Block Level 155 # 156 157 def compile_blocks(src) 158 f =
LineInput.new(StringIO.new(src)) 159 while line = f.peek 160 case line 161 when COMMENT_RE 162 f.gets 163 when HEADER_RE 164 compile_header f.gets 165 when HRULE_RE 166 f.gets 167 compile_hrule 168 when LIST_RE 169 compile_list f 170 when DLIST_RE 171 compile_dlist f 172 when TABLE_RE 173 compile_table f 174 when BLOCKQUOTE_RE 175 compile_blockquote f 176 when INDENTED_PRE_RE 177 compile_indented_pre f 178 when BLOCK_PRE_OPEN_RE
179 compile_block_pre f
180 else 181 if /^$/ =~ line 182 f.gets 183 next 184 end 185 compile_paragraph f 186 end 187 end 188 end 189 190 COMMENT_RE = %r<\A//> 191 192 def skip_comments(f) 193 f.while_match(COMMENT_RE) do |line| 194 end 195 end 196 197 HEADER_RE = /\A!+/ 198 199 def compile_header(line) 200 @header_re ||= /\A!{1,#{7 -
@level}}/ 201 level =
@level + (line.slice!(@header_re).size - 1) 202 title = strip(line) 203 @output.headline level, compile_inline(title)
204 end 205 206 HRULE_RE = /\A----$/ 207 208 def compile_hrule 209 @output.hrule 210 end 211 212 ULIST = "*" 213 OLIST = "#" 214 LIST_RE = /\A#{Regexp.union(ULIST, OLIST)}+/
215 216 def compile_list(f) 217 typestack = [] 218 level = 0 219 @output.list_begin 220 f.while_match(LIST_RE) do |line|
221 list_type =
(line[0,1] == ULIST ? "ul" : "ol") 222 new_level =
line.slice(LIST_RE).size 223 item = strip(line.sub(LIST_RE, ""))
224 if new_level >
level 225 (new_level -
level).times do 226
typestack.push list_type 227 @output.list_open list_type 228 @output.listitem_open 229 end 230 @output.listitem
compile_inline(item) 231
elsif new_level < level 232 (level - new_level).times do 233 @output.listitem_close
234 @output.list_close
typestack.pop 235 end
236
@output.listitem_close 237 @output.listitem_open 238 @output.listitem compile_inline(item) 239 elsif list_type == typestack.last
240
@output.listitem_close 241 @output.listitem_open 242 @output.listitem compile_inline(item) 243 else 244 @output.listitem_close
245 @output.list_close
typestack.pop 246
@output.list_open list_type 247 @output.listitem_open 248 @output.listitem compile_inline(item) 249 typestack.push list_type
250 end 251 level = new_level 252 skip_comments f 253 end 254 level.times do 255 @output.listitem_close
256 @output.list_close
typestack.pop 257 end
258 @output.list_end
259 end 260 261 DLIST_RE = /\A:/ 262 263 def compile_dlist(f) 264 @output.dlist_open 265 f.while_match(DLIST_RE) do |line| 266 dt, dd =
split_dlitem(line.sub(DLIST_RE, "")) 267 @output.dlist_item
compile_inline(dt), compile_inline(dd) 268 skip_comments f 269 end 270 @output.dlist_close 271 end 272 273 def split_dlitem(line) 274 re =
/\A((?:#{BRACKET_LINK_RE}|.)*?):/o 275 if m = re.match(line) 276 return m[1], m.post_match 277 else 278 return line, ""
279 end 280 end 281 282 TABLE_RE = /\A\|\|/ 283 284 def compile_table(f) 285 lines = [] 286 f.while_match(TABLE_RE) do |line| 287 lines.push line 288 skip_comments f 289 end 290 @output.table_open 291 lines.each do |line| 292 @output.table_record_open
293
split_columns(line.sub(TABLE_RE, "")).each do |col| 294 mid = col.sub!(/\A!/,
"") ? "table_head" : "table_data" 295 span = col.slice!(/\A[\^>]*/)
296 rs = span_count(span,
"^") 297 cs =
span_count(span, ">") 298 @output.__send__(mid, compile_inline(col), rs, cs)
299 end 300 @output.table_record_close
301 end 302 @output.table_close 303 end 304 305 def split_columns(str) 306 cols = str.split(/\|\|/)
307 cols.pop if
cols.last.chomp.empty? 308 cols 309 end 310 311 def span_count(str, ch) 312 c = str.count(ch) 313 c == 0 ? nil : c + 1 314 end 315 316 BLOCKQUOTE_RE = /\A""[ \t]?/ 317 318 def compile_blockquote(f) 319 @output.blockquote_open
320 lines = []
321
f.while_match(BLOCKQUOTE_RE) do |line| 322 lines.push line.sub(BLOCKQUOTE_RE, "")
323 skip_comments f
324 end 325 compile_blocks
lines.join("") 326 @output.blockquote_close 327 end 328 329 INDENTED_PRE_RE = /\A[ \t]/ 330 331 def compile_indented_pre(f) 332 lines = f.span(INDENTED_PRE_RE)\
333 .map {|line|
rstrip(line.sub(INDENTED_PRE_RE, "")) } 334 text =
restore_plugin_block(lines.join("\n")) 335
@output.preformatted(@output.text(text)) 336 end 337 338 BLOCK_PRE_OPEN_RE = /\A<<<\s*(\w+)?/
339 BLOCK_PRE_CLOSE_RE =
/\A>>>/ 340
341 def
compile_block_pre(f) 342
m = BLOCK_PRE_OPEN_RE.match(f.gets) or raise UnexpectedError, "must not
happen" 343 str =
restore_plugin_block(f.break(BLOCK_PRE_CLOSE_RE).join.chomp) 344 f.gets 345 @output.block_preformatted(str,
m[1]) 346 end
347 348 BLANK = /\A$/ 349 PARAGRAPH_END_RE =
Regexp.union(BLANK, 350
HEADER_RE, HRULE_RE, LIST_RE, DLIST_RE, 351 BLOCKQUOTE_RE, TABLE_RE, 352 INDENTED_PRE_RE,
BLOCK_PRE_OPEN_RE) 353
354 def
compile_paragraph(f) 355
lines = f.break(PARAGRAPH_END_RE)\ 356 .reject {|line| COMMENT_RE =~ line } 357 if lines.size == 1 and
/\A\0(\d+)\0\z/ =~ strip(lines[0]) 358 @output.block_plugin plugin_block($1.to_i)
359 else 360 line_buffer =
@output.container(:paragraph) 361 lines.each_with_index do |line, i| 362 buffer = @output.container
363 line_buffer <<
buffer 364
compile_inline(lstrip(line).chomp, buffer) 365 end 366 @output.paragraph(line_buffer) 367 end 368 end 369 370 # 371 # Inline Level 372 # 373 374 BRACKET_LINK_RE = /\[\[.+?\]\]/ 375 URI_RE =
/(?:https?|ftp|file|mailto):[A-Za-z0-9;\/?:@&=+$,\-_.!~*\'()#%]+/
376 WIKI_NAME_RE =
/\b(?:[A-Z]+[a-z\d]+){2,}\b/ 377 378 def inline_syntax_re 379 if @options[:use_wiki_name] 380 if @options[:use_not_wiki_name]
381 /
(#{BRACKET_LINK_RE}) 382 | (#{URI_RE}) 383 | (#{MODIFIER_RE}) 384 | (\^?#{WIKI_NAME_RE}) 385 /xo 386 else 387 / (#{BRACKET_LINK_RE})
388 | (#{URI_RE})
389 | (#{MODIFIER_RE})
390 | (#{WIKI_NAME_RE})
391 /xo 392 end 393 else 394 / (#{BRACKET_LINK_RE})
395 | (#{URI_RE})
396 | (#{MODIFIER_RE})
397 /xo 398 end 399 end 400 401 def compile_inline(str, buf = nil) 402 buf ||= @output.container
403 re = inline_syntax_re
404 pending_str = nil
405 while m =
re.match(str) 406 str =
m.post_match 407
408 link, uri, mod,
wiki_name = m[1, 4] 409
if wiki_name and wiki_name[0, 1] == "^" 410 pending_str = m.pre_match +
wiki_name[1..-1] 411 next
412 end 413 414 pre_str = "#{pending_str}#{m.pre_match}"
415 pending_str = nil
416
evaluate_plugin_block(pre_str, buf) 417 compile_inline_markup(buf, link, uri, mod, wiki_name)
418 end 419 evaluate_plugin_block(pending_str
|| str, buf) 420 buf
421 end 422 423 def compile_inline_markup(buf, link, uri, mod,
wiki_name) 424 case
425 when link
426 buf <<
compile_bracket_link(link[2...-2]) 427 when uri 428 buf << compile_uri_autolink(uri) 429 when mod 430 buf << compile_modifier(mod)
431 when wiki_name
432 buf <<
@output.wiki_name(wiki_name) 433 else 434 raise UnexpectedError, "must not happen"
435 end 436 end 437 438 def compile_bracket_link(link) 439 if m =
/\A(?>[^|\\]+|\\.)*\|/.match(link) 440 title = m[0].chop 441 uri = m.post_match 442 fixed_uri = fix_uri(uri) 443 if can_image_link?(uri)
444
@output.image_hyperlink(fixed_uri, title) 445 else 446 @output.hyperlink(fixed_uri, compile_modifier(title))
447 end 448 else 449 fixed_link = fix_uri(link)
450 if
can_image_link?(link) 451
@output.image_hyperlink(fixed_link) 452 else 453 @output.hyperlink(fixed_link, @output.text(link))
454 end 455 end 456 end 457 458 def can_image_link?(uri) 459 image?(uri) and
@options[:allow_bracket_inline_image] 460 end 461 462 def compile_uri_autolink(uri) 463 if image?(uri) 464
@output.image_hyperlink(fix_uri(uri)) 465 else 466 @output.hyperlink(fix_uri(uri), @output.text(uri))
467 end 468 end 469 470 def fix_uri(uri) 471 if /\A(?:https?|ftp|file):(?!\/\/)/ =~ uri
472 uri.sub(/\A\w+:/,
"") 473 else
474 uri 475 end 476 end 477 478 IMAGE_EXTS = %w(.jpg .jpeg .gif .png) 479 480 def image?(uri) 481 IMAGE_EXTS.include?(File.extname(uri).downcase)
482 end 483 484 STRONG = "'''" 485 EM = "''" 486 DEL = "==" 487 488 STRONG_RE = /'''.+?'''/ 489 EM_RE = /''.+?''/ 490 DEL_RE = /==.+?==/ 491 492 MODIFIER_RE = Regexp.union(STRONG_RE, EM_RE, DEL_RE)
493 494 MODTAG = { 495 STRONG => "strong",
496 EM =>
"em", 497 DEL
=> "del" 498 } 499 500 def compile_modifier(str) 501 buf = @output.container
502 while m = /
(#{MODIFIER_RE}) 503
/xo.match(str) 504
evaluate_plugin_block(m.pre_match, buf) 505 case 506 when chunk = m[1] 507 mod, s = split_mod(chunk) 508 mid = MODTAG[mod] 509 buf << @output.__send__(mid,
compile_inline(s)) 510
else 511 raise
UnexpectedError, "must not happen" 512 end 513 str = m.post_match 514 end 515 evaluate_plugin_block(str, buf) 516 buf 517 end 518 519 def split_mod(str) 520 case str 521 when /\A'''/ 522 return str[0, 3], str[3...-3] 523 when /\A''/ 524 return str[0, 2], str[2...-2]
525 when /\A==/
526 return str[0, 2],
str[2...-2] 527 else
528 raise
UnexpectedError, "must not happen: #{str.inspect}" 529 end 530 end 531 532 def strip(str) 533 rstrip(lstrip(str)) 534 end 535 536 def rstrip(str) 537 str.sub(/[ \t\r\n\v\f]+\z/, "")
538 end 539 540 def lstrip(str) 541 str.sub(/\A[ \t\r\n\v\f]+/, "")
542 end 543 544 545 class HTMLOutput 546 def initialize(suffix = " />")
547 @suffix = suffix
548 @f = nil 549 end 550 551 def reset 552 @f = StringIO.new 553 end 554 555 def finish 556 @f.string 557 end 558 559 def container(_for=nil) 560 case _for 561 when :paragraph 562 [] 563 else 564 "" 565 end 566 end 567 568 # 569 # Procedures 570 # 571 572 def headline(level, title) 573 @f.puts
"<h#{level}>#{title}</h#{level}>" 574 end 575 576 def hrule 577 @f.puts "<hr#{@suffix}" 578 end 579 580 def list_begin 581 end 582 583 def list_end 584 @f.puts 585 end 586 587 def list_open(type) 588 @f.puts "<#{type}>" 589 end 590 591 def list_close(type) 592 @f.print "</#{type}>" 593 end 594 595 def listitem_open 596 @f.print "<li>" 597 end 598 599 def listitem_close 600 @f.puts "</li>" 601 end 602 603 def listitem(item) 604 @f.print item 605 end 606 607 def dlist_open 608 @f.puts "<dl>" 609 end 610 611 def dlist_close 612 @f.puts "</dl>" 613 end 614 615 def dlist_item(dt, dd) 616 case 617 when dd.empty? 618 @f.puts
"<dt>#{dt}</dt>" 619 when dt.empty? 620 @f.puts "<dd>#{dd}</dd>"
621 else 622 @f.puts
"<dt>#{dt}</dt>" 623 @f.puts "<dd>#{dd}</dd>"
624 end 625 end 626 627 def table_open 628 @f.puts %Q(<table border="1">)
629 end 630 631 def table_close 632 @f.puts "</table>" 633 end 634 635 def table_record_open 636 @f.print "<tr>" 637 end 638 639 def table_record_close 640 @f.puts "</tr>"
641 end 642 643 def table_head(item, rs, cs) 644 @f.print "<th#{tdattr(rs,
cs)}>#{item}</th>" 645 end 646 647 def table_data(item, rs, cs) 648 @f.print "<td#{tdattr(rs,
cs)}>#{item}</td>" 649 end 650 651 def tdattr(rs, cs) 652 buf = "" 653 buf << %Q( rowspan="#{rs}") if rs
654 buf << %Q(
colspan="#{cs}") if cs 655 buf 656 end 657 private :tdattr 658 659 def blockquote_open 660 @f.print "<blockquote>" 661 end 662 663 def blockquote_close 664 @f.puts "</blockquote>" 665 end 666 667 def block_preformatted(str, info) 668 syntax = info ? info.downcase :
nil 669 if syntax
670 begin 671 convertor =
Syntax::Convertors::HTML.for_syntax(syntax) 672 @f.puts convertor.convert(str) 673 return 674 rescue NameError, RuntimeError
675 end 676 end 677 preformatted(text(str))
678 end 679 680 def preformatted(str) 681 @f.print "<pre>" 682 @f.print str 683 @f.puts "</pre>"
684 end 685 686 def paragraph(lines) 687 @f.puts
"<p>#{lines.join("\n")}</p>" 688 end 689 690 def block_plugin(str) 691 @f.puts %Q(<div
class="plugin">{{#{escape_html(str)}}}</div>)
692 end 693 694 # 695 # Functions 696 # 697 698 def hyperlink(uri, title) 699 %Q(<a
href="#{escape_html_param(uri)}">#{title}</a>)
700 end 701 702 def wiki_name(name) 703 hyperlink(name, text(name)) 704 end 705 706 def image_hyperlink(uri, alt = nil) 707 alt ||= uri.split(/\//).last
708 alt =
escape_html(alt) 709
%Q(<img src="#{escape_html_param(uri)}"
alt="#{alt}"#{@suffix}) 710 end 711 712 def strong(item) 713 "<strong>#{item}</strong>"
714 end 715 716 def em(item) 717 "<em>#{item}</em>" 718 end 719 720 def del(item) 721 "<del>#{item}</del>"
722 end 723 724 def text(str) 725 escape_html(str) 726 end 727 728 def inline_plugin(src) 729 %Q(<span
class="plugin">{{#{src}}}</span>) 730 end 731 732 # 733 # Utilities 734 # 735 736 def escape_html_param(str) 737 escape_quote(escape_html(str))
738 end 739 740 def escape_html(text) 741 text.gsub(/&/,
"&").gsub(/</, "<").gsub(/>/,
">") 742 end 743 744 def unescape_html(text) 745 text.gsub(/>/,
">").gsub(/</, "<").gsub(/&/,
"&") 746
end 747 748 def escape_quote(text)
749 text.gsub(/"/,
""") 750 end 751 end 752 753 754 class LineInput 755 def initialize(f) 756 @input = f 757 @buf = [] 758 @lineno = 0 759 @eof_p = false 760 end 761 762 def inspect 763 "\#<#{self.class} file=#{@input.inspect}
line=#{lineno()}>" 764 end 765 766 def eof? 767 @eof_p 768 end 769 770 def lineno 771 @lineno 772 end 773 774 def gets 775 unless @buf.empty? 776 @lineno += 1 777 return @buf.pop 778 end 779 return nil if @eof_p # to avoid ARGF blocking.
780 line = @input.gets
781 line =
line.sub(/\r\n/, "\n") if line 782 @eof_p = line.nil? 783 @lineno += 1 784 line 785 end 786 787 def ungets(line) 788 return unless line 789 @lineno -= 1 790 @buf.push line 791 line 792 end 793 794 def peek 795 line = gets() 796 ungets line if line 797 line 798 end 799 800 def next? 801 peek() ? true : false 802 end 803 804 def skip_blank_lines 805 n = 0 806 while line = gets()
807 unless
line.strip.empty? 808
ungets line 809 return
n 810 end 811 n += 1 812 end 813 n 814 end 815 816 def gets_if(re) 817 line = gets() 818 if not line or not (re =~ line) 819 ungets line 820 return nil 821 end 822 line 823 end 824 825 def gets_unless(re) 826 line = gets() 827 if not line or re =~ line
828 ungets line
829 return nil
830 end 831 line 832 end 833 834 def each 835 while line = gets() 836 yield line 837 end 838 end 839 840 def while_match(re) 841 while line = gets() 842 unless re =~ line 843 ungets line 844 return 845 end 846 yield line 847 end 848 nil 849 end 850 851 def getlines_while(re) 852 buf = [] 853 while_match(re) do |line|
854 buf.push line
855 end 856 buf 857 end 858 859 alias span getlines_while # from Haskell 860 861 def until_match(re) 862 while line = gets() 863 if re =~ line 864 ungets line 865 return 866 end 867 yield line 868 end 869 nil 870 end 871 872 def getlines_until(re) 873 buf = [] 874 until_match(re) do |line|
875 buf.push line
876 end 877 buf 878 end 879 880 alias break getlines_until # from Haskell
881 882 def until_terminator(re)
883 while line =
gets() 884 return if
re =~ line # discard terminal line 885 yield line 886 end 887 nil 888 end 889 890 def getblock(term_re) 891 buf = [] 892 until_terminator(term_re) do
|line| 893 buf.push
line 894 end
895 buf 896 end 897 end 898 end 899 900 if __FILE__ == $0 901 puts HikiDoc.to_html(ARGF.read(nil)) 902 end
Generated using the rcov
code coverage analysis tool for Ruby version 0.8.1.2.