gollum-lib 4.0.3-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|