vkhater-redcarpet 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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], %(![dust mite](http://dust.mite/image.png) <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
|