Ивайло обнови решението на 22.11.2011 15:10 (преди почти 13 години)
+module InlineFormatters
+ def format_font string
+ sub = ->(args) do
+ add_tags(format_font(args[2]), {'_' => 'em', '**' => "strong"}[args[1]])
+ end
+
+ string.gsub(/^.*(_|\*\*)([^<\n]+?)\1.*$/) do |s|
+ format s, /(_|\*\*)([^<]+?)\1/, sub
+ end
+ end
+
+ def format_link string
+ string.gsub(/^.*\[(.+?)\]\((.+?)\).*$/) do |s|
+ format s, /\[(.+?)\]\((.+?)\)/, ->(args) { "<a href=\"#{args[2]}\">#{args[1]}</a>" }
+ end
+ end
+
+ def escape string
+ escape_symbols = {'&' => '&', '<' => '<', '"' => '"'}
+
+ result = string.gsub(/[&<"]/, escape_symbols).gsub(/(?<!^)>/, '>')
+ end
+
+ def add_tags(body, tag, separator = '', end_tag = nil)
+ if body.end_with? "\n"
+ body = body.chomp "\n"
+ suffix = "\n"
+ else
+ suffix = ''
+ end
+
+ "<#{tag}>#{separator}#{body.chomp}#{separator}</#{end_tag ? end_tag : tag}>" + suffix
+ end
+
+ def format string, pattern, block
+ # Make it more general in order to use in format_paragraphs and format_headers
+ if string.start_with?(' ')
+ string
+ else
+ string.gsub(pattern) { block.call($~.to_a) }
+ end
+ end
+end
+
+module ParagraphFormatters
+ def format_paragraphs string
+ string.gsub(/(^[^<>\n\t][^<\n]*[^<\s][^<\n]*$\n?)+/) do |s|
+ if s.start_with?(' ') or s.start_with?("> ")
+ s
+ else
+ add_tags(s.gsub(/^[ \t]*(.*?\S)[ \t]*$/, '\1'), 'p')
+ end
+ end
+ end
+
+ def format_headers string
+ string.gsub(/^[ \t]*(\#{1,4})\s+(\S.*)$/) do |s|
+ if s.start_with? ' '
+ s
+ else
+ add_tags($2.strip,'h' + $1.size.to_s)
+ end
+ end
+ end
+
+ def format_code_block string
+ string.gsub(/((^ {4}(.*)$\n?)+)/) do |s|
+ add_tags(s.gsub(/^ (.*)$/, '\1'), 'pre><code', '', 'code></pre')
+ end
+ end
+
+ def format_block_quotes string
+ string.gsub(/(^>\s+.*$\n?)+/) do |s|
+ add_tags(format_paragraphs(s.gsub(/^>\s+?(.*)$/, '\1')), 'blockquote')
+ end
+ end
+
+ def format_unordered_list string
+ string.gsub(/^(\*\s+.*$\n?)+/) do |s|
+ add_tags(s.gsub(/^\*\s+(.*)$/, ' <li>\1</li>'), 'ul', "\n")
+ end
+ end
+
+ def format_ordered_list string
+ string.gsub(/^(\d+\.\s+.*$\n?)+/) do |s|
+ add_tags(s.gsub(/^\d+\.\s+(.*)$/, ' <li>\1</li>'), 'ol', "\n")
+ end
+ end
+
+ def format_empty_lines string
+ string.gsub(/^\s{1,3}$/, '')
+ end
+end
+
+class Formatter
+ include InlineFormatters
+ include ParagraphFormatters
+
+ def initialize raw_string
+ @raw_string = raw_string
+ end
+
+ def to_html
+ # we cache the result
+ @result ||= generate_result
+ end
+
+ def inspect
+ @raw_string
+ end
+
+ def generate_result
+ result = format_empty_lines(escape(@raw_string.dup))
+ result = format_headers result
+ result = format_unordered_list result
+ result = format_ordered_list result
+ result = format_paragraphs result
+ result = format_block_quotes result
+ result = format_font(format_link(result))
+
+ format_code_block(result).strip
+ end
+
+ alias to_s to_html
+end