gollum-lib 4.0.3-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Gemfile +3 -0
- data/HISTORY.md +11 -0
- data/LICENSE +21 -0
- data/README.md +286 -0
- data/Rakefile +187 -0
- data/docs/sanitization.md +33 -0
- data/gemspec.rb +100 -0
- data/gollum-lib.gemspec +4 -0
- data/gollum-lib_java.gemspec +4 -0
- data/lib/gollum-lib.rb +64 -0
- data/lib/gollum-lib/blob_entry.rb +95 -0
- data/lib/gollum-lib/committer.rb +243 -0
- data/lib/gollum-lib/file.rb +158 -0
- data/lib/gollum-lib/file_view.rb +155 -0
- data/lib/gollum-lib/filter.rb +78 -0
- data/lib/gollum-lib/filter/code.rb +145 -0
- data/lib/gollum-lib/filter/macro.rb +57 -0
- data/lib/gollum-lib/filter/metadata.rb +29 -0
- data/lib/gollum-lib/filter/plain_text.rb +16 -0
- data/lib/gollum-lib/filter/remote_code.rb +63 -0
- data/lib/gollum-lib/filter/render.rb +20 -0
- data/lib/gollum-lib/filter/sanitize.rb +18 -0
- data/lib/gollum-lib/filter/tags.rb +320 -0
- data/lib/gollum-lib/filter/toc.rb +109 -0
- data/lib/gollum-lib/filter/wsd.rb +54 -0
- data/lib/gollum-lib/git_access.rb +247 -0
- data/lib/gollum-lib/gitcode.rb +48 -0
- data/lib/gollum-lib/helpers.rb +13 -0
- data/lib/gollum-lib/hook.rb +35 -0
- data/lib/gollum-lib/macro.rb +43 -0
- data/lib/gollum-lib/macro/all_pages.rb +11 -0
- data/lib/gollum-lib/markup.rb +197 -0
- data/lib/gollum-lib/markups.rb +20 -0
- data/lib/gollum-lib/page.rb +491 -0
- data/lib/gollum-lib/pagination.rb +62 -0
- data/lib/gollum-lib/sanitization.rb +176 -0
- data/lib/gollum-lib/version.rb +5 -0
- data/lib/gollum-lib/wiki.rb +925 -0
- data/licenses/licenses.txt +6 -0
- metadata +410 -0
@@ -0,0 +1,158 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Gollum
|
5
|
+
class File
|
6
|
+
Wiki.file_class = self
|
7
|
+
|
8
|
+
# Public: Initialize a file.
|
9
|
+
#
|
10
|
+
# wiki - The Gollum::Wiki in question.
|
11
|
+
#
|
12
|
+
# Returns a newly initialized Gollum::File.
|
13
|
+
def initialize(wiki)
|
14
|
+
@wiki = wiki
|
15
|
+
@blob = nil
|
16
|
+
@path = nil
|
17
|
+
@on_disk = false
|
18
|
+
@on_disk_path = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: The url path required to reach this page within the repo.
|
22
|
+
#
|
23
|
+
# Returns the String url_path
|
24
|
+
def url_path
|
25
|
+
path = self.path
|
26
|
+
path = path.sub(/\/[^\/]+$/, '/') if path.include?('/')
|
27
|
+
path
|
28
|
+
end
|
29
|
+
|
30
|
+
# Public: The url_path, but CGI escaped.
|
31
|
+
#
|
32
|
+
# Returns the String url_path
|
33
|
+
def escaped_url_path
|
34
|
+
CGI.escape(self.url_path).gsub('%2F', '/')
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: The on-disk filename of the file.
|
38
|
+
#
|
39
|
+
# Returns the String name.
|
40
|
+
def name
|
41
|
+
return @path if on_disk?
|
42
|
+
@blob && @blob.name
|
43
|
+
end
|
44
|
+
|
45
|
+
alias filename name
|
46
|
+
|
47
|
+
# Public: The raw contents of the page.
|
48
|
+
#
|
49
|
+
# Returns the String data.
|
50
|
+
def raw_data
|
51
|
+
return IO.read(@on_disk_path) if on_disk?
|
52
|
+
return nil unless @blob
|
53
|
+
|
54
|
+
if !@wiki.repo.bare && @blob.is_symlink
|
55
|
+
new_path = @blob.symlink_target(::File.join(@wiki.repo.path, '..', self.path))
|
56
|
+
return IO.read(new_path) if new_path
|
57
|
+
end
|
58
|
+
|
59
|
+
@blob.data
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Is this an on-disk file reference?
|
63
|
+
#
|
64
|
+
# Returns true if this is a pointer to an on-disk file
|
65
|
+
def on_disk?
|
66
|
+
return @on_disk
|
67
|
+
end
|
68
|
+
|
69
|
+
# Public: The path to this file on disk
|
70
|
+
#
|
71
|
+
# Returns nil if on_disk? is false.
|
72
|
+
def on_disk_path
|
73
|
+
return @on_disk_path
|
74
|
+
end
|
75
|
+
|
76
|
+
# Public: The Gollum::Git::Commit version of the file.
|
77
|
+
attr_accessor :version
|
78
|
+
|
79
|
+
# Public: The String path of the file.
|
80
|
+
attr_reader :path
|
81
|
+
|
82
|
+
# Public: The String mime type of the file.
|
83
|
+
def mime_type
|
84
|
+
@blob && @blob.mime_type
|
85
|
+
end
|
86
|
+
|
87
|
+
# Populate the File with information from the Blob.
|
88
|
+
#
|
89
|
+
# blob - The Gollum::Git::Blob that contains the info.
|
90
|
+
# path - The String directory path of the file.
|
91
|
+
#
|
92
|
+
# Returns the populated Gollum::File.
|
93
|
+
def populate(blob, path=nil)
|
94
|
+
@blob = blob
|
95
|
+
@path = "#{path}/#{blob.name}"[1..-1]
|
96
|
+
@on_disk = false
|
97
|
+
@on_disk_path = nil
|
98
|
+
self
|
99
|
+
end
|
100
|
+
|
101
|
+
#########################################################################
|
102
|
+
#
|
103
|
+
# Internal Methods
|
104
|
+
#
|
105
|
+
#########################################################################
|
106
|
+
|
107
|
+
# Return the file path to this file on disk, if available.
|
108
|
+
#
|
109
|
+
# Returns nil if the file isn't available on disk. This can occur if the
|
110
|
+
# repo is bare, if the commit isn't the HEAD, or if there are problems
|
111
|
+
# resolving symbolic links.
|
112
|
+
def get_disk_reference(name, commit)
|
113
|
+
return false if @wiki.repo.bare
|
114
|
+
return false if commit.sha != @wiki.repo.head.commit.sha
|
115
|
+
|
116
|
+
# This will try to resolve symbolic links, as well
|
117
|
+
pathname = Pathname.new(::File.expand_path(::File.join(@wiki.repo.path, '..', name)))
|
118
|
+
if pathname.symlink?
|
119
|
+
source = ::File.readlink(pathname.to_path)
|
120
|
+
realpath = ::File.join(::File.dirname(pathname.to_path), source)
|
121
|
+
return false unless realpath && ::File.exist?(realpath)
|
122
|
+
@on_disk_path = realpath.to_s
|
123
|
+
else
|
124
|
+
@on_disk_path = pathname.to_path
|
125
|
+
end
|
126
|
+
return true
|
127
|
+
end
|
128
|
+
|
129
|
+
# Find a file in the given Gollum repo.
|
130
|
+
#
|
131
|
+
# name - The full String path.
|
132
|
+
# version - The String version ID to find.
|
133
|
+
# try_on_disk - If true, try to return just a reference to a file
|
134
|
+
# that exists on the disk.
|
135
|
+
#
|
136
|
+
# Returns a Gollum::File or nil if the file could not be found. Note
|
137
|
+
# that if you specify try_on_disk=true, you may or may not get a file
|
138
|
+
# for which on_disk? is actually true.
|
139
|
+
def find(name, version, try_on_disk=false)
|
140
|
+
checked = name.downcase
|
141
|
+
map = @wiki.tree_map_for(version)
|
142
|
+
commit = version.is_a?(Gollum::Git::Commit) ? version : @wiki.commit_for(version)
|
143
|
+
|
144
|
+
if entry = map.detect { |entry| entry.path.downcase == checked }
|
145
|
+
@path = name
|
146
|
+
@version = commit
|
147
|
+
|
148
|
+
if try_on_disk && get_disk_reference(name, commit)
|
149
|
+
@on_disk = true
|
150
|
+
else
|
151
|
+
@blob = entry.blob(@wiki.repo)
|
152
|
+
end
|
153
|
+
|
154
|
+
self
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
module Gollum
|
3
|
+
=begin
|
4
|
+
FileView requires that:
|
5
|
+
- All files in root dir are processed first
|
6
|
+
- Then all the folders are sorted and processed
|
7
|
+
=end
|
8
|
+
class FileView
|
9
|
+
# common use cases:
|
10
|
+
# set pages to wiki.pages and show_all to false
|
11
|
+
# set pages to wiki.pages + wiki.files and show_all to true
|
12
|
+
def initialize pages, options = {}
|
13
|
+
@pages = pages
|
14
|
+
@show_all = options[:show_all] || false
|
15
|
+
@checked = options[:collapse_tree] ? '' : "checked"
|
16
|
+
end
|
17
|
+
|
18
|
+
def enclose_tree string
|
19
|
+
%Q(<ol class="tree">\n) + string + %Q(</ol>)
|
20
|
+
end
|
21
|
+
|
22
|
+
def new_page page
|
23
|
+
name = page.name
|
24
|
+
url = url_for_page page
|
25
|
+
%Q( <li class="file"><a href="#{url}"><span class="icon"></span>#{name}</a></li>)
|
26
|
+
end
|
27
|
+
|
28
|
+
def new_folder folder_path
|
29
|
+
new_sub_folder folder_path
|
30
|
+
end
|
31
|
+
|
32
|
+
def new_sub_folder path
|
33
|
+
<<-HTML
|
34
|
+
<li>
|
35
|
+
<label>#{path}</label> <input type="checkbox" #{@checked} />
|
36
|
+
<ol>
|
37
|
+
HTML
|
38
|
+
end
|
39
|
+
|
40
|
+
def end_folder
|
41
|
+
"</ol></li>\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def url_for_page page
|
45
|
+
url = ''
|
46
|
+
if @show_all
|
47
|
+
# Remove ext for valid pages.
|
48
|
+
filename = page.filename
|
49
|
+
filename = Page::valid_page_name?(filename) ? filename.chomp(::File.extname(filename)) : filename
|
50
|
+
|
51
|
+
url = ::File.join(::File.dirname(page.path), filename)
|
52
|
+
else
|
53
|
+
url = ::File.join(::File.dirname(page.path), page.filename_stripped)
|
54
|
+
end
|
55
|
+
url = url[2..-1] if url[0, 2] == './'
|
56
|
+
url
|
57
|
+
end
|
58
|
+
|
59
|
+
def render_files
|
60
|
+
html = ''
|
61
|
+
count = @pages.size
|
62
|
+
folder_start = -1
|
63
|
+
|
64
|
+
# Process all pages until folders start
|
65
|
+
count.times do |index|
|
66
|
+
page = @pages[index]
|
67
|
+
path = page.path
|
68
|
+
|
69
|
+
unless path.include? '/'
|
70
|
+
# Page processed (not contained in a folder)
|
71
|
+
html += new_page page
|
72
|
+
else
|
73
|
+
# Folders start at the next index
|
74
|
+
folder_start = index
|
75
|
+
break # Pages finished, move on to folders
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# If there are no folders, then we're done.
|
80
|
+
return enclose_tree(html) if folder_start <= -1
|
81
|
+
|
82
|
+
# Handle special case of only one folder.
|
83
|
+
if (count - folder_start == 1)
|
84
|
+
page = @pages[folder_start]
|
85
|
+
html += <<-HTML
|
86
|
+
<li>
|
87
|
+
<label>#{::File.dirname(page.path)}</label> <input type="checkbox" #{@checked} />
|
88
|
+
<ol>
|
89
|
+
#{new_page page}
|
90
|
+
</ol>
|
91
|
+
</li>
|
92
|
+
HTML
|
93
|
+
|
94
|
+
return enclose_tree html
|
95
|
+
end
|
96
|
+
|
97
|
+
sorted_folders = []
|
98
|
+
(folder_start).upto count - 1 do |index|
|
99
|
+
sorted_folders += [[@pages[index].path, index]]
|
100
|
+
end
|
101
|
+
|
102
|
+
# http://stackoverflow.com/questions/3482814/sorting-list-of-string-paths-in-vb-net
|
103
|
+
sorted_folders.sort! do |first, second|
|
104
|
+
a = first[0]
|
105
|
+
b = second[0]
|
106
|
+
|
107
|
+
# use :: operator because gollum defines its own conflicting File class
|
108
|
+
dir_compare = ::File.dirname(a) <=> ::File.dirname(b)
|
109
|
+
|
110
|
+
# Sort based on directory name unless they're equal (0) in
|
111
|
+
# which case sort based on file name.
|
112
|
+
if dir_compare == 0
|
113
|
+
::File.basename(a) <=> ::File.basename(b)
|
114
|
+
else
|
115
|
+
dir_compare
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# keep track of folder depth, 0 = at root.
|
120
|
+
cwd_array = []
|
121
|
+
changed = false
|
122
|
+
|
123
|
+
# process rest of folders
|
124
|
+
(0...sorted_folders.size).each do |index|
|
125
|
+
page = @pages[sorted_folders[index][1]]
|
126
|
+
path = page.path
|
127
|
+
folder = ::File.dirname path
|
128
|
+
|
129
|
+
tmp_array = folder.split '/'
|
130
|
+
|
131
|
+
(0...tmp_array.size).each do |index|
|
132
|
+
if cwd_array[index].nil? || changed
|
133
|
+
html += new_sub_folder tmp_array[index]
|
134
|
+
next
|
135
|
+
end
|
136
|
+
|
137
|
+
if cwd_array[index] != tmp_array[index]
|
138
|
+
changed = true
|
139
|
+
(cwd_array.size - index).times do
|
140
|
+
html += end_folder
|
141
|
+
end
|
142
|
+
html += new_sub_folder tmp_array[index]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
html += new_page page
|
147
|
+
cwd_array = tmp_array
|
148
|
+
changed = false
|
149
|
+
end
|
150
|
+
|
151
|
+
# return the completed html
|
152
|
+
enclose_tree html
|
153
|
+
end # end render_files
|
154
|
+
end # end FileView class
|
155
|
+
end # end Gollum module
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# A "filter", in Gollum-speak, is an object which extracts tokens from an
|
2
|
+
# input stream of an arbitrary markup language, then replaces them with a
|
3
|
+
# final form in a rendered form of the same document. Filters are composed
|
4
|
+
# into a "filter chain", which forms the order in which filters are applied
|
5
|
+
# in both the extraction and processing phases (processing happens in the
|
6
|
+
# reverse order to extraction). A single instance of a filter class is used
|
7
|
+
# for both the extraction and processing, so you can store internal state
|
8
|
+
# from the extraction phase for use in the processing phase.
|
9
|
+
#
|
10
|
+
# Any class which is to be used as a filter must have an `initialize` method
|
11
|
+
# which takes a single mandatory argument (the instance of the `Markup`
|
12
|
+
# class we're being called from), and must implement two methods: `extract`
|
13
|
+
# and `process`, both of which must take a string as input and return a
|
14
|
+
# (possibly modified) form of that string as output.
|
15
|
+
#
|
16
|
+
# An example rendering session: consider a filter chain with three filters
|
17
|
+
# in it, :A, :B, and :C (filter chains are specified as symbols, which are
|
18
|
+
# taken to be class names within the Gollum::Filter namespace). An
|
19
|
+
# equivalent of the following will take place:
|
20
|
+
#
|
21
|
+
# a = Gollum::Filter.const_get(:A).new
|
22
|
+
# b = Gollum::Filter.const_get(:B).new
|
23
|
+
# c = Gollum::Filter.const_get(:C).new
|
24
|
+
#
|
25
|
+
# data = "<some markup>"
|
26
|
+
#
|
27
|
+
# data = a.extract(data)
|
28
|
+
# data = b.extract(data)
|
29
|
+
# data = c.extract(data)
|
30
|
+
#
|
31
|
+
# data = c.process(data)
|
32
|
+
# data = b.process(data)
|
33
|
+
# data = a.process(data)
|
34
|
+
#
|
35
|
+
# # `data` now contains the rendered document, ready for processing
|
36
|
+
#
|
37
|
+
# Note how the extraction steps go in the order of the filter chain, while
|
38
|
+
# the processing steps go in the reverse order. There hasn't (yet) been a
|
39
|
+
# case where that is a huge problem.
|
40
|
+
#
|
41
|
+
# If your particular filter doesn't need to do something with either the
|
42
|
+
# original markup or rendered forms, you can simply define the relevant
|
43
|
+
# method to be a pass-through (`def extract(d) d; end`), but you *must*
|
44
|
+
# define both methods yourself.
|
45
|
+
#
|
46
|
+
module Gollum
|
47
|
+
class Filter
|
48
|
+
include Gollum::Helpers
|
49
|
+
|
50
|
+
# Setup the object. Sets `@markup` to be the instance of Gollum::Markup that
|
51
|
+
# is running this filter chain, and sets `@map` to be an empty hash (for use
|
52
|
+
# in your extract/process operations).
|
53
|
+
def initialize(markup)
|
54
|
+
@markup = markup
|
55
|
+
@map = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def extract(_d)
|
59
|
+
raise RuntimeError,
|
60
|
+
"#{self.class} has not implemented ##extract!"
|
61
|
+
end
|
62
|
+
|
63
|
+
def process(_d)
|
64
|
+
raise RuntimeError,
|
65
|
+
"#{self.class} has not implemented ##process!"
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
# Render a (presumably) non-fatal error as HTML
|
70
|
+
#
|
71
|
+
def html_error(message)
|
72
|
+
"<p class=\"gollum-error\">#{message}</p>"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Load all standard filters
|
78
|
+
Dir[File.expand_path('../filter/*.rb', __FILE__)].each { |f| require f }
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
# Code
|
4
|
+
#
|
5
|
+
# Render a block of code using the Rouge syntax-highlighter.
|
6
|
+
class Gollum::Filter::Code < Gollum::Filter
|
7
|
+
def extract(data)
|
8
|
+
case @markup.format
|
9
|
+
when :txt
|
10
|
+
return data
|
11
|
+
when :asciidoc
|
12
|
+
data.gsub!(/^(\[source,([^\r\n]*)\]\n)?----\n(.+?)\n----$/m) do
|
13
|
+
cache_codeblock($2, $3)
|
14
|
+
end
|
15
|
+
when :org
|
16
|
+
org_headers = %r{([ \t]*#\+HEADER[S]?:[^\r\n]*\n)*}
|
17
|
+
org_name = %r{([ \t]*#\+NAME:[^\r\n]*\n)?}
|
18
|
+
org_lang = %r{[ ]*([^\n \r]*)[ ]*[^\r\n]*}
|
19
|
+
org_begin = %r{[ \t]*#\+BEGIN_SRC#{org_lang}\n}
|
20
|
+
org_end = %r{\n[ \t]*#\+END_SRC[ \t]*}
|
21
|
+
data.gsub!(/^#{org_headers}#{org_name}#{org_begin}(.+?)#{org_end}$/mi) do
|
22
|
+
cache_codeblock($3, $4)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
data.gsub!(/^([ \t]*)(~~~+) ?([^\r\n]+)?\r?\n(.+?)\r?\n\1(~~~+)[ \t\r]*$/m) do
|
26
|
+
m_indent = $1
|
27
|
+
m_start = $2 # ~~~
|
28
|
+
m_lang = $3
|
29
|
+
m_code = $4
|
30
|
+
m_end = $5 # ~~~
|
31
|
+
# start and finish tilde fence must be the same length
|
32
|
+
next '' if m_start.length != m_end.length
|
33
|
+
lang = m_lang ? m_lang.strip : nil
|
34
|
+
if lang
|
35
|
+
lang = lang.match(/\.([^}\s]+)/)
|
36
|
+
lang = lang[1] unless lang.nil?
|
37
|
+
end
|
38
|
+
"#{m_indent}#{cache_codeblock(lang, m_code, m_indent)}"
|
39
|
+
end
|
40
|
+
|
41
|
+
data.gsub!(/^([ \t]*)``` ?([^\r\n]+)?\r?\n(.+?)\r?\n\1```[ \t]*\r?$/m) do
|
42
|
+
"#{$1}#{cache_codeblock($2.to_s.strip, $3, $1)}" # print the SHA1 ID with the proper indentation
|
43
|
+
end
|
44
|
+
data
|
45
|
+
end
|
46
|
+
|
47
|
+
# Process all code from the codemap and replace the placeholders with the
|
48
|
+
# final HTML.
|
49
|
+
#
|
50
|
+
# data - The String data (with placeholders).
|
51
|
+
# encoding - Encoding Constant or String.
|
52
|
+
#
|
53
|
+
# Returns the marked up String data.
|
54
|
+
def process(data)
|
55
|
+
return data if data.nil? || data.size.zero? || @map.size.zero?
|
56
|
+
|
57
|
+
blocks = []
|
58
|
+
|
59
|
+
@map.each do |id, spec|
|
60
|
+
next if spec[:output] # cached
|
61
|
+
|
62
|
+
code = spec[:code]
|
63
|
+
|
64
|
+
remove_leading_space(code, /^#{spec[:indent]}/m)
|
65
|
+
remove_leading_space(code, /^( |\t)/m)
|
66
|
+
|
67
|
+
blocks << [spec[:lang], code]
|
68
|
+
end
|
69
|
+
|
70
|
+
highlighted = []
|
71
|
+
blocks.each do |lang, code|
|
72
|
+
encoding = @markup.encoding || 'utf-8'
|
73
|
+
|
74
|
+
if defined? Pygments
|
75
|
+
# treat unknown and bash as standard pre tags
|
76
|
+
if !lang || lang.downcase == 'bash'
|
77
|
+
hl_code = "<pre>#{code}</pre>"
|
78
|
+
else
|
79
|
+
# must set startinline to true for php to be highlighted without <?
|
80
|
+
hl_code = Pygments.highlight(code, :lexer => lang, :options => { :encoding => encoding.to_s, :startinline => true })
|
81
|
+
end
|
82
|
+
else # Rouge
|
83
|
+
begin
|
84
|
+
# if `lang` was not defined then assume plaintext
|
85
|
+
# if `lang` is defined but cannot be found then wrap it and escape it
|
86
|
+
lang ||= 'plaintext'
|
87
|
+
if Rouge::Lexer.find(lang).nil?
|
88
|
+
lexer = Rouge::Lexers::PlainText.new
|
89
|
+
formatter = Rouge::Formatters::HTML.new(:wrap => false)
|
90
|
+
hl_code = formatter.format(lexer.lex(code))
|
91
|
+
hl_code = "<pre class='highlight'><span class='err'>#{CGI.escapeHTML(hl_code)}</span></pre>"
|
92
|
+
else
|
93
|
+
hl_code = Rouge.highlight(code, lang, 'html')
|
94
|
+
end
|
95
|
+
rescue
|
96
|
+
hl_code = code
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
highlighted << hl_code
|
101
|
+
end
|
102
|
+
|
103
|
+
@map.each do |id, spec|
|
104
|
+
body = spec[:output] || begin
|
105
|
+
if (body = highlighted.shift.to_s).size > 0
|
106
|
+
@markup.update_cache(:code, id, body)
|
107
|
+
body
|
108
|
+
else
|
109
|
+
"<pre><code>#{CGI.escapeHTML(spec[:code])}</code></pre>"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
# Removes paragraph tags surrounding <pre> blocks, see issue https://github.com/gollum/gollum-lib/issues/97
|
113
|
+
data.gsub!(/(<p>#{id}<\/p>|#{id})/) do
|
114
|
+
body
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
data
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
# Remove the leading space from a code block. Leading space
|
123
|
+
# is only removed if every single line in the block has leading
|
124
|
+
# whitespace.
|
125
|
+
#
|
126
|
+
# code - The code block to remove spaces from
|
127
|
+
# regex - A regex to match whitespace
|
128
|
+
def remove_leading_space(code, regex)
|
129
|
+
if code.lines.all? { |line| line =~ /\A\r?\n\Z/ || line =~ regex }
|
130
|
+
code.gsub!(regex) do
|
131
|
+
''
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def cache_codeblock(language, code, indent = "")
|
137
|
+
language = language.to_s.empty? ? nil : language
|
138
|
+
id = Digest::SHA1.hexdigest("#{language}.#{code}")
|
139
|
+
cached = @markup.check_cache(:code, id)
|
140
|
+
@map[id] = cached ?
|
141
|
+
{ :output => cached } :
|
142
|
+
{ :lang => language, :code => code, :indent => indent }
|
143
|
+
id
|
144
|
+
end
|
145
|
+
end
|