greenmat 3.2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +14 -0
  3. data/Gemfile +9 -0
  4. data/README.md +36 -0
  5. data/Rakefile +62 -0
  6. data/bin/greenmat +7 -0
  7. data/ext/greenmat/autolink.c +296 -0
  8. data/ext/greenmat/autolink.h +49 -0
  9. data/ext/greenmat/buffer.c +196 -0
  10. data/ext/greenmat/buffer.h +83 -0
  11. data/ext/greenmat/extconf.rb +6 -0
  12. data/ext/greenmat/gm_markdown.c +161 -0
  13. data/ext/greenmat/gm_render.c +534 -0
  14. data/ext/greenmat/greenmat.h +30 -0
  15. data/ext/greenmat/houdini.h +29 -0
  16. data/ext/greenmat/houdini_href_e.c +108 -0
  17. data/ext/greenmat/houdini_html_e.c +83 -0
  18. data/ext/greenmat/html.c +826 -0
  19. data/ext/greenmat/html.h +84 -0
  20. data/ext/greenmat/html_blocks.h +229 -0
  21. data/ext/greenmat/html_smartypants.c +445 -0
  22. data/ext/greenmat/markdown.c +2912 -0
  23. data/ext/greenmat/markdown.h +138 -0
  24. data/ext/greenmat/stack.c +62 -0
  25. data/ext/greenmat/stack.h +26 -0
  26. data/greenmat.gemspec +72 -0
  27. data/lib/greenmat.rb +92 -0
  28. data/lib/greenmat/compat.rb +73 -0
  29. data/lib/greenmat/render_man.rb +65 -0
  30. data/lib/greenmat/render_strip.rb +48 -0
  31. data/test/benchmark.rb +24 -0
  32. data/test/custom_render_test.rb +28 -0
  33. data/test/greenmat_compat_test.rb +38 -0
  34. data/test/html5_test.rb +69 -0
  35. data/test/html_render_test.rb +241 -0
  36. data/test/html_toc_render_test.rb +76 -0
  37. data/test/markdown_test.rb +337 -0
  38. data/test/pathological_inputs_test.rb +34 -0
  39. data/test/safe_render_test.rb +36 -0
  40. data/test/smarty_html_test.rb +45 -0
  41. data/test/smarty_pants_test.rb +48 -0
  42. data/test/stripdown_render_test.rb +40 -0
  43. data/test/test_helper.rb +33 -0
  44. metadata +158 -0
@@ -0,0 +1,65 @@
1
+ module Greenmat
2
+ module Render
3
+ class ManPage < Base
4
+
5
+ def normal_text(text)
6
+ text.gsub('-', '\\-').strip
7
+ end
8
+
9
+ def block_code(code, language)
10
+ "\n.nf\n#{normal_text(code)}\n.fi\n"
11
+ end
12
+
13
+ def codespan(code)
14
+ block_code(code, nil)
15
+ end
16
+
17
+ def header(title, level)
18
+ case level
19
+ when 1
20
+ "\n.TH #{title}\n"
21
+
22
+ when 2
23
+ "\n.SH #{title}\n"
24
+
25
+ when 3
26
+ "\n.SS #{title}\n"
27
+ end
28
+ end
29
+
30
+ def double_emphasis(text)
31
+ "\\fB#{text}\\fP"
32
+ end
33
+
34
+ def emphasis(text)
35
+ "\\fI#{text}\\fP"
36
+ end
37
+
38
+ def linebreak
39
+ "\n.LP\n"
40
+ end
41
+
42
+ def paragraph(text)
43
+ "\n.TP\n#{text}\n"
44
+ end
45
+
46
+ def list(content, list_type)
47
+ case list_type
48
+ when :ordered
49
+ "\n\n.nr step 0 1\n#{content}\n"
50
+ when :unordered
51
+ "\n.\n#{content}\n"
52
+ end
53
+ end
54
+
55
+ def list_item(content, list_type)
56
+ case list_type
57
+ when :ordered
58
+ ".IP \\n+[step]\n#{content.strip}\n"
59
+ when :unordered
60
+ ".IP \\[bu] 2 \n#{content.strip}\n"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ module Greenmat
2
+ module Render
3
+ # Markdown-stripping renderer. Turns Markdown into plaintext
4
+ # Thanks to @toupeira (Markus Koller)
5
+ class StripDown < Base
6
+ # Methods where the first argument is the text content
7
+ [
8
+ # block-level calls
9
+ :block_code, :block_quote,
10
+ :block_html, :list, :list_item,
11
+
12
+ # span-level calls
13
+ :autolink, :codespan, :double_emphasis,
14
+ :emphasis, :underline, :raw_html,
15
+ :triple_emphasis, :strikethrough,
16
+ :superscript,
17
+
18
+ # footnotes
19
+ :footnotes, :footnote_def, :footnote_ref,
20
+
21
+ # low level rendering
22
+ :entity, :normal_text
23
+ ].each do |method|
24
+ define_method method do |*args|
25
+ args.first
26
+ end
27
+ end
28
+
29
+ # Other methods where we don't return only a specific argument
30
+ def link(link, title, content)
31
+ "#{content} (#{link})"
32
+ end
33
+
34
+ def image(link, title, content)
35
+ content &&= content + " "
36
+ "#{content}#{link}"
37
+ end
38
+
39
+ def paragraph(text)
40
+ text + "\n"
41
+ end
42
+
43
+ def header(text, header_level)
44
+ text + "\n"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,24 @@
1
+ # coding: UTF-8
2
+ # Thanks Kramdown for the inspiration!
3
+ require 'benchmark/ips'
4
+
5
+ require 'greenmat'
6
+ require 'bluecloth'
7
+ require 'kramdown'
8
+
9
+ markdown = File.read(File.join(File.dirname(__FILE__), "fixtures/benchmark.md"))
10
+
11
+ # Let's bench!
12
+ Benchmark.ips do |bench|
13
+ bench.report("Greenmat") do
14
+ Greenmat::Markdown.new(Greenmat::Render::HTML).render(markdown)
15
+ end
16
+
17
+ bench.report("BlueCloth") do
18
+ BlueCloth.new(markdown).to_html
19
+ end
20
+
21
+ bench.report("Kramdown") do
22
+ Kramdown::Document.new(markdown).to_html
23
+ end
24
+ end
@@ -0,0 +1,28 @@
1
+ # coding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class CustomRenderTest < Greenmat::TestCase
5
+ class SimpleRender < Greenmat::Render::HTML
6
+ def emphasis(text)
7
+ "<em class=\"cool\">#{text}</em>"
8
+ end
9
+ end
10
+
11
+ def test_simple_overload
12
+ md = Greenmat::Markdown.new(SimpleRender)
13
+ assert_equal "<p>This is <em class=\"cool\">just</em> a test</p>\n",
14
+ md.render("This is *just* a test")
15
+ end
16
+
17
+ class NilPreprocessRenderer < Greenmat::Render::HTML
18
+ def preprocess(fulldoc)
19
+ nil
20
+ end
21
+ end
22
+
23
+ def test_preprocess_returning_nil
24
+ md = Greenmat::Markdown.new(NilPreprocessRenderer)
25
+ assert_equal(nil,md.render("Anything"))
26
+ end
27
+
28
+ end
@@ -0,0 +1,38 @@
1
+ # coding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class GreenmatCompatTest < Greenmat::TestCase
5
+ def test_simple_compat_api
6
+ html = GreenmatCompat.new("This is_just_a test").to_html
7
+ assert_equal "<p>This is<em>just</em>a test</p>\n", html
8
+ end
9
+
10
+ def test_compat_api_enables_extensions
11
+ html = GreenmatCompat.new("This is_just_a test", :no_intra_emphasis).to_html
12
+ assert_equal "<p>This is_just_a test</p>\n", html
13
+ end
14
+
15
+ def test_compat_api_knows_fenced_code_extension
16
+ text = "```ruby\nx = 'foo'\n```"
17
+ html = GreenmatCompat.new(text, :fenced_code).to_html
18
+ assert_equal "<pre><code class=\"ruby\">x = &#39;foo&#39;\n</code></pre>\n", html
19
+ end
20
+
21
+ def test_compat_api_ignores_gh_blockcode_extension
22
+ text = "```ruby\nx = 'foo'\n```"
23
+ html = GreenmatCompat.new(text, :fenced_code, :gh_blockcode).to_html
24
+ assert_equal "<pre><code class=\"ruby\">x = &#39;foo&#39;\n</code></pre>\n", html
25
+ end
26
+
27
+ def test_compat_api_knows_no_intraemphasis_extension
28
+ html = GreenmatCompat.new("This is_just_a test", :no_intraemphasis).to_html
29
+ assert_equal "<p>This is_just_a test</p>\n", html
30
+ end
31
+
32
+ def test_translate_outdated_extensions
33
+ # these extensions are no longer used
34
+ exts = [:gh_blockcode, :no_tables, :smart, :strict]
35
+ html = GreenmatCompat.new('"TEST"', *exts).to_html
36
+ assert_equal "<p>&quot;TEST&quot;</p>\n", html
37
+ end
38
+ end
@@ -0,0 +1,69 @@
1
+ require 'test_helper'
2
+
3
+ class HTML5Test < Greenmat::TestCase
4
+ def test_that_html5_works
5
+ section = <<EOS
6
+ <section>
7
+ <p>The quick brown fox jumps over the lazy dog.</p>
8
+ </section>
9
+ EOS
10
+
11
+ figure = <<EOS
12
+ <figure>
13
+ <img src="http://example.org/image.jpg" alt="">
14
+ <figcaption>
15
+ <p>Hello world!</p>
16
+ </figcaption>
17
+ </figure>
18
+ EOS
19
+
20
+ assert_renders section, section
21
+ assert_renders figure, figure
22
+ end
23
+
24
+ def test_that_html5_works_with_code_blocks
25
+ section = <<EOS
26
+ \t<section>
27
+ \t\t<p>The quick brown fox jumps over the lazy dog.</p>
28
+ \t</section>
29
+ EOS
30
+
31
+ section_expected = <<EOE
32
+ <pre><code>&lt;section&gt;
33
+ &lt;p&gt;The quick brown fox jumps over the lazy dog.&lt;/p&gt;
34
+ &lt;/section&gt;
35
+ </code></pre>
36
+ EOE
37
+
38
+ header = <<EOS
39
+ <header>
40
+ <hgroup>
41
+ <h1>Section heading</h1>
42
+ <h2>Subhead</h2>
43
+ </hgroup>
44
+ </header>
45
+ EOS
46
+
47
+ header_expected = <<EOE
48
+ <pre><code>&lt;header&gt;
49
+ &lt;hgroup&gt;
50
+ &lt;h1&gt;Section heading&lt;/h1&gt;
51
+ &lt;h2&gt;Subhead&lt;/h2&gt;
52
+ &lt;/hgroup&gt;
53
+ &lt;/header&gt;
54
+ </code></pre>
55
+ EOE
56
+
57
+ assert_renders section_expected, section
58
+ assert_renders header_expected, header
59
+ end
60
+
61
+ def test_script_tag_recognition
62
+ markdown = <<-Md
63
+ <script type="text/javascript">
64
+ alert('Foo!');
65
+ </script>
66
+ Md
67
+ assert_renders markdown, markdown
68
+ end
69
+ end
@@ -0,0 +1,241 @@
1
+ # coding: UTF-8
2
+ require 'test_helper'
3
+
4
+ class HTMLRenderTest < Greenmat::TestCase
5
+ def setup
6
+ @renderer = Greenmat::Render::HTML
7
+ end
8
+
9
+ # Hint: overrides filter_html, no_images and no_links
10
+ def test_that_escape_html_works
11
+ source = <<EOS
12
+ Through <em>NO</em> <script>DOUBLE NO</script>
13
+
14
+ <script>BAD</script>
15
+
16
+ <img src="/favicon.ico" />
17
+ EOS
18
+ expected = <<EOE
19
+ <p>Through &lt;em&gt;NO&lt;/em&gt; &lt;script&gt;DOUBLE NO&lt;/script&gt;</p>
20
+
21
+ <p>&lt;script&gt;BAD&lt;/script&gt;</p>
22
+
23
+ <p>&lt;img src=&quot;/favicon.ico&quot; /&gt;</p>
24
+ EOE
25
+
26
+ assert_equal expected, render(source, with: [:escape_html])
27
+ end
28
+
29
+ def test_that_filter_html_works
30
+ markdown = 'Through <em>NO</em> <script>DOUBLE NO</script>'
31
+ output = render(markdown, with: [:filter_html])
32
+
33
+ assert_equal "<p>Through NO DOUBLE NO</p>\n", output
34
+ end
35
+
36
+ def test_filter_html_doesnt_break_two_space_hard_break
37
+ markdown = "Lorem, \nipsum\n"
38
+ output = render(markdown, with: [:filter_html])
39
+
40
+ assert_equal "<p>Lorem,<br>\nipsum</p>\n", output
41
+ end
42
+
43
+ def test_that_no_image_flag_works
44
+ markdown = %(![dust mite](http://dust.mite/image.png) <img src="image.png" />)
45
+ output = render(markdown, with: [:no_images])
46
+
47
+ assert_no_match %r{<img}, output
48
+ end
49
+
50
+ def test_that_no_links_flag_works
51
+ markdown = %([This link](http://example.net/) <a href="links.html">links</a>)
52
+ output = render(markdown, with: [:no_links])
53
+
54
+ assert_no_match %r{<a }, output
55
+ end
56
+
57
+ def test_that_safelink_flag_works
58
+ markdown = "[IRC](irc://chat.freenode.org/#freenode)"
59
+ output = render(markdown, with: [:safe_links_only])
60
+
61
+ assert_equal "<p>[IRC](irc://chat.freenode.org/#freenode)</p>\n", output
62
+ end
63
+
64
+ def test_that_hard_wrap_works
65
+ markdown = <<EOE
66
+ Hello world,
67
+ this is just a simple test
68
+
69
+ With hard wraps
70
+ and other *things*.
71
+ EOE
72
+ output = render(markdown, with: [:hard_wrap])
73
+
74
+ assert_match %r{<br>}, output
75
+ end
76
+
77
+ def test_that_link_attributes_work
78
+ rndr = Greenmat::Render::HTML.new(:link_attributes => {:rel => 'blank'})
79
+ md = Greenmat::Markdown.new(rndr)
80
+ assert md.render('This is a [simple](http://test.com) test.').include?('rel="blank"')
81
+ end
82
+
83
+ def test_that_link_works_with_quotes
84
+ markdown = %([This'link"is](http://example.net/))
85
+ expected = %(<p><a href="http://example.net/">This&#39;link&quot;is</a></p>\n)
86
+
87
+ assert_equal expected, render(markdown)
88
+ assert_equal expected, render(markdown, with: [:escape_html])
89
+ end
90
+
91
+ def test_that_code_emphasis_work
92
+ markdown = <<-MD
93
+ This should be **`a bold codespan`**
94
+ However, this should be *`an emphasised codespan`*
95
+
96
+ * **`ABC`** or **`DEF`**
97
+ * Foo bar
98
+ MD
99
+
100
+ html = <<HTML
101
+ <p>This should be <strong><code>a bold codespan</code></strong>
102
+ However, this should be <em><code>an emphasised codespan</code></em></p>
103
+
104
+ <ul>
105
+ <li><strong><code>ABC</code></strong> or <strong><code>DEF</code></strong></li>
106
+ <li>Foo bar</li>
107
+ </ul>
108
+ HTML
109
+
110
+ assert_equal html, render(markdown)
111
+ end
112
+
113
+ def test_that_parenthesis_are_handled_into_links
114
+ markdown = %(The [bash man page](man:bash(1))!)
115
+ expected = %(<p>The <a href="man:bash(1)">bash man page</a>!</p>\n)
116
+
117
+ assert_equal expected, render(markdown)
118
+ end
119
+
120
+ def test_autolinking_works_as_expected
121
+ markdown = "Uri ftp://user:pass@example.com/. Email foo@bar.com and link http://bar.com"
122
+ output = render(markdown, with: [:autolink])
123
+
124
+ assert output.include? '<a href="ftp://user:pass@example.com/">ftp://user:pass@example.com/</a>'
125
+ assert output.include? 'mailto:foo@bar.com'
126
+ assert output.include? '<a href="http://bar.com">'
127
+ end
128
+
129
+ def test_that_footnotes_work
130
+ markdown = <<-MD
131
+ This is a footnote.[^1]
132
+
133
+ [^1]: It provides additional information.
134
+ MD
135
+
136
+ html = <<HTML
137
+ <p>This is a footnote.<sup id="fnref1"><a href="#fn1" rel="footnote">1</a></sup></p>
138
+
139
+ <div class="footnotes">
140
+ <hr>
141
+ <ol>
142
+
143
+ <li id="fn1">
144
+ <p>It provides additional information.&nbsp;<a href="#fnref1" rev="footnote">&#8617;</a></p>
145
+ </li>
146
+
147
+ </ol>
148
+ </div>
149
+ HTML
150
+
151
+ output = render(markdown, with: [:footnotes])
152
+ assert_equal html, output
153
+ end
154
+
155
+ def test_footnotes_enabled_but_missing_marker
156
+ markdown = <<MD
157
+ Some text without a marker
158
+
159
+ [^1] And a trailing definition
160
+ MD
161
+ html = <<HTML
162
+ <p>Some text without a marker</p>
163
+
164
+ <p>[^1] And a trailing definition</p>
165
+ HTML
166
+
167
+ output = render(markdown, with: [:footnotes])
168
+ assert_equal html, output
169
+ end
170
+
171
+ def test_footnotes_enabled_but_missing_definition
172
+ markdown = "Some text with a marker[^1] but no definition."
173
+ expected = "<p>Some text with a marker[^1] but no definition.</p>\n"
174
+
175
+ output = render(markdown, with: [:footnotes])
176
+ assert_equal expected, output
177
+ end
178
+
179
+ def test_autolink_short_domains
180
+ markdown = "Example of uri ftp://auto/short/domains. Email auto@l.n and link http://a/u/t/o/s/h/o/r/t"
181
+ output = render(markdown, with: [:autolink])
182
+
183
+ assert output.include? '<a href="ftp://auto/short/domains">ftp://auto/short/domains</a>'
184
+ assert output.include? 'mailto:auto@l.n'
185
+ assert output.include? '<a href="http://a/u/t/o/s/h/o/r/t">http://a/u/t/o/s/h/o/r/t</a>'
186
+ end
187
+
188
+ def test_that_prettify_works
189
+ markdown = "\tclass Foo\nend"
190
+ output = render(markdown, with: [:prettify])
191
+
192
+ assert output.include?("<pre><code class=\"prettyprint\">")
193
+
194
+ markdown = "`class`"
195
+ output = render(markdown, with: [:prettify])
196
+
197
+ assert output.include?("<code class=\"prettyprint\">")
198
+ end
199
+
200
+ def test_prettify_with_fenced_code_blocks
201
+ markdown = "~~~ruby\ncode\n~~~"
202
+ output = render(markdown, with: [:fenced_code_blocks, :prettify])
203
+
204
+ assert output.include?("<code class=\"prettyprint lang-ruby\">")
205
+ end
206
+
207
+ def test_safe_links_only_with_anchors
208
+ markdown = "An [anchor link](#anchor) on a page."
209
+ output = render(markdown, with: [:safe_links_only])
210
+
211
+ assert_match %r{<a href="#anchor">anchor link</a>}, output
212
+ end
213
+
214
+ def test_autolink_with_link_attributes
215
+ options = { autolink: true, link_attributes: {rel: "nofollow"} }
216
+ output = render("https://github.com/", with: options)
217
+
218
+ assert_match %r{rel="nofollow"}, output
219
+ end
220
+
221
+ def test_image_unsafe_src_with_safe_links_only
222
+ markdown = "![foo](javascript:while(1);)"
223
+ output = render(markdown, with: [:safe_links_only])
224
+
225
+ assert_not_match %r{img src}, output
226
+ end
227
+
228
+ def test_no_styles_option_inside_a_paragraph
229
+ markdown = "Hello <style> foo { bar: baz; } </style> !"
230
+ output = render(markdown, with: [:no_styles])
231
+
232
+ assert_no_match %r{<style>}, output
233
+ end
234
+
235
+ def test_no_styles_inside_html_block_rendering
236
+ markdown = "<style> foo { bar: baz; } </style>"
237
+ output = render(markdown, with: [:no_styles])
238
+
239
+ assert_no_match %r{<style>}, output
240
+ end
241
+ end