gitlab-markup 1.5.0.pre
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 +7 -0
- data/.gitignore +5 -0
- data/.gitlab-ci.yml +27 -0
- data/.kick +26 -0
- data/.travis.yml +21 -0
- data/CONTRIBUTING.md +49 -0
- data/Gemfile +13 -0
- data/HISTORY.md +128 -0
- data/LICENSE +20 -0
- data/README.md +58 -0
- data/Rakefile +17 -0
- data/bin/github-markup +10 -0
- data/gitlab-markup.gemspec +25 -0
- data/lib/github-markup.rb +6 -0
- data/lib/github/commands/rest2html +200 -0
- data/lib/github/markup.rb +55 -0
- data/lib/github/markup/command_implementation.rb +71 -0
- data/lib/github/markup/gem_implementation.rb +30 -0
- data/lib/github/markup/implementation.rb +28 -0
- data/lib/github/markup/markdown.rb +60 -0
- data/lib/github/markup/rdoc.rb +26 -0
- data/lib/github/markups.rb +50 -0
- data/script/bootstrap +8 -0
- data/script/cibuild +20 -0
- data/test/fixtures/fail.sh +3 -0
- data/test/markup_test.rb +116 -0
- data/test/markups/README.asciidoc +23 -0
- data/test/markups/README.asciidoc.html +59 -0
- data/test/markups/README.creole +34 -0
- data/test/markups/README.creole.html +20 -0
- data/test/markups/README.litcoffee +59 -0
- data/test/markups/README.litcoffee.html +66 -0
- data/test/markups/README.markdown +2 -0
- data/test/markups/README.markdown.html +4 -0
- data/test/markups/README.mediawiki +30 -0
- data/test/markups/README.mediawiki.html +60 -0
- data/test/markups/README.noformat +2 -0
- data/test/markups/README.noformat.html +2 -0
- data/test/markups/README.org +131 -0
- data/test/markups/README.org.html +139 -0
- data/test/markups/README.pod +88 -0
- data/test/markups/README.pod.html +85 -0
- data/test/markups/README.rdoc +6 -0
- data/test/markups/README.rdoc.html +12 -0
- data/test/markups/README.rmd +3 -0
- data/test/markups/README.rmd.html +6 -0
- data/test/markups/README.rst +79 -0
- data/test/markups/README.rst.html +91 -0
- data/test/markups/README.rst.txt +21 -0
- data/test/markups/README.rst.txt.html +37 -0
- data/test/markups/README.textile +2 -0
- data/test/markups/README.textile.html +4 -0
- data/test/markups/README.toc.rst +30 -0
- data/test/markups/README.toc.rst.html +32 -0
- data/test/markups/README.txt +2 -0
- data/test/markups/README.txt.html +2 -0
- metadata +216 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
require "github/markup/command_implementation"
|
2
|
+
require "github/markup/gem_implementation"
|
3
|
+
|
4
|
+
module GitHub
|
5
|
+
module Markup
|
6
|
+
extend self
|
7
|
+
@@markups = []
|
8
|
+
|
9
|
+
def markups
|
10
|
+
@@markups
|
11
|
+
end
|
12
|
+
|
13
|
+
def preload!
|
14
|
+
markups.each do |markup|
|
15
|
+
markup.load
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def render(filename, content = nil)
|
20
|
+
content ||= File.read(filename)
|
21
|
+
|
22
|
+
if impl = renderer(filename)
|
23
|
+
impl.render(content)
|
24
|
+
else
|
25
|
+
content
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def markup(file, pattern, opts = {}, &block)
|
30
|
+
markups << GemImplementation.new(pattern, file, &block)
|
31
|
+
end
|
32
|
+
|
33
|
+
def command(command, regexp, name, &block)
|
34
|
+
if File.exist?(file = File.dirname(__FILE__) + "/commands/#{command}")
|
35
|
+
command = file
|
36
|
+
end
|
37
|
+
|
38
|
+
markups << CommandImplementation.new(regexp, command, name, &block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def can_render?(filename)
|
42
|
+
!!renderer(filename)
|
43
|
+
end
|
44
|
+
|
45
|
+
def renderer(filename)
|
46
|
+
markups.find { |impl|
|
47
|
+
impl.match?(filename)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Define markups
|
52
|
+
markups_rb = File.dirname(__FILE__) + '/markups.rb'
|
53
|
+
instance_eval File.read(markups_rb), markups_rb
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
begin
|
2
|
+
require "posix-spawn"
|
3
|
+
rescue LoadError
|
4
|
+
require "open3"
|
5
|
+
end
|
6
|
+
|
7
|
+
require "github/markup/implementation"
|
8
|
+
|
9
|
+
module GitHub
|
10
|
+
module Markup
|
11
|
+
class CommandError < RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
class CommandImplementation < Implementation
|
15
|
+
attr_reader :command, :block, :name
|
16
|
+
|
17
|
+
def initialize(regexp, command, name, &block)
|
18
|
+
super regexp
|
19
|
+
@command = command.to_s
|
20
|
+
@block = block
|
21
|
+
@name = name
|
22
|
+
end
|
23
|
+
|
24
|
+
def render(content)
|
25
|
+
rendered = execute(command, content)
|
26
|
+
rendered = rendered.to_s.empty? ? content : rendered
|
27
|
+
call_block(rendered, content)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def call_block(rendered, content)
|
32
|
+
if block && block.arity == 2
|
33
|
+
block.call(rendered, content)
|
34
|
+
elsif block
|
35
|
+
block.call(rendered)
|
36
|
+
else
|
37
|
+
rendered
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if defined?(POSIX::Spawn)
|
42
|
+
def execute(command, target)
|
43
|
+
spawn = POSIX::Spawn::Child.new(*command, :input => target)
|
44
|
+
if spawn.status.success?
|
45
|
+
sanitize(spawn.out, target.encoding)
|
46
|
+
else
|
47
|
+
raise CommandError.new(spawn.err.strip)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
def execute(command, target)
|
52
|
+
output = Open3.popen3(*command) do |stdin, stdout, stderr, wait_thr|
|
53
|
+
stdin.puts target
|
54
|
+
stdin.close
|
55
|
+
if wait_thr.value.success?
|
56
|
+
stdout.readlines
|
57
|
+
else
|
58
|
+
raise CommandError.new(stderr.readlines.join('').strip)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
sanitize(output.join(''), target.encoding)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def sanitize(input, encoding)
|
66
|
+
input.gsub("\r", '').force_encoding(encoding)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "github/markup/implementation"
|
2
|
+
|
3
|
+
module GitHub
|
4
|
+
module Markup
|
5
|
+
class GemImplementation < Implementation
|
6
|
+
attr_reader :gem_name, :renderer
|
7
|
+
|
8
|
+
def initialize(regexp, gem_name, &renderer)
|
9
|
+
super regexp
|
10
|
+
@gem_name = gem_name.to_s
|
11
|
+
@renderer = renderer
|
12
|
+
end
|
13
|
+
|
14
|
+
def load
|
15
|
+
return if @loaded
|
16
|
+
require gem_name
|
17
|
+
@loaded = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def render(content)
|
21
|
+
load
|
22
|
+
renderer.call(content)
|
23
|
+
end
|
24
|
+
|
25
|
+
def name
|
26
|
+
gem_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module GitHub
|
2
|
+
module Markup
|
3
|
+
class Implementation
|
4
|
+
attr_reader :regexp
|
5
|
+
|
6
|
+
def initialize(regexp)
|
7
|
+
@regexp = regexp
|
8
|
+
end
|
9
|
+
|
10
|
+
def load
|
11
|
+
# no-op by default
|
12
|
+
end
|
13
|
+
|
14
|
+
def render(content)
|
15
|
+
raise NotImplementedError, "subclasses of GitHub::Markup::Implementation must define #render"
|
16
|
+
end
|
17
|
+
|
18
|
+
def match?(filename)
|
19
|
+
file_ext_regexp =~ filename
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def file_ext_regexp
|
24
|
+
@file_ext_regexp ||= /\.(#{regexp})\z/
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "github/markup/implementation"
|
2
|
+
|
3
|
+
module GitHub
|
4
|
+
module Markup
|
5
|
+
class Markdown < Implementation
|
6
|
+
MARKDOWN_GEMS = {
|
7
|
+
"github/markdown" => proc { |content|
|
8
|
+
GitHub::Markdown.render(content)
|
9
|
+
},
|
10
|
+
"redcarpet" => proc { |content|
|
11
|
+
Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(content)
|
12
|
+
},
|
13
|
+
"rdiscount" => proc { |content|
|
14
|
+
RDiscount.new(content).to_html
|
15
|
+
},
|
16
|
+
"maruku" => proc { |content|
|
17
|
+
Maruku.new(content).to_html
|
18
|
+
},
|
19
|
+
"kramdown" => proc { |content|
|
20
|
+
Kramdown::Document.new(content).to_html
|
21
|
+
},
|
22
|
+
"bluecloth" => proc { |content|
|
23
|
+
BlueCloth.new(content).to_html
|
24
|
+
},
|
25
|
+
}
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
super(/md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee/i)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load
|
32
|
+
return if @renderer
|
33
|
+
MARKDOWN_GEMS.each do |gem_name, renderer|
|
34
|
+
if try_require(gem_name)
|
35
|
+
@renderer = renderer
|
36
|
+
return
|
37
|
+
end
|
38
|
+
end
|
39
|
+
raise LoadError, "no suitable markdown gem found"
|
40
|
+
end
|
41
|
+
|
42
|
+
def render(content)
|
43
|
+
load
|
44
|
+
@renderer.call(content)
|
45
|
+
end
|
46
|
+
|
47
|
+
def name
|
48
|
+
"markdown"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def try_require(file)
|
53
|
+
require file
|
54
|
+
true
|
55
|
+
rescue LoadError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "github/markup/implementation"
|
2
|
+
require "rdoc"
|
3
|
+
require "rdoc/markup/to_html"
|
4
|
+
|
5
|
+
module GitHub
|
6
|
+
module Markup
|
7
|
+
class RDoc < Implementation
|
8
|
+
def initialize
|
9
|
+
super /rdoc/
|
10
|
+
end
|
11
|
+
|
12
|
+
def render(content)
|
13
|
+
if ::RDoc::VERSION.to_i >= 4
|
14
|
+
h = ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
|
15
|
+
else
|
16
|
+
h = ::RDoc::Markup::ToHtml.new
|
17
|
+
end
|
18
|
+
h.convert(content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
"rdoc"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "github/markup/markdown"
|
2
|
+
require "github/markup/rdoc"
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
markups << GitHub::Markup::Markdown.new
|
6
|
+
|
7
|
+
markup(:redcloth, /textile/) do |content|
|
8
|
+
RedCloth.new(content).to_html
|
9
|
+
end
|
10
|
+
|
11
|
+
markups << GitHub::Markup::RDoc.new
|
12
|
+
|
13
|
+
markup('org-ruby', /org/) do |content|
|
14
|
+
Orgmode::Parser.new(content, {
|
15
|
+
:allow_include_files => false,
|
16
|
+
:skip_syntax_highlight => true
|
17
|
+
}).to_html
|
18
|
+
end
|
19
|
+
|
20
|
+
markup(:creole, /creole/) do |content|
|
21
|
+
Creole.creolize(content)
|
22
|
+
end
|
23
|
+
|
24
|
+
markup(:wikicloth, /mediawiki|wiki/) do |content|
|
25
|
+
wikicloth = WikiCloth::WikiCloth.new(:data => content)
|
26
|
+
WikiCloth::WikiBuffer::HTMLElement::ESCAPED_TAGS << 'tt'
|
27
|
+
wikicloth.to_html(:noedit => true)
|
28
|
+
end
|
29
|
+
|
30
|
+
markup(:asciidoctor, /adoc|asc(iidoc)?/) do |content|
|
31
|
+
Asciidoctor::Compliance.unique_id_start_index = 1
|
32
|
+
Asciidoctor.convert(content, :safe => :secure, :attributes => %w(showtitle=@ idprefix idseparator=- env=github env-github source-highlighter=html-pipeline))
|
33
|
+
end
|
34
|
+
|
35
|
+
command(
|
36
|
+
"python2 -S #{Shellwords.escape(File.dirname(__FILE__))}/commands/rest2html",
|
37
|
+
/re?st(\.txt)?/,
|
38
|
+
"restructuredtext"
|
39
|
+
)
|
40
|
+
|
41
|
+
# pod2html is nice enough to generate a full-on HTML document for us,
|
42
|
+
# so we return the favor by ripping out the good parts.
|
43
|
+
#
|
44
|
+
# Any block passed to `command` will be handed the command's STDOUT for
|
45
|
+
# post processing.
|
46
|
+
command('/usr/bin/env perl -MPod::Simple::HTML -e Pod::Simple::HTML::go', /pod/, "pod") do |rendered|
|
47
|
+
if rendered =~ /<!-- start doc -->\s*(.+)\s*<!-- end doc -->/mi
|
48
|
+
$1
|
49
|
+
end
|
50
|
+
end
|
data/script/bootstrap
ADDED
data/script/cibuild
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
# GC customizations
|
6
|
+
export RUBY_GC_MALLOC_LIMIT=79000000
|
7
|
+
export RUBY_HEAP_MIN_SLOTS=800000
|
8
|
+
export RUBY_HEAP_FREE_MIN=100000
|
9
|
+
export RUBY_HEAP_SLOTS_INCREMENT=400000
|
10
|
+
export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
|
11
|
+
|
12
|
+
export PATH="/usr/share/rbenv/shims:$PATH"
|
13
|
+
export RBENV_VERSION="1.9.3"
|
14
|
+
|
15
|
+
# bootstrap gem environment changes
|
16
|
+
echo "Bootstrapping gem environment ..."
|
17
|
+
|
18
|
+
script/bootstrap --local
|
19
|
+
|
20
|
+
rake
|
data/test/markup_test.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
4
|
+
|
5
|
+
require 'github/markup'
|
6
|
+
require 'minitest/autorun'
|
7
|
+
require 'html/pipeline'
|
8
|
+
require 'nokogiri'
|
9
|
+
require 'nokogiri/diff'
|
10
|
+
|
11
|
+
def normalize_html(text)
|
12
|
+
text.strip!
|
13
|
+
text.gsub!(/\s\s+/,' ')
|
14
|
+
text.gsub!(/\p{Pi}|\p{Pf}|&quot;/u,'"')
|
15
|
+
text.gsub!("\u2026",'...')
|
16
|
+
text
|
17
|
+
end
|
18
|
+
|
19
|
+
def assert_html_equal(expected, actual, msg = nil)
|
20
|
+
assertion = Proc.new do
|
21
|
+
expected_doc = Nokogiri::HTML(expected) {|config| config.noblanks}
|
22
|
+
actual_doc = Nokogiri::HTML(actual) {|config| config.noblanks}
|
23
|
+
|
24
|
+
expected_doc.search('//text()').each {|node| node.content = normalize_html node.content}
|
25
|
+
actual_doc.search('//text()').each {|node| node.content = normalize_html node.content}
|
26
|
+
|
27
|
+
ignore_changes = {"+" => Regexp.union(/^\s*id=".*"\s*$/), "-" => nil}
|
28
|
+
expected_doc.diff(actual_doc) do |change, node|
|
29
|
+
if change != ' ' && !node.blank? then
|
30
|
+
break unless node.to_html =~ ignore_changes[change]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
assert(assertion.call, msg)
|
35
|
+
end
|
36
|
+
|
37
|
+
class MarkupTest < Minitest::Test
|
38
|
+
class MarkupFilter < HTML::Pipeline::Filter
|
39
|
+
def call
|
40
|
+
filename = context[:filename]
|
41
|
+
GitHub::Markup.render(filename, File.read(filename)).strip.force_encoding("utf-8")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Pipeline = HTML::Pipeline.new [
|
46
|
+
MarkupFilter,
|
47
|
+
HTML::Pipeline::SanitizationFilter
|
48
|
+
]
|
49
|
+
|
50
|
+
Dir['test/markups/README.*'].each do |readme|
|
51
|
+
next if readme =~ /html$/
|
52
|
+
markup = readme.split('/').last.gsub(/^README\./, '')
|
53
|
+
|
54
|
+
define_method "test_#{markup}" do
|
55
|
+
skip "Skipping MediaWiki test because wikicloth is currently not compatible with JRuby." if markup == "mediawiki" && RUBY_PLATFORM == "java"
|
56
|
+
|
57
|
+
source = File.read(readme)
|
58
|
+
expected_file = "#{readme}.html"
|
59
|
+
expected = File.read(expected_file).rstrip
|
60
|
+
actual = Pipeline.to_html(nil, :filename => readme)
|
61
|
+
|
62
|
+
if source != expected
|
63
|
+
assert(source != actual, "#{markup} did not render anything")
|
64
|
+
end
|
65
|
+
|
66
|
+
diff = IO.popen("diff -u - #{expected_file}", 'r+') do |f|
|
67
|
+
f.write actual
|
68
|
+
f.close_write
|
69
|
+
f.read
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_html_equal expected, actual, <<message
|
73
|
+
#{File.basename expected_file}'s contents are not html equal to output:
|
74
|
+
#{diff}
|
75
|
+
message
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_knows_what_it_can_and_cannot_render
|
80
|
+
assert_equal false, GitHub::Markup.can_render?('README.html')
|
81
|
+
assert_equal true, GitHub::Markup.can_render?('README.markdown')
|
82
|
+
assert_equal true, GitHub::Markup.can_render?('README.rmd')
|
83
|
+
assert_equal true, GitHub::Markup.can_render?('README.Rmd')
|
84
|
+
assert_equal false, GitHub::Markup.can_render?('README.cmd')
|
85
|
+
assert_equal true, GitHub::Markup.can_render?('README.litcoffee')
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_each_render_has_a_name
|
89
|
+
assert_equal "markdown", GitHub::Markup.renderer('README.md').name
|
90
|
+
assert_equal "redcloth", GitHub::Markup.renderer('README.textile').name
|
91
|
+
assert_equal "rdoc", GitHub::Markup.renderer('README.rdoc').name
|
92
|
+
assert_equal "org-ruby", GitHub::Markup.renderer('README.org').name
|
93
|
+
assert_equal "creole", GitHub::Markup.renderer('README.creole').name
|
94
|
+
assert_equal "wikicloth", GitHub::Markup.renderer('README.wiki').name
|
95
|
+
assert_equal "asciidoctor", GitHub::Markup.renderer('README.adoc').name
|
96
|
+
assert_equal "restructuredtext", GitHub::Markup.renderer('README.rst').name
|
97
|
+
assert_equal "pod", GitHub::Markup.renderer('README.pod').name
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_raises_error_if_command_exits_non_zero
|
101
|
+
GitHub::Markup.command('test/fixtures/fail.sh', /fail/, 'fail')
|
102
|
+
assert GitHub::Markup.can_render?('README.fail')
|
103
|
+
begin
|
104
|
+
GitHub::Markup.render('README.fail', "stop swallowing errors")
|
105
|
+
rescue GitHub::Markup::CommandError => e
|
106
|
+
assert_equal "failure message", e.message
|
107
|
+
else
|
108
|
+
fail "an exception was expected but was not raised"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_preserve_markup
|
113
|
+
content = "Noël"
|
114
|
+
assert_equal content.encoding.name, GitHub::Markup.render('Foo.rst', content).encoding.name
|
115
|
+
end
|
116
|
+
end
|