lib/trac_wiki/parser.rb in trac-wiki-0.0.1 vs lib/trac_wiki/parser.rb in trac-wiki-0.0.2
- old
+ new
@@ -1,23 +1,23 @@
require 'cgi'
require 'uri'
# :main: TracWiki
-# The Creole parses and translates Creole formatted text into
+# The TracWiki parses and translates Trac formatted text into
# XHTML. Creole is a lightweight markup syntax similar to what many
# WikiWikiWebs use. Example syntax:
#
# = Heading 1 =
# == Heading 2 ==
# === Heading 3 ===
# **Bold text**
-# //Italic text//
+# ''Italic text''
# [[Links]]
-# |=Table|=Heading|
-# |Table |Cells |
-# {{image.png}}
+# ||=Table||=Heading||
+# || Table|| Cells ||
+# [[Image(image.png)]]
#
# The simplest interface is TracWiki.render. The default handling of
# links allow explicit local links using the [[link]] syntax. External
# links will only be allowed if specified using http(s) and ftp(s)
# schemes. If special link handling is needed, such as inter-wiki or
@@ -72,10 +72,11 @@
# #=> "<p><strong>Hello <em>World</em></strong></p>"
def to_html
@out = ''
@p = false
@stack = []
+ @stacki = []
parse_block(@text)
@out
end
protected
@@ -90,17 +91,31 @@
# encoding.
def escape_url(string)
CGI::escape(string)
end
- def start_tag(tag)
+ def start_tag(tag, args = '', lindent = nil)
+ lindent = @stacki.last || -1 if lindent.nil?
+
@stack.push(tag)
- @out << '<' << tag << '>'
+ @stacki.push(lindent)
+
+ if tag == 'strongem'
+ @out << '<strong><em>'
+ else
+ @out << '<' << tag << args << '>'
+ end
end
def end_tag
- @out << '</' << @stack.pop << '>'
+ tag = @stack.pop
+ tagi = @stacki.pop
+ if tag == 'strongem'
+ @out << '</em></strong>'
+ else
+ @out << "</#{tag}>"
+ end
end
def toggle_tag(tag, match)
if @stack.include?(tag)
if @stack.last == tag
@@ -183,15 +198,15 @@
# Create image markup. This
# method can be overridden to generate custom
# markup, for example to add html additional attributes or
# to put divs around the imgs.
- def make_image(uri, alt)
+ def make_image(uri, alt='')
if alt
- '<img src="' << escape_html(uri) << '" alt="' << escape_html(alt) << '"/>'
+ "<img src='" << escape_html(uri) << "' alt='" << escape_html(alt) << "'/>"
else
- '<img src="' << escape_html(uri) << '"/>'
+ "<img src='" << escape_html(uri) << "'/>"
end
end
def make_headline(level, text)
"<h#{level}>" << escape_html(text) << "</h#{level}>"
@@ -207,10 +222,11 @@
end
def parse_inline(str)
until str.empty?
case str
+ # raw url
when /\A(\~)?((https?|ftps?):\/\/\S+?)(?=([\,.?!:;"'\)]+)?(\s|$))/
str = $'
if $1
@out << escape_html($2)
else
@@ -218,61 +234,85 @@
@out << '<a href="' << escape_html(uri) << '">' << escape_html($2) << '</a>'
else
@out << escape_html($&)
end
end
+ # [[Image(pic.jpg|tag)]]
+ when /\A\[\[Image\(([^|].*?)(\|(.*?))?\)\]\]/ # image
+ str = $'
+ @out << make_image($1, $3)
+ # [[link]]
when /\A\[\[\s*([^|]*?)\s*(\|\s*(.*?))?\s*\]\]/m
str = $'
- link, content = $1, $3
- if uri = make_explicit_link(link)
- @out << '<a href="' << escape_html(uri) << '">'
- if content
- until content.empty?
- content = parse_inline_tag(content)
- end
- else
- @out << escape_html(link)
- end
- @out << '</a>'
- else
- @out << escape_html($&)
- end
+ link, content, whole= $1, $3, $&
+ make_link(link, content, whole)
else
str = parse_inline_tag(str)
end
+
end
end
+ def make_link(link, content, whole)
+ uri = make_explicit_link(link)
+ # specail "link" [[BR]]:
+ if link =~ /br/i
+ @out << '<br/>'
+ return
+ end
+ if not uri
+ @out << escape_html(whole)
+ return
+ end
+
+ make_explicit_link(link)
+ @out << '<a href="' << escape_html(uri) << '">'
+ if content
+ until content.empty?
+ content = parse_inline_tag(content)
+ end
+ else
+ @out << escape_html(link)
+ end
+ @out << '</a>'
+ end
+
def parse_inline_tag(str)
case str
when /\A\{\{\{(.*?\}*)\}\}\}/ # inline pre (tt)
@out << '<tt>' << escape_html($1) << '</tt>'
when /\A`(.*?)`/ # inline pre (tt)
@out << '<tt>' << escape_html($1) << '</tt>'
- when /\A\{\{\s*(.*?)\s*(\|\s*(.*?)\s*)?\}\}/
- if uri = make_image_link($1)
- @out << make_image(uri, $3)
- else
- @out << escape_html($&)
- end # link
+# when /\A\[\[Image\(([^|].*?)(\|(.*?))?\)\]\]/ # image
+# @out << make_image($1, $3)
+
+# when /\A\{\{\s*(.*?)\s*(\|\s*(.*?)\s*)?\}\}/
+# if uri = make_image_link($1)
+# @out << make_image(uri, $3)
+# else
+# @out << escape_html($&)
+# end # link
+
when /\A([:alpha:]|[:digit:])+/
@out << $& # word
when /\A\s+/
@out << ' ' if @out[-1] != ?\s # spaces
- when /\A\*\*/
+ when /\A'''''/
+ toggle_tag 'strongem', $& # bolditallic
+ when /\A\*\*/, /\A'''/
toggle_tag 'strong', $& # bold
- when /\A''/
+ when /\A''/, /\A\/\//
toggle_tag 'em', $& # italic
- when /\A\\\\/
+ when /\A\\\\/, /\A\[\[br\]\]/i
@out << '<br/>' # newline
when /\A__/
toggle_tag 'u', $& # underline
when /\A~~/
toggle_tag 'del', $& # delete
# when /\A\+\+/
# toggle_tag 'ins', $& # insert
- when /\A\^\^/
+ when /\A\^/
toggle_tag 'sup', $& # ^{}
when /\A,,/
toggle_tag 'sub', $& # _{}
when /\A\(R\)/i
@out << '®' # (R)
@@ -285,40 +325,111 @@
end
return $'
end
def parse_table_row(str)
- @out << '<tr>'
- #str.scan(/\s*\|\|(=)?\s*((\[\[.*?\]\]|\{\{.*?\}\}|[^|~]|~.)*)(?=\||$)/) do
- str.scan(/\s*\|\|(=)?(\s*)(.*?)(?==?\|\||$)/) do
- if !$3.empty? || !$'.empty?
- tag = $1 ? 'th' : 'td'
- le = $2.size
- txt = $3
- style =''
- if txt =~ /\S(\s*)$/
- ri = $1.size
-# style = " style='text-align:left'" if le == 0
- style = " style='text-align:right'" if ri == 0 && le >= 1
- style = " style='text-align:center'" if le >= 2 && ri >= 2
- #print "le#{le} ri#{ri} st:#{style}\n"
- end
- @out << ('<' + tag + style + '>' )
- parse_inline(txt.strip) if txt
- end_tag while @stack.last != 'table'
- @out << ('</' + tag + '>')
+ start_tag('tr') if !@stack.include?('tr')
+ colspan = 1
+ print_tr = true
+ last_tail = ''
+ last_txt = ''
+ str.scan(/(=?)(\s*)(.*?)\1?($ | \|\|\\\s*$ | \|\| )/x) do
+ tdth = $1.empty? ? 'td' : 'th'
+ le, txt, tail = $2.size, $3, $4
+
+ # do not end row, continue on next line
+ print_tr = false if tail =~ /^\|\|\\/
+
+ if txt.empty? && le == 0
+ colspan += 1
+ next
end
+
+ style = ''
+ if txt =~ /\S(\s*)$/
+ ri = $1.size
+ ri += 100 if tail.empty? # do not right when last || omnited
+ style = " style='text-align:right'" if ri == 0 && le >= 1
+ style = " style='text-align:center'" if le >= 2 && ri >= 2
+ #print "le#{le} ri#{ri} st:#{style}\n"
+ end
+
+ colspan_txt = colspan > 1 ? " colspan='#{colspan}'" : ''
+ start_tag(tdth, style + colspan_txt);
+ colspan = 1
+
+ parse_inline(txt.strip) if txt
+ end_tag while @stack.last != 'tr'
end
- @out << '</tr>'
+ if print_tr
+ end_tag
+ end
end
def make_nowikiblock(input)
input.gsub(/^ (?=\}\}\})/, '')
end
- def ulol?(x); x == 'ul' || x == 'ol'; end
+ def parse_li_line(spc_size, bullet, text)
+ while !@stacki.empty? && @stacki.last > spc_size
+ end_tag
+ end
+
+ if @stack.include?('li')
+ while @stack.last != 'li'
+ end_tag
+ end
+
+ # end list if type differ
+ # @stack.last is now ul or li
+ if @stacki.last == spc_size
+ end_tag # li
+ ulol_last = @stack.last
+ ulol_now = bullet =~ /[*-]/ ? 'ul' : 'ol'
+ if ulol_last != ulol_now
+ end_tag # ol | ul
+ end
+ end
+ else
+ end_paragraph
+ end
+
+ if @stacki.empty? || @stacki.last < spc_size
+ bullet.gsub!(/\.$/,'')
+ ulol = bullet =~ /[-*]/ ? 'ul' : 'ol';
+ attr = ""
+ attr = " type='i'" if bullet =~ /i/i;
+ attr = " type='a'" if bullet =~ /a/i;
+
+ if bullet =~ /^\d+$/ && bullet != '1'
+ attr += " start='#{bullet}'"
+ end
+ start_tag(ulol, attr, spc_size)
+ end
+
+ start_tag('li')
+ parse_inline(text)
+
+ end
+
+ def blockquote_level_to(level)
+ cur_level = @stack.count('blockquote')
+ if cur_level == level
+ @out << ' '
+ return
+ end
+ while cur_level < level
+ cur_level += 1
+ start_tag('blockquote')
+ end
+ while cur_level > level
+ cur_level -= 1 if @stack.last == 'blockquote'
+ end_tag
+ end
+ end
+
def parse_block(str)
until str.empty?
case str
# pre {{{ ... }}}
@@ -337,61 +448,62 @@
end_paragraph
level = $1.size
@out << make_headline(level, $2)
# table row
- when /\A[ \t]*\|\|.*$(\r?\n)?/
+ when /\A[ \t]*\|\|(.*)$(\r?\n)?/
if !@stack.include?('table')
end_paragraph
start_tag('table')
end
- parse_table_row($&)
+ parse_table_row($1)
# empty line
when /\A\s*$(\r?\n)?/
end_paragraph
+ when /\A([\w\s]*)::\s*/
+ term = $1
+ start_tag('dl')
+ start_tag('dt')
+ @out << escape_html(term)
+ end_tag
+ start_tag('dd')
# li
- when /\A(\s*([*#]+)\s*(.*?))$(\r?\n)?/
- line, bullet, item = $1, $2, $3
- tag = (bullet[0,1] == '*' ? 'ul' : 'ol')
- if bullet[0,1] == '#' || bullet.size != 2 || @stack.find {|x| ulol?(x) }
- count = @stack.select { |x| ulol?(x) }.size
+ when /\A(\s*)([*-]|[aAIi\d]\.)\s+(.*?)$(\r?\n)?/
+ parse_li_line($1.size, $2, $3)
- while !@stack.empty? && count > bullet.size
- count -= 1 if ulol?(@stack.last)
- end_tag
- end
+ when /\A(>[>\s]*)(.*?)$(\r?\n)?/
+ # citation
+ level, quote = $1.count('>'), $2
- end_tag while !@stack.empty? && @stack.last != 'li'
+ start_paragraph if !@stack.include? 'p'
+ blockquote_level_to(level)
+ parse_inline(quote.strip)
- if @stack.last == 'li' && count == bullet.size
- end_tag
- if @stack.last != tag
- end_tag
- count -= 1
- end
- end
- while count < bullet.size
- start_tag tag
- count += 1
- start_tag 'li' if count < bullet.size
- end
+ # ordinary line
+ when /\A(\s*)(\S+.*?)$(\r?\n)?/
+ spc_size, text = $1.size, $2
- @p = true
- start_tag('li')
- parse_inline(item)
+ if @stack.include?('li') ||@stack.include?('dl')
+
+ # dl, li continuation
+ parse_inline(' ')
+ parse_inline(text)
+
+ elsif spc_size > 0
+ # quote continuation
+ start_paragraph if !@stack.include? 'p'
+ blockquote_level_to(1)
+ parse_inline(text)
+
else
+ # real ordinary line
start_paragraph
- parse_inline(line)
+ parse_inline(text)
end
-
- # ordinary line
- when /\A([ \t]*\S+.*?)$(\r?\n)?/
- start_paragraph
- parse_inline($1)
- else
+ else # case str
raise "Parse error at #{str[0,30].inspect}"
end
str = $'
end
end_paragraph