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 << '&#174;' # (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