Merge remote-tracking branch 'origin/master' into websequencediagrams
Conflicts: README.md lib/gollum.rb lib/gollum/markup.rb
This commit is contained in:
+183
-63
@@ -1,7 +1,10 @@
|
||||
require 'digest/sha1'
|
||||
require 'cgi'
|
||||
require 'pygments'
|
||||
require 'base64'
|
||||
|
||||
module Gollum
|
||||
|
||||
class Markup
|
||||
# Initialize a new Markup object.
|
||||
#
|
||||
@@ -11,13 +14,15 @@ module Gollum
|
||||
def initialize(page)
|
||||
@wiki = page.wiki
|
||||
@name = page.filename
|
||||
@data = page.raw_data
|
||||
@version = page.version.id
|
||||
@data = page.text_data
|
||||
@version = page.version.id if page.version
|
||||
@format = page.format
|
||||
@dir = ::File.dirname(page.path)
|
||||
@tagmap = {}
|
||||
@codemap = {}
|
||||
@texmap = {}
|
||||
@wsdmap = {}
|
||||
@premap = {}
|
||||
end
|
||||
|
||||
# Render the content with Gollum wiki syntax on top of the file's own
|
||||
@@ -25,13 +30,15 @@ module Gollum
|
||||
#
|
||||
# no_follow - Boolean that determines if rel="nofollow" is added to all
|
||||
# <a> tags.
|
||||
# encoding - Encoding Constant or String.
|
||||
#
|
||||
# Returns the formatted String content.
|
||||
def render(no_follow = false)
|
||||
sanitize_options = no_follow ?
|
||||
HISTORY_SANITIZATION_OPTIONS :
|
||||
SANITIZATION_OPTIONS
|
||||
data = extract_tex(@data)
|
||||
def render(no_follow = false, encoding = nil)
|
||||
sanitize = no_follow ?
|
||||
@wiki.history_sanitizer :
|
||||
@wiki.sanitizer
|
||||
|
||||
data = extract_tex(@data.dup)
|
||||
data = extract_code(data)
|
||||
data = extract_wsd(data)
|
||||
data = extract_tags(data)
|
||||
@@ -44,10 +51,15 @@ module Gollum
|
||||
data = %{<p class="gollum-error">#{e.message}</p>}
|
||||
end
|
||||
data = process_tags(data)
|
||||
data = process_code(data)
|
||||
data = process_wsd(data)
|
||||
data = Sanitize.clean(data, sanitize_options)
|
||||
data = process_code(data, encoding)
|
||||
if sanitize || block_given?
|
||||
doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
||||
doc = sanitize.clean_node!(doc) if sanitize
|
||||
yield doc if block_given?
|
||||
data = doc.to_html
|
||||
end
|
||||
data = process_tex(data)
|
||||
data = process_wsd(data)
|
||||
data.gsub!(/<p><\/p>/, '')
|
||||
data
|
||||
end
|
||||
@@ -65,12 +77,14 @@ module Gollum
|
||||
# Returns the placeholder'd String data.
|
||||
def extract_tex(data)
|
||||
data.gsub(/\\\[\s*(.*?)\s*\\\]/m) do
|
||||
id = Digest::SHA1.hexdigest($1)
|
||||
@texmap[id] = [:block, $1]
|
||||
tag = CGI.escapeHTML($1)
|
||||
id = Digest::SHA1.hexdigest(tag)
|
||||
@texmap[id] = [:block, tag]
|
||||
id
|
||||
end.gsub(/\\\(\s*(.*?)\s*\\\)/m) do
|
||||
id = Digest::SHA1.hexdigest($1)
|
||||
@texmap[id] = [:inline, $1]
|
||||
tag = CGI.escapeHTML($1)
|
||||
id = Digest::SHA1.hexdigest(tag)
|
||||
@texmap[id] = [:inline, tag]
|
||||
id
|
||||
end
|
||||
end
|
||||
@@ -84,13 +98,7 @@ module Gollum
|
||||
def process_tex(data)
|
||||
@texmap.each do |id, spec|
|
||||
type, tex = *spec
|
||||
out =
|
||||
case type
|
||||
when :block
|
||||
%{<script type="math/tex; mode=display">#{tex}</script>}
|
||||
when :inline
|
||||
%{<script type="math/tex">#{tex}</script>}
|
||||
end
|
||||
out = %{<img src="#{::File.join(@wiki.base_path, '_tex.png')}?type=#{type}&data=#{Base64.encode64(tex).chomp}" alt="#{CGI.escapeHTML(tex)}">}
|
||||
data.gsub!(id, out)
|
||||
end
|
||||
data
|
||||
@@ -108,49 +116,57 @@ module Gollum
|
||||
#
|
||||
# Returns the placeholder'd String data.
|
||||
def extract_tags(data)
|
||||
data.gsub(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
|
||||
data.gsub!(/(.?)\[\[(.+?)\]\]([^\[]?)/m) do
|
||||
if $1 == "'" && $3 != "'"
|
||||
"[[#{$2}]]#{$3}"
|
||||
elsif $2.include?('][')
|
||||
$&
|
||||
if $2[0..4] == 'file:'
|
||||
pre = $1
|
||||
post = $3
|
||||
parts = $2.split('][')
|
||||
parts[0][0..4] = ""
|
||||
link = "#{parts[1]}|#{parts[0].sub(/\.org/,'')}"
|
||||
id = Digest::SHA1.hexdigest(link)
|
||||
@tagmap[id] = link
|
||||
"#{pre}#{id}#{post}"
|
||||
else
|
||||
$&
|
||||
end
|
||||
else
|
||||
id = Digest::SHA1.hexdigest($2)
|
||||
@tagmap[id] = $2
|
||||
"#{$1}#{id}#{$3}"
|
||||
end
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
# Process all tags from the tagmap and replace the placeholders with the
|
||||
# final markup.
|
||||
#
|
||||
# data - The String data (with placeholders).
|
||||
# no_follow - Boolean that determines if rel="nofollow" is added to all
|
||||
# <a> tags.
|
||||
#
|
||||
# Returns the marked up String data.
|
||||
def process_tags(data, no_follow = false)
|
||||
def process_tags(data)
|
||||
@tagmap.each do |id, tag|
|
||||
data.gsub!(id, process_tag(tag, no_follow))
|
||||
data.gsub!(id, process_tag(tag))
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
# Process a single tag into its final HTML form.
|
||||
#
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# brackets).
|
||||
# no_follow - Boolean that determines if rel="nofollow" is added to all
|
||||
# <a> tags.
|
||||
#
|
||||
# Returns the String HTML version of the tag.
|
||||
def process_tag(tag, no_follow = false)
|
||||
def process_tag(tag)
|
||||
if html = process_image_tag(tag)
|
||||
html
|
||||
elsif html = process_file_link_tag(tag, no_follow)
|
||||
elsif html = process_file_link_tag(tag)
|
||||
html
|
||||
else
|
||||
process_page_link_tag(tag, no_follow)
|
||||
process_page_link_tag(tag)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -162,10 +178,12 @@ module Gollum
|
||||
# if it is not.
|
||||
def process_image_tag(tag)
|
||||
parts = tag.split('|')
|
||||
return if parts.size.zero?
|
||||
|
||||
name = parts[0].strip
|
||||
path = if file = find_file(name)
|
||||
::File.join @wiki.base_path, file.path
|
||||
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/
|
||||
elsif name =~ /^https?:\/\/.+(jpg|png|gif|svg|bmp)$/i
|
||||
name
|
||||
end
|
||||
|
||||
@@ -243,15 +261,15 @@ module Gollum
|
||||
|
||||
# Attempt to process the tag as a file link tag.
|
||||
#
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# brackets).
|
||||
# no_follow - Boolean that determines if rel="nofollow" is added to all
|
||||
# <a> tags.
|
||||
#
|
||||
# Returns the String HTML if the tag is a valid file link tag or nil
|
||||
# if it is not.
|
||||
def process_file_link_tag(tag, no_follow = false)
|
||||
def process_file_link_tag(tag)
|
||||
parts = tag.split('|')
|
||||
return if parts.size.zero?
|
||||
|
||||
name = parts[0].strip
|
||||
path = parts[1] && parts[1].strip
|
||||
path = if path && file = find_file(path)
|
||||
@@ -262,49 +280,42 @@ module Gollum
|
||||
nil
|
||||
end
|
||||
|
||||
tag = if name && path && file
|
||||
if name && path && file
|
||||
%{<a href="#{::File.join @wiki.base_path, file.path}">#{name}</a>}
|
||||
elsif name && path
|
||||
%{<a href="#{path}">#{name}</a>}
|
||||
else
|
||||
nil
|
||||
end
|
||||
if tag && no_follow
|
||||
tag.sub! /^<a/, '<a ref="nofollow"'
|
||||
end
|
||||
tag
|
||||
end
|
||||
|
||||
# Attempt to process the tag as a page link tag.
|
||||
#
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# tag - The String tag contents (the stuff inside the double
|
||||
# brackets).
|
||||
# no_follow - Boolean that determines if rel="nofollow" is added to all
|
||||
# <a> tags.
|
||||
#
|
||||
# Returns the String HTML if the tag is a valid page link tag or nil
|
||||
# if it is not.
|
||||
def process_page_link_tag(tag, no_follow = false)
|
||||
def process_page_link_tag(tag)
|
||||
parts = tag.split('|')
|
||||
name = parts[0].strip
|
||||
cname = Page.cname((parts[1] || parts[0]).strip)
|
||||
tag = if name =~ %r{^https?://} && parts[1].nil?
|
||||
parts.reverse! if @format == :mediawiki
|
||||
|
||||
name, page_name = *parts.compact.map(&:strip)
|
||||
cname = @wiki.page_class.cname(page_name || name)
|
||||
|
||||
if name =~ %r{^https?://} && page_name.nil?
|
||||
%{<a href="#{name}">#{name}</a>}
|
||||
else
|
||||
presence = "absent"
|
||||
link_name = cname
|
||||
page, extra = find_page_from_name(cname)
|
||||
if page
|
||||
link_name = Page.cname(page.name)
|
||||
link_name = @wiki.page_class.cname(page.name)
|
||||
presence = "present"
|
||||
end
|
||||
link = ::File.join(@wiki.base_path, CGI.escape(link_name))
|
||||
%{<a class="internal #{presence}" href="#{link}#{extra}">#{name}</a>}
|
||||
end
|
||||
if tag && no_follow
|
||||
tag.sub! /^<a/, '<a ref="nofollow"'
|
||||
end
|
||||
tag
|
||||
end
|
||||
|
||||
# Find the given file in the repo.
|
||||
@@ -326,7 +337,7 @@ module Gollum
|
||||
#
|
||||
# cname - The String canonical page name.
|
||||
#
|
||||
# Returns a Gollum::Page instance if a page is found, or an Array of
|
||||
# Returns a Gollum::Page instance if a page is found, or an Array of
|
||||
# [Gollum::Page, String extra] if a page without the extra anchor data
|
||||
# is found.
|
||||
def find_page_from_name(cname)
|
||||
@@ -350,28 +361,58 @@ module Gollum
|
||||
#
|
||||
# Returns the placeholder'd String data.
|
||||
def extract_code(data)
|
||||
data.gsub(/^``` ?(.+?)\r?\n(.+?)\r?\n```\r?$/m) do
|
||||
id = Digest::SHA1.hexdigest($2)
|
||||
@codemap[id] = { :lang => $1, :code => $2 }
|
||||
data.gsub!(/^``` ?([^\r\n]+)?\r?\n(.+?)\r?\n```\r?$/m) do
|
||||
id = Digest::SHA1.hexdigest("#{$1}.#{$2}")
|
||||
cached = check_cache(:code, id)
|
||||
@codemap[id] = cached ?
|
||||
{ :output => cached } :
|
||||
{ :lang => $1, :code => $2 }
|
||||
id
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
# Process all code from the codemap and replace the placeholders with the
|
||||
# final HTML.
|
||||
#
|
||||
# data - The String data (with placeholders).
|
||||
# data - The String data (with placeholders).
|
||||
# encoding - Encoding Constant or String.
|
||||
#
|
||||
# Returns the marked up String data.
|
||||
def process_code(data)
|
||||
def process_code(data, encoding = nil)
|
||||
return data if data.nil? || data.size.zero? || @codemap.size.zero?
|
||||
|
||||
blocks = []
|
||||
@codemap.each do |id, spec|
|
||||
lang = spec[:lang]
|
||||
next if spec[:output] # cached
|
||||
|
||||
code = spec[:code]
|
||||
if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ /^( |\t)/ }
|
||||
code.gsub!(/^( |\t)/m, '')
|
||||
end
|
||||
data.gsub!(id, Gollum::Albino.new(code, lang).colorize)
|
||||
|
||||
blocks << [spec[:lang], code]
|
||||
end
|
||||
|
||||
highlighted = begin
|
||||
encoding ||= 'utf-8'
|
||||
blocks.map { |lang, code| Pygments.highlight(code, :lexer => lang, :options => {:encoding => encoding.to_s}) }
|
||||
rescue ::RubyPython::PythonError
|
||||
[]
|
||||
end
|
||||
|
||||
@codemap.each do |id, spec|
|
||||
body = spec[:output] || begin
|
||||
if (body = highlighted.shift.to_s).size > 0
|
||||
update_cache(:code, id, body)
|
||||
body
|
||||
else
|
||||
"<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
|
||||
end
|
||||
end
|
||||
data.gsub!(id, body)
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
|
||||
@@ -409,5 +450,84 @@ module Gollum
|
||||
end
|
||||
data
|
||||
end
|
||||
|
||||
# Hook for getting the formatted value of extracted tag data.
|
||||
#
|
||||
# type - Symbol value identifying what type of data is being extracted.
|
||||
# id - String SHA1 hash of original extracted tag data.
|
||||
#
|
||||
# Returns the String cached formatted data, or nil.
|
||||
def check_cache(type, id)
|
||||
end
|
||||
|
||||
# Hook for caching the formatted value of extracted tag data.
|
||||
#
|
||||
# type - Symbol value identifying what type of data is being extracted.
|
||||
# id - String SHA1 hash of original extracted tag data.
|
||||
# data - The String formatted value to be cached.
|
||||
#
|
||||
# Returns nothing.
|
||||
def update_cache(type, id, data)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
require 'redcarpet'
|
||||
|
||||
class MarkupGFM < Markup
|
||||
def render(no_follow = false, encoding = nil)
|
||||
sanitize = no_follow ?
|
||||
@wiki.history_sanitizer :
|
||||
@wiki.sanitizer
|
||||
|
||||
data = extract_tex(@data.dup)
|
||||
data = extract_code(data)
|
||||
data = extract_tags(data)
|
||||
|
||||
if Gem::Version.new(Redcarpet::VERSION) > Gem::Version.new("1.17.2")
|
||||
html_renderer = Redcarpet::Render::HTML.new({
|
||||
:autolink => true,
|
||||
:fenced_code_blocks => true,
|
||||
:tables => true,
|
||||
:strikethrough => true,
|
||||
:lax_htmlblock => true,
|
||||
:no_intraemphasis => true
|
||||
})
|
||||
markdown = Redcarpet::Markdown.new(html_renderer)
|
||||
data = markdown.render(data)
|
||||
else
|
||||
flags = [
|
||||
:autolink,
|
||||
:fenced_code,
|
||||
:tables,
|
||||
:strikethrough,
|
||||
:lax_htmlblock,
|
||||
:no_intraemphasis
|
||||
]
|
||||
data = Redcarpet.new(data, *flags).to_html
|
||||
end
|
||||
data = process_tags(data)
|
||||
data = process_code(data, encoding)
|
||||
|
||||
doc = Nokogiri::HTML::DocumentFragment.parse(data)
|
||||
|
||||
doc.search('pre').each do |node|
|
||||
next unless lang = node['lang']
|
||||
next unless lexer = Pygments::Lexer[lang]
|
||||
text = node.inner_text
|
||||
html = lexer.highlight(text)
|
||||
node.replace(html)
|
||||
end
|
||||
|
||||
doc = sanitize.clean_node!(doc) if sanitize
|
||||
yield doc if block_given?
|
||||
|
||||
data = doc.to_html
|
||||
data = process_tex(data)
|
||||
data
|
||||
end
|
||||
end
|
||||
rescue LoadError
|
||||
MarkupGFM = Markup
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user