vkhater-redcarpet 2.2.3
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.
- data/COPYING +14 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +20 -0
- data/README.markdown +335 -0
- data/Rakefile +131 -0
- data/bin/redcarpet +38 -0
- data/ext/redcarpet/autolink.c +296 -0
- data/ext/redcarpet/autolink.h +51 -0
- data/ext/redcarpet/buffer.c +225 -0
- data/ext/redcarpet/buffer.h +96 -0
- data/ext/redcarpet/extconf.rb +4 -0
- data/ext/redcarpet/houdini.h +37 -0
- data/ext/redcarpet/houdini_href_e.c +108 -0
- data/ext/redcarpet/houdini_html_e.c +84 -0
- data/ext/redcarpet/html.c +635 -0
- data/ext/redcarpet/html.h +77 -0
- data/ext/redcarpet/html_blocks.h +206 -0
- data/ext/redcarpet/html_smartypants.c +389 -0
- data/ext/redcarpet/markdown.c +2556 -0
- data/ext/redcarpet/markdown.h +138 -0
- data/ext/redcarpet/rc_markdown.c +149 -0
- data/ext/redcarpet/rc_render.c +469 -0
- data/ext/redcarpet/redcarpet.h +37 -0
- data/ext/redcarpet/stack.c +81 -0
- data/ext/redcarpet/stack.h +29 -0
- data/lib/redcarpet.rb +124 -0
- data/lib/redcarpet/compat.rb +3 -0
- data/lib/redcarpet/render_man.rb +65 -0
- data/lib/redcarpet/render_strip.rb +33 -0
- data/redcarpet.gemspec +54 -0
- data/test/redcarpet_test.rb +447 -0
- metadata +102 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
#ifndef REDCARPET_H__
|
2
|
+
#define REDCARPET_H__
|
3
|
+
|
4
|
+
#define RSTRING_NOT_MODIFIED
|
5
|
+
#include "ruby.h"
|
6
|
+
#include <stdio.h>
|
7
|
+
|
8
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
9
|
+
# include <ruby/encoding.h>
|
10
|
+
# define redcarpet_str_new(data, size, enc) rb_enc_str_new(data, size, enc)
|
11
|
+
#else
|
12
|
+
# define redcarpet_str_new(data, size, enc) rb_str_new(data, size)
|
13
|
+
#endif
|
14
|
+
|
15
|
+
#include "markdown.h"
|
16
|
+
#include "html.h"
|
17
|
+
|
18
|
+
#define CSTR2SYM(s) (ID2SYM(rb_intern((s))))
|
19
|
+
|
20
|
+
extern void Init_redcarpet_rndr();
|
21
|
+
|
22
|
+
struct redcarpet_renderopt {
|
23
|
+
struct html_renderopt html;
|
24
|
+
VALUE link_attributes;
|
25
|
+
VALUE self;
|
26
|
+
VALUE base_class;
|
27
|
+
#ifdef HAVE_RUBY_ENCODING_H
|
28
|
+
rb_encoding *active_enc;
|
29
|
+
#endif
|
30
|
+
};
|
31
|
+
|
32
|
+
struct rb_redcarpet_rndr {
|
33
|
+
struct sd_callbacks callbacks;
|
34
|
+
struct redcarpet_renderopt options;
|
35
|
+
};
|
36
|
+
|
37
|
+
#endif
|
@@ -0,0 +1,81 @@
|
|
1
|
+
#include "stack.h"
|
2
|
+
#include <string.h>
|
3
|
+
|
4
|
+
int
|
5
|
+
stack_grow(struct stack *st, size_t new_size)
|
6
|
+
{
|
7
|
+
void **new_st;
|
8
|
+
|
9
|
+
if (st->asize >= new_size)
|
10
|
+
return 0;
|
11
|
+
|
12
|
+
new_st = realloc(st->item, new_size * sizeof(void *));
|
13
|
+
if (new_st == NULL)
|
14
|
+
return -1;
|
15
|
+
|
16
|
+
memset(new_st + st->asize, 0x0,
|
17
|
+
(new_size - st->asize) * sizeof(void *));
|
18
|
+
|
19
|
+
st->item = new_st;
|
20
|
+
st->asize = new_size;
|
21
|
+
|
22
|
+
if (st->size > new_size)
|
23
|
+
st->size = new_size;
|
24
|
+
|
25
|
+
return 0;
|
26
|
+
}
|
27
|
+
|
28
|
+
void
|
29
|
+
stack_free(struct stack *st)
|
30
|
+
{
|
31
|
+
if (!st)
|
32
|
+
return;
|
33
|
+
|
34
|
+
free(st->item);
|
35
|
+
|
36
|
+
st->item = NULL;
|
37
|
+
st->size = 0;
|
38
|
+
st->asize = 0;
|
39
|
+
}
|
40
|
+
|
41
|
+
int
|
42
|
+
stack_init(struct stack *st, size_t initial_size)
|
43
|
+
{
|
44
|
+
st->item = NULL;
|
45
|
+
st->size = 0;
|
46
|
+
st->asize = 0;
|
47
|
+
|
48
|
+
if (!initial_size)
|
49
|
+
initial_size = 8;
|
50
|
+
|
51
|
+
return stack_grow(st, initial_size);
|
52
|
+
}
|
53
|
+
|
54
|
+
void *
|
55
|
+
stack_pop(struct stack *st)
|
56
|
+
{
|
57
|
+
if (!st->size)
|
58
|
+
return NULL;
|
59
|
+
|
60
|
+
return st->item[--st->size];
|
61
|
+
}
|
62
|
+
|
63
|
+
int
|
64
|
+
stack_push(struct stack *st, void *item)
|
65
|
+
{
|
66
|
+
if (stack_grow(st, st->size * 2) < 0)
|
67
|
+
return -1;
|
68
|
+
|
69
|
+
st->item[st->size++] = item;
|
70
|
+
return 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
void *
|
74
|
+
stack_top(struct stack *st)
|
75
|
+
{
|
76
|
+
if (!st->size)
|
77
|
+
return NULL;
|
78
|
+
|
79
|
+
return st->item[st->size - 1];
|
80
|
+
}
|
81
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#ifndef STACK_H__
|
2
|
+
#define STACK_H__
|
3
|
+
|
4
|
+
#include <stdlib.h>
|
5
|
+
|
6
|
+
#ifdef __cplusplus
|
7
|
+
extern "C" {
|
8
|
+
#endif
|
9
|
+
|
10
|
+
struct stack {
|
11
|
+
void **item;
|
12
|
+
size_t size;
|
13
|
+
size_t asize;
|
14
|
+
};
|
15
|
+
|
16
|
+
void stack_free(struct stack *);
|
17
|
+
int stack_grow(struct stack *, size_t);
|
18
|
+
int stack_init(struct stack *, size_t);
|
19
|
+
|
20
|
+
int stack_push(struct stack *, void *);
|
21
|
+
|
22
|
+
void *stack_pop(struct stack *);
|
23
|
+
void *stack_top(struct stack *);
|
24
|
+
|
25
|
+
#ifdef __cplusplus
|
26
|
+
}
|
27
|
+
#endif
|
28
|
+
|
29
|
+
#endif
|
data/lib/redcarpet.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'redcarpet.so'
|
2
|
+
|
3
|
+
module Redcarpet
|
4
|
+
VERSION = '2.2.2'
|
5
|
+
|
6
|
+
class Markdown
|
7
|
+
attr_reader :renderer
|
8
|
+
end
|
9
|
+
|
10
|
+
module Render
|
11
|
+
|
12
|
+
# XHTML Renderer
|
13
|
+
class XHTML < HTML
|
14
|
+
def initialize(extensions={})
|
15
|
+
super(extensions.merge(:xhtml => true))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# HTML + SmartyPants renderer
|
20
|
+
class SmartyHTML < HTML
|
21
|
+
include SmartyPants
|
22
|
+
end
|
23
|
+
|
24
|
+
# SmartyPants Mixin module
|
25
|
+
#
|
26
|
+
# Implements SmartyPants.postprocess, which
|
27
|
+
# performs smartypants replacements on the HTML file,
|
28
|
+
# once it has been fully rendered.
|
29
|
+
#
|
30
|
+
# To add SmartyPants postprocessing to your custom
|
31
|
+
# renderers, just mixin the module `include SmartyPants`
|
32
|
+
#
|
33
|
+
# You can also use this as a standalone SmartyPants
|
34
|
+
# implementation.
|
35
|
+
#
|
36
|
+
# Example:
|
37
|
+
#
|
38
|
+
# # Mixin
|
39
|
+
# class CoolRenderer < HTML
|
40
|
+
# include SmartyPants
|
41
|
+
# # more code here
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # Standalone
|
45
|
+
# Redcarpet::Render::SmartyPants.render("you're")
|
46
|
+
#
|
47
|
+
module SmartyPants
|
48
|
+
extend self
|
49
|
+
def self.render(text)
|
50
|
+
postprocess text
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Compatibility class;
|
57
|
+
# Creates an instance of Redcarpet with the RedCloth API.
|
58
|
+
class RedcarpetCompat
|
59
|
+
attr_accessor :text
|
60
|
+
|
61
|
+
def initialize(text, *exts)
|
62
|
+
exts_hash, render_hash = *parse_extensions_and_renderer_options(exts)
|
63
|
+
@text = text
|
64
|
+
renderer = Redcarpet::Render::HTML.new(render_hash)
|
65
|
+
@markdown = Redcarpet::Markdown.new(renderer, exts_hash)
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_html(*_dummy)
|
69
|
+
@markdown.render(@text)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
EXTENSION_MAP = {
|
75
|
+
# old name => new name
|
76
|
+
:autolink => :autolink,
|
77
|
+
:fenced_code => :fenced_code_blocks,
|
78
|
+
:filter_html => :filter_html,
|
79
|
+
:hard_wrap => :hard_wrap,
|
80
|
+
:lax_htmlblock => :lax_spacing,
|
81
|
+
:no_image => :no_images,
|
82
|
+
:no_intraemphasis => :no_intra_emphasis,
|
83
|
+
:no_links => :no_links,
|
84
|
+
:filter_styles => :no_styles,
|
85
|
+
:safelink => :safe_links_only,
|
86
|
+
:space_header => :space_after_headers,
|
87
|
+
:strikethrough => :strikethrough,
|
88
|
+
:tables => :tables,
|
89
|
+
:generate_toc => :with_toc_data,
|
90
|
+
:xhtml => :xhtml,
|
91
|
+
# old names with no new mapping
|
92
|
+
:gh_blockcode => nil,
|
93
|
+
:no_tables => nil,
|
94
|
+
:smart => nil,
|
95
|
+
:strict => nil
|
96
|
+
}
|
97
|
+
|
98
|
+
RENDERER_OPTIONS = [:filter_html, :no_images, :no_links, :no_styles,
|
99
|
+
:safe_links_only, :with_toc_data, :hard_wrap, :xhtml]
|
100
|
+
|
101
|
+
def rename_extensions(exts)
|
102
|
+
exts.map do |old_name|
|
103
|
+
if new_name = EXTENSION_MAP[old_name]
|
104
|
+
new_name
|
105
|
+
else
|
106
|
+
old_name
|
107
|
+
end
|
108
|
+
end.compact
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns two hashes, the extensions and renderer options
|
112
|
+
# given the extension list
|
113
|
+
def parse_extensions_and_renderer_options(exts)
|
114
|
+
exts = rename_extensions(exts)
|
115
|
+
exts.partition {|ext| !RENDERER_OPTIONS.include?(ext) }.
|
116
|
+
map {|list| list_to_truthy_hash(list) }
|
117
|
+
end
|
118
|
+
|
119
|
+
# Turns a list of symbols into a hash of <tt>symbol => true</tt>.
|
120
|
+
def list_to_truthy_hash(list)
|
121
|
+
list.inject({}) {|h, k| h[k] = true; h }
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Redcarpet
|
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,33 @@
|
|
1
|
+
|
2
|
+
module Redcarpet
|
3
|
+
module Render
|
4
|
+
# Markdown-stripping renderer. Turns Markdown into plaintext
|
5
|
+
# Thanks to @toupeira (Markus Koller)
|
6
|
+
class StripDown < Base
|
7
|
+
# Methods where the first argument is the text content
|
8
|
+
[
|
9
|
+
# block-level calls
|
10
|
+
:block_code, :block_quote,
|
11
|
+
:block_html, :header, :list,
|
12
|
+
:list_item, :paragraph,
|
13
|
+
|
14
|
+
# span-level calls
|
15
|
+
:autolink, :codespan, :double_emphasis,
|
16
|
+
:emphasis, :raw_html, :triple_emphasis,
|
17
|
+
:strikethrough, :superscript,
|
18
|
+
|
19
|
+
# low level rendering
|
20
|
+
:entity, :normal_text
|
21
|
+
].each do |method|
|
22
|
+
define_method method do |*args|
|
23
|
+
args.first
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Other methods where the text content is in another argument
|
28
|
+
def link(link, title, content)
|
29
|
+
content
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/redcarpet.gemspec
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'vkhater-redcarpet'
|
4
|
+
s.version = '2.2.3'
|
5
|
+
s.summary = "Markdown that smells nice"
|
6
|
+
s.description = 'A fast, safe and extensible Markdown to (X)HTML parser'
|
7
|
+
s.date = '2012-10-19'
|
8
|
+
s.email = 'vicent@github.com'
|
9
|
+
s.homepage = 'http://github.com/vmg/redcarpet'
|
10
|
+
s.authors = ["Natacha Porté", "Vicent Martí"]
|
11
|
+
# = MANIFEST =
|
12
|
+
s.files = %w[
|
13
|
+
COPYING
|
14
|
+
Gemfile
|
15
|
+
Gemfile.lock
|
16
|
+
README.markdown
|
17
|
+
Rakefile
|
18
|
+
bin/redcarpet
|
19
|
+
ext/redcarpet/autolink.c
|
20
|
+
ext/redcarpet/autolink.h
|
21
|
+
ext/redcarpet/buffer.c
|
22
|
+
ext/redcarpet/buffer.h
|
23
|
+
ext/redcarpet/extconf.rb
|
24
|
+
ext/redcarpet/houdini.h
|
25
|
+
ext/redcarpet/houdini_href_e.c
|
26
|
+
ext/redcarpet/houdini_html_e.c
|
27
|
+
ext/redcarpet/html.c
|
28
|
+
ext/redcarpet/html.h
|
29
|
+
ext/redcarpet/html_blocks.h
|
30
|
+
ext/redcarpet/html_smartypants.c
|
31
|
+
ext/redcarpet/markdown.c
|
32
|
+
ext/redcarpet/markdown.h
|
33
|
+
ext/redcarpet/rc_markdown.c
|
34
|
+
ext/redcarpet/rc_render.c
|
35
|
+
ext/redcarpet/redcarpet.h
|
36
|
+
ext/redcarpet/stack.c
|
37
|
+
ext/redcarpet/stack.h
|
38
|
+
lib/redcarpet.rb
|
39
|
+
lib/redcarpet/compat.rb
|
40
|
+
lib/redcarpet/render_man.rb
|
41
|
+
lib/redcarpet/render_strip.rb
|
42
|
+
redcarpet.gemspec
|
43
|
+
sundown
|
44
|
+
test/redcarpet_test.rb
|
45
|
+
]
|
46
|
+
# = MANIFEST =
|
47
|
+
s.test_files = ["test/redcarpet_test.rb"]
|
48
|
+
s.extra_rdoc_files = ["COPYING"]
|
49
|
+
s.extensions = ["ext/redcarpet/extconf.rb"]
|
50
|
+
s.executables = ["redcarpet"]
|
51
|
+
s.require_paths = ["lib"]
|
52
|
+
s.add_development_dependency "nokogiri"
|
53
|
+
s.add_development_dependency "rake-compiler"
|
54
|
+
end
|
@@ -0,0 +1,447 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
rootdir = File.dirname(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift "#{rootdir}/lib"
|
4
|
+
|
5
|
+
if defined? Encoding
|
6
|
+
Encoding.default_internal = 'UTF-8'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'test/unit'
|
10
|
+
require 'redcarpet'
|
11
|
+
require 'redcarpet/render_man'
|
12
|
+
require 'nokogiri'
|
13
|
+
|
14
|
+
def html_equal(html_a, html_b)
|
15
|
+
assert_equal Nokogiri::HTML::DocumentFragment.parse(html_a).to_html,
|
16
|
+
Nokogiri::HTML::DocumentFragment.parse(html_b).to_html
|
17
|
+
end
|
18
|
+
|
19
|
+
class SmartyPantsTest < Test::Unit::TestCase
|
20
|
+
def setup
|
21
|
+
@pants = Redcarpet::Render::SmartyPants
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_that_smart_converts_single_quotes_in_words_that_end_in_re
|
25
|
+
markdown = @pants.render("<p>They're not for sale.</p>")
|
26
|
+
assert_equal "<p>They’re not for sale.</p>", markdown
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_that_smart_converts_single_quotes_in_words_that_end_in_ll
|
30
|
+
markdown = @pants.render("<p>Well that'll be the day</p>")
|
31
|
+
assert_equal "<p>Well that’ll be the day</p>", markdown
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_that_smart_converts_double_quotes_to_curly_quotes
|
35
|
+
rd = @pants.render(%(<p>"Quoted text"</p>))
|
36
|
+
assert_equal %(<p>“Quoted text”</p>), rd
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_that_smart_gives_ve_suffix_a_rsquo
|
40
|
+
rd = @pants.render("<p>I've been meaning to tell you ..</p>")
|
41
|
+
assert_equal "<p>I’ve been meaning to tell you ..</p>", rd
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_that_smart_gives_m_suffix_a_rsquo
|
45
|
+
rd = @pants.render("<p>I'm not kidding</p>")
|
46
|
+
assert_equal "<p>I’m not kidding</p>", rd
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_that_smart_gives_d_suffix_a_rsquo
|
50
|
+
rd = @pants.render("<p>what'd you say?</p>")
|
51
|
+
assert_equal "<p>what’d you say?</p>", rd
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class HTMLRenderTest < Test::Unit::TestCase
|
56
|
+
def setup
|
57
|
+
@markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
|
58
|
+
@rndr = {
|
59
|
+
:no_html => Redcarpet::Render::HTML.new(:filter_html => true),
|
60
|
+
:no_images => Redcarpet::Render::HTML.new(:no_images => true),
|
61
|
+
:no_links => Redcarpet::Render::HTML.new(:no_links => true),
|
62
|
+
:safe_links => Redcarpet::Render::HTML.new(:safe_links_only => true),
|
63
|
+
:escape_html => Redcarpet::Render::HTML.new(:escape_html => true),
|
64
|
+
:hard_wrap => Redcarpet::Render::HTML.new(:hard_wrap => true),
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_with(rndr, text)
|
69
|
+
Redcarpet::Markdown.new(rndr).render(text)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Hint: overrides filter_html, no_images and no_links
|
73
|
+
def test_that_escape_html_works
|
74
|
+
source = <<EOS
|
75
|
+
Through <em>NO</em> <script>DOUBLE NO</script>
|
76
|
+
|
77
|
+
<script>BAD</script>
|
78
|
+
|
79
|
+
<img src="/favicon.ico" />
|
80
|
+
EOS
|
81
|
+
expected = <<EOE
|
82
|
+
<p>Through <em>NO</em> <script>DOUBLE NO</script></p>
|
83
|
+
|
84
|
+
<p><script>BAD</script></p>
|
85
|
+
|
86
|
+
<p><img src="/favicon.ico" />
|
87
|
+
|
88
|
+
EOE
|
89
|
+
|
90
|
+
markdown = render_with(@rndr[:escape_html], source)
|
91
|
+
html_equal expected, markdown
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_that_filter_html_works
|
95
|
+
markdown = render_with(@rndr[:no_html], 'Through <em>NO</em> <script>DOUBLE NO</script>')
|
96
|
+
html_equal "<p>Through NO DOUBLE NO</p>", markdown
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_filter_html_doesnt_break_two_space_hard_break
|
100
|
+
markdown = render_with(@rndr[:no_html], "Lorem, \nipsum\n")
|
101
|
+
html_equal "<p>Lorem,<br/>\nipsum</p>\n", markdown
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_that_no_image_flag_works
|
105
|
+
rd = render_with(@rndr[:no_images], %( <img src="image.png" />))
|
106
|
+
assert rd !~ /<img/
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_that_no_links_flag_works
|
110
|
+
rd = render_with(@rndr[:no_links], %([This link](http://example.net/) <a href="links.html">links</a>))
|
111
|
+
assert rd !~ /<a /
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_that_safelink_flag_works
|
115
|
+
rd = render_with(@rndr[:safe_links], "[IRC](irc://chat.freenode.org/#freenode)")
|
116
|
+
html_equal "<p>[IRC](irc://chat.freenode.org/#freenode)</p>\n", rd
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_that_hard_wrap_works
|
120
|
+
rd = render_with(@rndr[:hard_wrap], <<EOE)
|
121
|
+
Hello world,
|
122
|
+
this is just a simple test
|
123
|
+
|
124
|
+
With hard wraps
|
125
|
+
and other *things*.
|
126
|
+
EOE
|
127
|
+
|
128
|
+
assert rd =~ /<br>/
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_that_link_attributes_work
|
132
|
+
rndr = Redcarpet::Render::HTML.new(:link_attributes => {:rel => 'blank'})
|
133
|
+
md = Redcarpet::Markdown.new(rndr)
|
134
|
+
assert md.render('This is a [simple](http://test.com) test.').include?('rel="blank"')
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class MarkdownTest < Test::Unit::TestCase
|
139
|
+
|
140
|
+
def setup
|
141
|
+
@markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
|
142
|
+
end
|
143
|
+
|
144
|
+
def render_with(flags, text)
|
145
|
+
Redcarpet::Markdown.new(Redcarpet::Render::HTML, flags).render(text)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_that_simple_one_liner_goes_to_html
|
149
|
+
assert_respond_to @markdown, :render
|
150
|
+
html_equal "<p>Hello World.</p>", @markdown.render("Hello World.")
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_that_inline_markdown_goes_to_html
|
154
|
+
markdown = @markdown.render('_Hello World_!')
|
155
|
+
html_equal "<p><em>Hello World</em>!</p>", markdown
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_that_inline_markdown_starts_and_ends_correctly
|
159
|
+
markdown = render_with({:no_intra_emphasis => true}, '_start _ foo_bar bar_baz _ end_ *italic* **bold** <a>_blah_</a>')
|
160
|
+
|
161
|
+
html_equal "<p><em>start _ foo_bar bar_baz _ end</em> <em>italic</em> <strong>bold</strong> <a><em>blah</em></a></p>", markdown
|
162
|
+
|
163
|
+
markdown = @markdown.render("Run 'rake radiant:extensions:rbac_base:migrate'")
|
164
|
+
html_equal "<p>Run 'rake radiant:extensions:rbac_base:migrate'</p>", markdown
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_that_urls_are_not_doubly_escaped
|
168
|
+
markdown = @markdown.render('[Page 2](/search?query=Markdown+Test&page=2)')
|
169
|
+
html_equal "<p><a href=\"/search?query=Markdown+Test&page=2\">Page 2</a></p>\n", markdown
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_simple_inline_html
|
173
|
+
#markdown = Markdown.new("before\n\n<div>\n foo\n</div>\nafter")
|
174
|
+
markdown = @markdown.render("before\n\n<div>\n foo\n</div>\n\nafter")
|
175
|
+
html_equal "<p>before</p>\n\n<div>\n foo\n</div>\n\n<p>after</p>\n", markdown
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_that_html_blocks_do_not_require_their_own_end_tag_line
|
179
|
+
markdown = @markdown.render("Para 1\n\n<div><pre>HTML block\n</pre></div>\n\nPara 2 [Link](#anchor)")
|
180
|
+
html_equal "<p>Para 1</p>\n\n<div><pre>HTML block\n</pre></div>\n\n<p>Para 2 <a href=\"#anchor\">Link</a></p>\n",
|
181
|
+
markdown
|
182
|
+
end
|
183
|
+
|
184
|
+
# This isn't in the spec but is Markdown.pl behavior.
|
185
|
+
def test_block_quotes_preceded_by_spaces
|
186
|
+
markdown = @markdown.render(
|
187
|
+
"A wise man once said:\n\n" +
|
188
|
+
" > Isn't it wonderful just to be alive.\n"
|
189
|
+
)
|
190
|
+
html_equal "<p>A wise man once said:</p>\n\n" +
|
191
|
+
"<blockquote><p>Isn't it wonderful just to be alive.</p>\n</blockquote>\n",
|
192
|
+
markdown
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_para_before_block_html_should_not_wrap_in_p_tag
|
196
|
+
markdown = render_with({:lax_spacing => true},
|
197
|
+
"Things to watch out for\n" +
|
198
|
+
"<ul>\n<li>Blah</li>\n</ul>\n")
|
199
|
+
|
200
|
+
html_equal "<p>Things to watch out for</p>\n\n" +
|
201
|
+
"<ul>\n<li>Blah</li>\n</ul>\n", markdown
|
202
|
+
end
|
203
|
+
|
204
|
+
# http://github.com/rtomayko/rdiscount/issues/#issue/13
|
205
|
+
def test_headings_with_trailing_space
|
206
|
+
text = "The Ant-Sugar Tales \n" +
|
207
|
+
"=================== \n\n" +
|
208
|
+
"By Candice Yellowflower \n"
|
209
|
+
html_equal "<h1>The Ant-Sugar Tales </h1>\n\n<p>By Candice Yellowflower </p>\n", @markdown.render(text)
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_that_intra_emphasis_works
|
213
|
+
rd = render_with({}, "foo_bar_baz")
|
214
|
+
html_equal "<p>foo<em>bar</em>baz</p>\n", rd
|
215
|
+
|
216
|
+
rd = render_with({:no_intra_emphasis => true},"foo_bar_baz")
|
217
|
+
html_equal "<p>foo_bar_baz</p>\n", rd
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_that_autolink_flag_works
|
221
|
+
rd = render_with({:autolink => true}, "http://github.com/rtomayko/rdiscount")
|
222
|
+
html_equal "<p><a href=\"http://github.com/rtomayko/rdiscount\">http://github.com/rtomayko/rdiscount</a></p>\n", rd
|
223
|
+
end
|
224
|
+
|
225
|
+
if "".respond_to?(:encoding)
|
226
|
+
def test_should_return_string_in_same_encoding_as_input
|
227
|
+
input = "Yogācāra"
|
228
|
+
output = @markdown.render(input)
|
229
|
+
assert_equal input.encoding.name, output.encoding.name
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_should_return_string_in_same_encoding_not_in_utf8
|
233
|
+
input = "testing".encode('US-ASCII')
|
234
|
+
output = @markdown.render(input)
|
235
|
+
assert_equal input.encoding.name, output.encoding.name
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_should_accept_non_utf8_or_ascii
|
239
|
+
input = "testing \xAB\xCD".force_encoding('ASCII-8BIT')
|
240
|
+
output = @markdown.render(input)
|
241
|
+
assert_equal 'ASCII-8BIT', output.encoding.name
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_that_tags_can_have_dashes_and_underscores
|
246
|
+
rd = @markdown.render("foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b>")
|
247
|
+
html_equal "<p>foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b></p>\n", rd
|
248
|
+
end
|
249
|
+
|
250
|
+
def test_link_syntax_is_not_processed_within_code_blocks
|
251
|
+
markdown = @markdown.render(" This is a code block\n This is a link [[1]] inside\n")
|
252
|
+
html_equal "<pre><code>This is a code block\nThis is a link [[1]] inside\n</code></pre>\n",
|
253
|
+
markdown
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_whitespace_after_urls
|
257
|
+
rd = render_with({:autolink => true}, "Japan: http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm (yes, japan)")
|
258
|
+
exp = %{<p>Japan: <a href="http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm">http://www.abc.net.au/news/events/japan-quake-2011/beforeafter.htm</a> (yes, japan)</p>}
|
259
|
+
html_equal exp, rd
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_memory_leak_when_parsing_char_links
|
263
|
+
@markdown.render(<<-leaks)
|
264
|
+
2. Identify the wild-type cluster and determine all clusters
|
265
|
+
containing or contained by it:
|
266
|
+
|
267
|
+
wildtype <- wildtype.cluster(h)
|
268
|
+
wildtype.mask <- logical(nclust)
|
269
|
+
wildtype.mask[c(contains(h, wildtype),
|
270
|
+
wildtype,
|
271
|
+
contained.by(h, wildtype))] <- TRUE
|
272
|
+
|
273
|
+
This could be more elegant.
|
274
|
+
leaks
|
275
|
+
end
|
276
|
+
|
277
|
+
def test_infinite_loop_in_header
|
278
|
+
html_equal @markdown.render(<<-header), "<h1>Body</h1>"
|
279
|
+
######
|
280
|
+
#Body#
|
281
|
+
######
|
282
|
+
header
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_that_tables_flag_works
|
286
|
+
text = <<EOS
|
287
|
+
aaa | bbbb
|
288
|
+
-----|------
|
289
|
+
hello|sailor
|
290
|
+
EOS
|
291
|
+
|
292
|
+
assert render_with({}, text) !~ /<table/
|
293
|
+
|
294
|
+
assert render_with({:tables => true}, text) =~ /<table/
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_strikethrough_flag_works
|
298
|
+
text = "this is ~some~ striked ~~text~~"
|
299
|
+
|
300
|
+
assert render_with({}, text) !~ /<del/
|
301
|
+
|
302
|
+
assert render_with({:strikethrough => true}, text) =~ /<del/
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_that_fenced_flag_works
|
306
|
+
text = <<fenced
|
307
|
+
This is a simple test
|
308
|
+
|
309
|
+
~~~~~
|
310
|
+
This is some awesome code
|
311
|
+
with tabs and shit
|
312
|
+
~~~
|
313
|
+
fenced
|
314
|
+
|
315
|
+
assert render_with({}, text) !~ /<code/
|
316
|
+
|
317
|
+
assert render_with({:fenced_code_blocks => true}, text) =~ /<code/
|
318
|
+
end
|
319
|
+
|
320
|
+
def test_that_fenced_flag_works_without_space
|
321
|
+
text = "foo\nbar\n```\nsome\ncode\n```\nbaz"
|
322
|
+
out = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :fenced_code_blocks => true, :lax_spacing => true).render(text)
|
323
|
+
assert out.include?("<pre><code>")
|
324
|
+
|
325
|
+
out = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :fenced_code_blocks => true).render(text)
|
326
|
+
assert !out.include?("<pre><code>")
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_that_headers_are_linkable
|
330
|
+
markdown = @markdown.render('### Hello [GitHub](http://github.com)')
|
331
|
+
html_equal "<h3>Hello <a href=\"http://github.com\">GitHub</a></h3>", markdown
|
332
|
+
end
|
333
|
+
|
334
|
+
def test_autolinking_with_ent_chars
|
335
|
+
markdown = render_with({:autolink => true}, <<text)
|
336
|
+
This a stupid link: https://github.com/rtomayko/tilt/issues?milestone=1&state=open
|
337
|
+
text
|
338
|
+
html_equal "<p>This a stupid link: <a href=\"https://github.com/rtomayko/tilt/issues?milestone=1&state=open\">https://github.com/rtomayko/tilt/issues?milestone=1&state=open</a></p>\n", markdown
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_spaced_headers
|
342
|
+
rd = render_with({:space_after_headers => true}, "#123 a header yes\n")
|
343
|
+
assert rd !~ /<h1>/
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_proper_intra_emphasis
|
347
|
+
md = Redcarpet::Markdown.new(Redcarpet::Render::HTML, :no_intra_emphasis => true)
|
348
|
+
assert render_with({:no_intra_emphasis => true}, "http://en.wikipedia.org/wiki/Dave_Allen_(comedian)") !~ /<em>/
|
349
|
+
assert render_with({:no_intra_emphasis => true}, "this fails: hello_world_") !~ /<em>/
|
350
|
+
assert render_with({:no_intra_emphasis => true}, "this also fails: hello_world_#bye") !~ /<em>/
|
351
|
+
assert render_with({:no_intra_emphasis => true}, "this works: hello_my_world") !~ /<em>/
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
class CustomRenderTest < Test::Unit::TestCase
|
356
|
+
class SimpleRender < Redcarpet::Render::HTML
|
357
|
+
def emphasis(text)
|
358
|
+
"<em class=\"cool\">#{text}</em>"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def test_simple_overload
|
363
|
+
md = Redcarpet::Markdown.new(SimpleRender)
|
364
|
+
html_equal "<p>This is <em class=\"cool\">just</em> a test</p>",
|
365
|
+
md.render("This is *just* a test")
|
366
|
+
end
|
367
|
+
|
368
|
+
class NilPreprocessRenderer < Redcarpet::Render::HTML
|
369
|
+
def preprocess(fulldoc)
|
370
|
+
nil
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_preprocess_returning_nil
|
375
|
+
md = Redcarpet::Markdown.new(NilPreprocessRenderer)
|
376
|
+
assert_equal(nil,md.render("Anything"))
|
377
|
+
end
|
378
|
+
|
379
|
+
end
|
380
|
+
|
381
|
+
class RedcarpetCompatTest < Test::Unit::TestCase
|
382
|
+
def test_simple_compat_api
|
383
|
+
html = RedcarpetCompat.new("This is_just_a test").to_html
|
384
|
+
html_equal "<p>This is<em>just</em>a test</p>", html
|
385
|
+
end
|
386
|
+
|
387
|
+
def test_compat_api_enables_extensions
|
388
|
+
html = RedcarpetCompat.new("This is_just_a test", :no_intra_emphasis).to_html
|
389
|
+
html_equal "<p>This is_just_a test</p>", html
|
390
|
+
end
|
391
|
+
|
392
|
+
def test_compat_api_knows_fenced_code_extension
|
393
|
+
text = "```ruby\nx = 'foo'\n```"
|
394
|
+
html = RedcarpetCompat.new(text, :fenced_code).to_html
|
395
|
+
html_equal "<pre><code class=\"ruby\">x = 'foo'\n</code></pre>", html
|
396
|
+
end
|
397
|
+
|
398
|
+
def test_compat_api_ignores_gh_blockcode_extension
|
399
|
+
text = "```ruby\nx = 'foo'\n```"
|
400
|
+
html = RedcarpetCompat.new(text, :fenced_code, :gh_blockcode).to_html
|
401
|
+
html_equal "<pre><code class=\"ruby\">x = 'foo'\n</code></pre>", html
|
402
|
+
end
|
403
|
+
|
404
|
+
def test_compat_api_knows_no_intraemphasis_extension
|
405
|
+
html = RedcarpetCompat.new("This is_just_a test", :no_intraemphasis).to_html
|
406
|
+
html_equal "<p>This is_just_a test</p>", html
|
407
|
+
end
|
408
|
+
|
409
|
+
def test_translate_outdated_extensions
|
410
|
+
# these extensions are no longer used
|
411
|
+
exts = [:gh_blockcode, :no_tables, :smart, :strict]
|
412
|
+
html = RedcarpetCompat.new('"TEST"', *exts).to_html
|
413
|
+
html_equal "<p>"TEST"</p>", html
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
# Disabled by default
|
418
|
+
# (these are the easy ones -- the evil ones are not disclosed)
|
419
|
+
class PathologicalInputsTest # < Test::Unit::TestCase
|
420
|
+
def setup
|
421
|
+
@markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML)
|
422
|
+
end
|
423
|
+
|
424
|
+
def test_pathological_1
|
425
|
+
star = '*' * 250000
|
426
|
+
@markdown.render("#{star}#{star} hi #{star}#{star}")
|
427
|
+
end
|
428
|
+
|
429
|
+
def test_pathological_2
|
430
|
+
crt = '^' * 255
|
431
|
+
str = "#{crt}(\\)"
|
432
|
+
@markdown.render("#{str*300}")
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_pathological_3
|
436
|
+
c = "`t`t`t`t`t`t" * 20000000
|
437
|
+
@markdown.render(c)
|
438
|
+
end
|
439
|
+
|
440
|
+
def test_pathological_4
|
441
|
+
@markdown.render(" [^a]: #{ "A" * 10000 }\n#{ "[^a][]" * 1000000 }\n")
|
442
|
+
end
|
443
|
+
|
444
|
+
def test_unbound_recursion
|
445
|
+
@markdown.render(("[" * 10000) + "foo" + ("](bar)" * 10000))
|
446
|
+
end
|
447
|
+
end
|