redcarpet 1.17.2 → 2.0.0b
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of redcarpet might be problematic. Click here for more details.
- data/README.markdown +232 -37
- data/Rakefile +13 -15
- data/bin/redcarpet +1 -1
- data/ext/redcarpet/autolink.c +5 -5
- data/ext/redcarpet/autolink.h +7 -4
- data/ext/redcarpet/html.c +83 -71
- data/ext/redcarpet/html.h +14 -5
- data/ext/redcarpet/html_smartypants.c +35 -11
- data/ext/redcarpet/markdown.c +153 -85
- data/ext/redcarpet/markdown.h +6 -10
- data/ext/redcarpet/rc_markdown.c +108 -0
- data/ext/redcarpet/rc_render.c +469 -0
- data/lib/redcarpet.rb +68 -106
- data/redcarpet.gemspec +8 -11
- data/test/redcarpet_test.rb +206 -122
- metadata +17 -18
- data/ext/redcarpet/redcarpet.c +0 -161
- data/lib/markdown.rb +0 -1
- data/test/benchmark.rb +0 -56
- data/test/benchmark.txt +0 -306
- data/test/markdown_test.rb +0 -186
data/README.markdown
CHANGED
@@ -1,61 +1,256 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
Redcarpet 2 is written with sugar, spice and everything nice
|
2
|
+
============================================================
|
3
3
|
|
4
|
-
Redcarpet is
|
5
|
-
|
4
|
+
Redcarpet is Ruby library for Markdown processing that smells like
|
5
|
+
butterflies and popcorn.
|
6
6
|
|
7
|
-
Redcarpet
|
7
|
+
Redcarpet used to be a drop-in replacement for Redcloth. This is no longer the
|
8
|
+
case since version 2 -- it now has its own API, but retains the old name. Yes,
|
9
|
+
that does mean that Redcarpet 2 is not backwards-compatible with the 1.X
|
10
|
+
versions.
|
8
11
|
|
9
|
-
|
12
|
+
Redcarpet is powered by the Sundown library, which can be found at
|
10
13
|
|
11
|
-
|
12
|
-
bindings so awesome.
|
14
|
+
https://www.github.com/tanoku/sundown
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
+
You might want to find out more about Sundown to see what makes this Ruby
|
17
|
+
library so awesome.
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
* With special thanks to Ryan Tomayko
|
19
|
+
This library is written by people
|
20
|
+
-------------------------------------------------------
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
Redcarpet 2 has been rewritten from scratch by Vicent Marti (@tanoku). Why
|
23
|
+
are you not following me on Twitter?
|
23
24
|
|
24
|
-
Redcarpet
|
25
|
+
Redcarpet would not be possible without the Sundown library and its authors
|
26
|
+
(Natacha Porté, Vicent Martí, and its many awesome contributors).
|
27
|
+
|
28
|
+
You can totally install it as a Gem
|
29
|
+
-----------------------------------
|
30
|
+
|
31
|
+
Redcarpet is readily available as a Ruby gem. It will build some native
|
32
|
+
extensions, but the parser is standalone and requires no installed libraries.
|
25
33
|
|
26
34
|
$ [sudo] gem install redcarpet
|
27
35
|
|
28
|
-
The Redcarpet source (including
|
36
|
+
The Redcarpet source (including Sundown as a submodule) is available at GitHub:
|
29
37
|
|
30
38
|
$ git clone git://github.com/tanoku/redcarpet.git
|
31
39
|
|
32
|
-
|
33
|
-
|
40
|
+
And it's like *really* simple to use
|
41
|
+
------------------------------------
|
42
|
+
|
43
|
+
The core of the Redcarpet library is the `Redcarpet::Markdown` class. Each
|
44
|
+
instance of the class is attached to a `Renderer` object; the Markdown class
|
45
|
+
performs parsing of a document and uses the attached renderer to generate
|
46
|
+
output.
|
47
|
+
|
48
|
+
The `Markdown` object is encouraged to be instantiated once with the required
|
49
|
+
settings, and reused between parses.
|
50
|
+
|
51
|
+
Markdown.new(renderer, extensions={})
|
52
|
+
|
53
|
+
Initializes a Markdown parser
|
54
|
+
|
55
|
+
renderer - a renderer object, inheriting from Redcarpet::Render::Base.
|
56
|
+
If the given object has not been instantiated, the library
|
57
|
+
will do it with default arguments.
|
58
|
+
|
59
|
+
extensions - a hash containing the Markdown extesions which the parser
|
60
|
+
will identify. The following extensions are accepted:
|
61
|
+
|
62
|
+
:no_intra_emphasis - do not parse emphasis inside of words.
|
63
|
+
Strings such as `foo_bar_baz` will not generate `<em>`
|
64
|
+
tags.
|
65
|
+
|
66
|
+
:tables - parse tables, PHP-Markdown style
|
67
|
+
|
68
|
+
:fenced_code_blocks - parse fenced code blocks, PHP-Markdown
|
69
|
+
style .Blocks delimited with 3 or more `~` or backticks
|
70
|
+
will be considered as code, without the need to be
|
71
|
+
indented. An optional language name may be added at the
|
72
|
+
end of the opening fence for the code block
|
73
|
+
|
74
|
+
:autolink - parse links even when they are not enclosed in
|
75
|
+
`<>` characters. Autolinks for the http, https and ftp
|
76
|
+
protocols will be automatically detected. Email addresses
|
77
|
+
are also handled, and http links without protocol, but
|
78
|
+
starting with `www.`
|
79
|
+
|
80
|
+
:strikethrough - parse strikethrough, PHP-Markdown style
|
81
|
+
Two `~` characters mark the start of a strikethrough,
|
82
|
+
e.g. `this is ~~good~~ bad`
|
83
|
+
|
84
|
+
:lax_html_blocks - HTML blocks do not require to be surrounded
|
85
|
+
by an empty line as in the Markdown standard.
|
86
|
+
|
87
|
+
:space_after_headers - A space is always required between the
|
88
|
+
hash at the beggining of a header and its name, e.g.
|
89
|
+
`#this is my header` would not be a valid header.
|
90
|
+
|
91
|
+
:superscript - parse superscripts after the `^` character;
|
92
|
+
contiguous superscripts are nested together, and complex
|
93
|
+
values can be enclosed in parenthesis,
|
94
|
+
e.g. `this is the 2^(nd) time`
|
95
|
+
|
96
|
+
Example:
|
97
|
+
|
98
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
99
|
+
:autolink => true, :space_after_headers => true)
|
100
|
+
|
101
|
+
Rendering with the `Markdown` object is done through `Markdown#render`.
|
102
|
+
Unlike in the RedCloth API, the text to render is passed as an argument
|
103
|
+
and not stored inside the `Markdown` instance, to encourage reusability.
|
104
|
+
|
105
|
+
Markdown.render(text)
|
106
|
+
|
107
|
+
Render a Markdown document with the attached renderer
|
108
|
+
|
109
|
+
text - a Markdown document
|
110
|
+
|
111
|
+
Example:
|
112
|
+
|
113
|
+
markdown.render("This is *bongos*, indeed.")
|
114
|
+
#=> "<p>This is <em>bongos</em>, indeed</p>"
|
115
|
+
|
116
|
+
|
117
|
+
Darling, I packed you a couple renderers for lunch
|
118
|
+
--------------------------------------------------
|
119
|
+
|
120
|
+
Redcarpet comes with two built-in renderers, `Redcarpet::Render::HTML` and
|
121
|
+
`Redcarpet::Render::XHTML`, which output HTML and XHTML, respectively. These
|
122
|
+
renderers are actually implemented in C, and hence offer a brilliant
|
123
|
+
performance, several degrees of magnitude faster than other Ruby Markdown
|
124
|
+
solutions.
|
125
|
+
|
126
|
+
The `HTML` renderer has an alternate version, `Redcarpet::Render::HTML_toc`,
|
127
|
+
which will output a table of contents in HTML based on the headers of the
|
128
|
+
Markdown document.
|
129
|
+
|
130
|
+
Furthermore, the abstract base class `Redcarpet::Render::Base` can be used
|
131
|
+
to write a custom renderer purely in Ruby, or extending an existing renderer.
|
132
|
+
See the following section for more information.
|
133
|
+
|
134
|
+
|
135
|
+
And you can even cook your own
|
136
|
+
------------------------------
|
137
|
+
|
138
|
+
Custom renderers are created by inheriting from an existing renderer. The
|
139
|
+
builtin renderers, `HTML` and `XHTML` may be extended as such:
|
140
|
+
|
141
|
+
~~~~~ ruby
|
142
|
+
# create a custom renderer that allows highlighting of code blocks
|
143
|
+
class HTMLwithAlbino < Redcarpet::Render::HTML
|
144
|
+
def block_code(code, language)
|
145
|
+
Albino.safe_colorize(code, language)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
markdown = Redcarpet::Markdown.new(HTMLwithAlbino, :fenced_code_blocks => true)
|
150
|
+
~~~~~
|
151
|
+
|
152
|
+
But new renderers can also be created from scratch (see `lib/render_man.rb` for
|
153
|
+
an example implementation of a Manpage renderer)
|
154
|
+
|
155
|
+
~~~~~~ ruby
|
156
|
+
class ManPage < Redcarpet::Render::Base
|
157
|
+
# you get the drill -- keep going from here
|
158
|
+
end
|
159
|
+
~~~~~
|
160
|
+
|
161
|
+
The following instance methods may be implemented by the renderer:
|
162
|
+
|
163
|
+
# Block-level calls
|
164
|
+
# If the return value of the method is `nil`, the block
|
165
|
+
# will be skipped.
|
166
|
+
# If the method for a document element is not implemented,
|
167
|
+
# the block will be skipped.
|
168
|
+
#
|
169
|
+
# Example:
|
170
|
+
#
|
171
|
+
# class RenderWithoutCode < Redcarpet::Render::HTML
|
172
|
+
# def block_code(code, language)
|
173
|
+
# nil
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
block_code(code, language)
|
178
|
+
block_quote(quote)
|
179
|
+
block_html(raw_html)
|
180
|
+
header(text, header_level)
|
181
|
+
hrule()
|
182
|
+
list(contents, list_type)
|
183
|
+
list_item(text)
|
184
|
+
paragraph(text)
|
185
|
+
table(header, body)
|
186
|
+
table_row(content)
|
187
|
+
table_cell(content, alignment)
|
188
|
+
|
189
|
+
# Span-level calls
|
190
|
+
# A return value of `nil` will not output any data
|
191
|
+
# If the method for a document element is not implemented,
|
192
|
+
# the contents of the span will be copied verbatim
|
193
|
+
autolink(link, link_type)
|
194
|
+
codespan(code)
|
195
|
+
double_emphasis(text)
|
196
|
+
emphasis(text)
|
197
|
+
image(link, title, alt_text)
|
198
|
+
linebreak()
|
199
|
+
link(link, title, content)
|
200
|
+
raw_html(raw_html)
|
201
|
+
triple_emphasis(text)
|
202
|
+
strikethrough(text)
|
203
|
+
superscript(text)
|
204
|
+
|
205
|
+
# Low level rendering
|
206
|
+
entity(text)
|
207
|
+
normal_text(text)
|
208
|
+
|
209
|
+
# Header of the document
|
210
|
+
# Rendered before any another elements
|
211
|
+
doc_header()
|
212
|
+
|
213
|
+
# Footer of the document
|
214
|
+
# Rendered after all the other elements
|
215
|
+
doc_footer()
|
216
|
+
|
217
|
+
# Pre/post-process
|
218
|
+
# Special callback: preprocess or postprocess the whole
|
219
|
+
# document before or after the rendering process begins
|
220
|
+
preprocess(full_document)
|
221
|
+
postprocess(full_document)
|
222
|
+
|
223
|
+
|
224
|
+
Also, now our Pants are much smarter
|
225
|
+
------------------------------------
|
34
226
|
|
35
|
-
Redcarpet
|
227
|
+
Redcarpet 2 comes with a standalone [SmartyPants](
|
228
|
+
http://daringfireball.net/projects/smartypants/) implementation. It is fully
|
229
|
+
compilant with the original implementation. It is the fastest SmartyPants
|
230
|
+
parser there is, with a difference of several orders of magnitude.
|
36
231
|
|
37
|
-
|
38
|
-
|
39
|
-
markdown = Redcarpet.new("Hello World!")
|
40
|
-
puts markdown.to_html
|
41
|
-
~~~~~~
|
232
|
+
The SmartyPants parser can be found in `Redcarpet::Render::SmartyPants`. It has
|
233
|
+
been implemented as a module, so it can be used standalone or as a mixin.
|
42
234
|
|
43
|
-
|
44
|
-
|
235
|
+
When mixed with a Renderer class, it will override the `postprocess` method
|
236
|
+
to perform SmartyPants replacements once the rendering is complete
|
45
237
|
|
46
|
-
|
47
|
-
|
48
|
-
|
238
|
+
~~~~ ruby
|
239
|
+
# Mixin
|
240
|
+
class HTMLWithPants < Redcarpet::Render::HTML
|
241
|
+
include Redcarpet::Render::SmartyPants
|
242
|
+
end
|
49
243
|
|
50
|
-
|
51
|
-
|
52
|
-
|
244
|
+
# Standalone
|
245
|
+
Redcarpet::Render::SmartyPants.render("<p>Oh SmartyPants, you're so crazy...</p>")
|
246
|
+
~~~~~
|
53
247
|
|
54
|
-
|
55
|
-
|
248
|
+
SmartyPants works on top of already-rendered HTML, and will ignore replacements
|
249
|
+
inside the content of HTML tags and inside specific HTML blocks such as
|
250
|
+
`<code>` or `<pre>`.
|
56
251
|
|
57
|
-
|
58
|
-
|
252
|
+
Boring legal stuff
|
253
|
+
------------------
|
59
254
|
|
60
255
|
Copyright (c) 2011, Vicent Martí
|
61
256
|
|
data/Rakefile
CHANGED
@@ -70,9 +70,6 @@ task :install => package('.gem') do
|
|
70
70
|
sh "gem install #{package('.gem')}"
|
71
71
|
end
|
72
72
|
|
73
|
-
desc 'Update the gemspec'
|
74
|
-
task :update_gem => file('redcarpet.gemspec')
|
75
|
-
|
76
73
|
directory 'pkg/'
|
77
74
|
|
78
75
|
file package('.gem') => %w[pkg/ redcarpet.gemspec] + $spec.files do |f|
|
@@ -91,9 +88,10 @@ def source_version
|
|
91
88
|
line.match(/.*VERSION = '(.*)'/)[1]
|
92
89
|
end
|
93
90
|
|
94
|
-
|
91
|
+
task :update_gem do
|
95
92
|
# read spec file and split out manifest section
|
96
|
-
|
93
|
+
GEMFILE = 'redcarpet.gemspec'
|
94
|
+
spec = File.read(GEMFILE)
|
97
95
|
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
98
96
|
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
|
99
97
|
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
|
@@ -107,26 +105,26 @@ file 'redcarpet.gemspec' => FileList['Rakefile','lib/redcarpet.rb'] do |f|
|
|
107
105
|
# piece file back together and write...
|
108
106
|
manifest = " s.files = %w[\n#{files}\n ]\n"
|
109
107
|
spec = [head,manifest,tail].join(" # = MANIFEST =\n")
|
110
|
-
File.open(
|
111
|
-
puts "updated #{
|
108
|
+
File.open(GEMFILE, 'w') { |io| io.write(spec) }
|
109
|
+
puts "updated #{GEMFILE}"
|
112
110
|
end
|
113
111
|
|
114
|
-
desc 'Gather required
|
115
|
-
task :gather => '
|
112
|
+
desc 'Gather required Sundown sources into extension directory'
|
113
|
+
task :gather => 'sundown/src/markdown.h' do |t|
|
116
114
|
files =
|
117
115
|
FileList[
|
118
|
-
'
|
119
|
-
'
|
120
|
-
'
|
121
|
-
'
|
116
|
+
'sundown/src/{markdown,buffer,array,autolink}.h',
|
117
|
+
'sundown/src/{markdown,buffer,array,autolink}.c',
|
118
|
+
'sundown/html/{html,html_smartypants}.c',
|
119
|
+
'sundown/html/html.h',
|
122
120
|
]
|
123
121
|
cp files, 'ext/redcarpet/',
|
124
122
|
:preserve => true,
|
125
123
|
:verbose => true
|
126
124
|
end
|
127
125
|
|
128
|
-
file '
|
129
|
-
abort "The
|
126
|
+
file 'sundown/src/markdown.h' do |t|
|
127
|
+
abort "The Sundown submodule is required."
|
130
128
|
end
|
131
129
|
|
132
130
|
|
data/bin/redcarpet
CHANGED
data/ext/redcarpet/autolink.c
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
#include <ctype.h>
|
23
23
|
|
24
24
|
int
|
25
|
-
|
25
|
+
sd_autolink_issafe(const char *link, size_t link_len)
|
26
26
|
{
|
27
27
|
static const size_t valid_uris_count = 4;
|
28
28
|
static const char *valid_uris[] = {
|
@@ -147,7 +147,7 @@ check_domain(char *data, size_t size)
|
|
147
147
|
}
|
148
148
|
|
149
149
|
size_t
|
150
|
-
|
150
|
+
sd_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
|
151
151
|
{
|
152
152
|
size_t link_end;
|
153
153
|
|
@@ -177,7 +177,7 @@ ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
177
177
|
}
|
178
178
|
|
179
179
|
size_t
|
180
|
-
|
180
|
+
sd_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
|
181
181
|
{
|
182
182
|
size_t link_end, rewind;
|
183
183
|
int nb = 0, np = 0;
|
@@ -226,7 +226,7 @@ ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offse
|
|
226
226
|
}
|
227
227
|
|
228
228
|
size_t
|
229
|
-
|
229
|
+
sd_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
|
230
230
|
{
|
231
231
|
size_t link_end, rewind = 0, domain_len;
|
232
232
|
|
@@ -236,7 +236,7 @@ ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
236
236
|
while (rewind < offset && isalpha(data[-rewind - 1]))
|
237
237
|
rewind++;
|
238
238
|
|
239
|
-
if (!
|
239
|
+
if (!sd_autolink_issafe(data - rewind, size + rewind))
|
240
240
|
return 0;
|
241
241
|
link_end = STRLEN("://");
|
242
242
|
|
data/ext/redcarpet/autolink.h
CHANGED
@@ -15,18 +15,21 @@
|
|
15
15
|
*/
|
16
16
|
|
17
17
|
#ifndef UPSKIRT_AUTOLINK_H
|
18
|
-
#define
|
18
|
+
#define UPSKIRT_AUTOLINK_H
|
19
19
|
|
20
20
|
#include "buffer.h"
|
21
21
|
|
22
|
+
extern int
|
23
|
+
sd_autolink_issafe(const char *link, size_t link_len);
|
24
|
+
|
22
25
|
extern size_t
|
23
|
-
|
26
|
+
sd_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
|
24
27
|
|
25
28
|
extern size_t
|
26
|
-
|
29
|
+
sd_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
|
27
30
|
|
28
31
|
extern size_t
|
29
|
-
|
32
|
+
sd_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
|
30
33
|
|
31
34
|
#endif
|
32
35
|
|
data/ext/redcarpet/html.c
CHANGED
@@ -23,14 +23,17 @@
|
|
23
23
|
#include <stdio.h>
|
24
24
|
#include <ctype.h>
|
25
25
|
|
26
|
+
#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)
|
27
|
+
|
26
28
|
struct html_renderopt {
|
29
|
+
void *extra;
|
30
|
+
|
27
31
|
struct {
|
28
32
|
int header_count;
|
29
33
|
int current_level;
|
30
34
|
} toc_data;
|
31
35
|
|
32
36
|
unsigned int flags;
|
33
|
-
const char *close_tag;
|
34
37
|
};
|
35
38
|
|
36
39
|
static inline void
|
@@ -45,9 +48,9 @@ put_scaped_char(struct buf *ob, char c)
|
|
45
48
|
}
|
46
49
|
}
|
47
50
|
|
48
|
-
/*
|
51
|
+
/* sdhtml_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
|
49
52
|
void
|
50
|
-
|
53
|
+
sdhtml_escape(struct buf *ob, const char *src, size_t size)
|
51
54
|
{
|
52
55
|
size_t i = 0, org;
|
53
56
|
while (i < size) {
|
@@ -66,37 +69,37 @@ upshtml_escape(struct buf *ob, const char *src, size_t size)
|
|
66
69
|
}
|
67
70
|
}
|
68
71
|
|
69
|
-
|
70
|
-
|
72
|
+
int
|
73
|
+
sdhtml_tag(const char *tag_data, size_t tag_size, const char *tagname)
|
71
74
|
{
|
72
|
-
size_t i
|
73
|
-
|
74
|
-
if (i < tag->size && tag->data[0] != '<')
|
75
|
-
return 0;
|
75
|
+
size_t i;
|
76
|
+
int closed = 0;
|
76
77
|
|
77
|
-
|
78
|
+
if (tag_size < 3 || tag_data[0] != '<')
|
79
|
+
return HTML_TAG_NONE;
|
78
80
|
|
79
|
-
|
80
|
-
i++;
|
81
|
+
i = 1;
|
81
82
|
|
82
|
-
if (
|
83
|
-
|
84
|
-
|
85
|
-
while (i < tag->size && isspace(tag->data[i]))
|
83
|
+
if (tag_data[i] == '/') {
|
84
|
+
closed = 1;
|
86
85
|
i++;
|
86
|
+
}
|
87
87
|
|
88
|
-
for (; i <
|
88
|
+
for (; i < tag_size; ++i, ++tagname) {
|
89
89
|
if (*tagname == 0)
|
90
90
|
break;
|
91
91
|
|
92
|
-
if (
|
93
|
-
return
|
92
|
+
if (tag_data[i] != *tagname)
|
93
|
+
return HTML_TAG_NONE;
|
94
94
|
}
|
95
95
|
|
96
|
-
if (i ==
|
97
|
-
return
|
96
|
+
if (i == tag_size)
|
97
|
+
return HTML_TAG_NONE;
|
98
|
+
|
99
|
+
if (isspace(tag_data[i]) || tag_data[i] == '>')
|
100
|
+
return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
|
98
101
|
|
99
|
-
return
|
102
|
+
return HTML_TAG_NONE;
|
100
103
|
}
|
101
104
|
|
102
105
|
/********************
|
@@ -111,7 +114,7 @@ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *op
|
|
111
114
|
return 0;
|
112
115
|
|
113
116
|
if ((options->flags & HTML_SAFELINK) != 0 &&
|
114
|
-
!
|
117
|
+
!sd_autolink_issafe(link->data, link->size) &&
|
115
118
|
type != MKDA_EMAIL)
|
116
119
|
return 0;
|
117
120
|
|
@@ -127,9 +130,9 @@ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *op
|
|
127
130
|
* want to print the `mailto:` prefix
|
128
131
|
*/
|
129
132
|
if (bufprefix(link, "mailto:") == 0) {
|
130
|
-
|
133
|
+
sdhtml_escape(ob, link->data + 7, link->size - 7);
|
131
134
|
} else {
|
132
|
-
|
135
|
+
sdhtml_escape(ob, link->data, link->size);
|
133
136
|
}
|
134
137
|
|
135
138
|
BUFPUTSL(ob, "</a>");
|
@@ -159,7 +162,7 @@ rndr_blockcode(struct buf *ob, struct buf *text, struct buf *lang, void *opaque)
|
|
159
162
|
org++;
|
160
163
|
|
161
164
|
if (cls) bufputc(ob, ' ');
|
162
|
-
|
165
|
+
sdhtml_escape(ob, lang->data + org, i - org);
|
163
166
|
}
|
164
167
|
}
|
165
168
|
|
@@ -168,7 +171,7 @@ rndr_blockcode(struct buf *ob, struct buf *text, struct buf *lang, void *opaque)
|
|
168
171
|
BUFPUTSL(ob, "<pre><code>");
|
169
172
|
|
170
173
|
if (text)
|
171
|
-
|
174
|
+
sdhtml_escape(ob, text->data, text->size);
|
172
175
|
|
173
176
|
BUFPUTSL(ob, "</code></pre>\n");
|
174
177
|
}
|
@@ -204,16 +207,16 @@ rndr_blockcode_github(struct buf *ob, struct buf *text, struct buf *lang, void *
|
|
204
207
|
i++;
|
205
208
|
|
206
209
|
if (lang->data[0] == '.')
|
207
|
-
|
210
|
+
sdhtml_escape(ob, lang->data + 1, i - 1);
|
208
211
|
else
|
209
|
-
|
212
|
+
sdhtml_escape(ob, lang->data, i);
|
210
213
|
|
211
214
|
BUFPUTSL(ob, "\"><code>");
|
212
215
|
} else
|
213
216
|
BUFPUTSL(ob, "<pre><code>");
|
214
217
|
|
215
218
|
if (text)
|
216
|
-
|
219
|
+
sdhtml_escape(ob, text->data, text->size);
|
217
220
|
|
218
221
|
BUFPUTSL(ob, "</code></pre>\n");
|
219
222
|
}
|
@@ -221,16 +224,17 @@ rndr_blockcode_github(struct buf *ob, struct buf *text, struct buf *lang, void *
|
|
221
224
|
static void
|
222
225
|
rndr_blockquote(struct buf *ob, struct buf *text, void *opaque)
|
223
226
|
{
|
227
|
+
if (ob->size) bufputc(ob, '\n');
|
224
228
|
BUFPUTSL(ob, "<blockquote>\n");
|
225
229
|
if (text) bufput(ob, text->data, text->size);
|
226
|
-
BUFPUTSL(ob, "</blockquote
|
230
|
+
BUFPUTSL(ob, "</blockquote>\n");
|
227
231
|
}
|
228
232
|
|
229
233
|
static int
|
230
234
|
rndr_codespan(struct buf *ob, struct buf *text, void *opaque)
|
231
235
|
{
|
232
236
|
BUFPUTSL(ob, "<code>");
|
233
|
-
if (text)
|
237
|
+
if (text) sdhtml_escape(ob, text->data, text->size);
|
234
238
|
BUFPUTSL(ob, "</code>");
|
235
239
|
return 1;
|
236
240
|
}
|
@@ -270,6 +274,14 @@ rndr_emphasis(struct buf *ob, struct buf *text, void *opaque)
|
|
270
274
|
return 1;
|
271
275
|
}
|
272
276
|
|
277
|
+
static int
|
278
|
+
rndr_linebreak(struct buf *ob, void *opaque)
|
279
|
+
{
|
280
|
+
struct html_renderopt *options = opaque;
|
281
|
+
bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");
|
282
|
+
return 1;
|
283
|
+
}
|
284
|
+
|
273
285
|
static void
|
274
286
|
rndr_header(struct buf *ob, struct buf *text, int level, void *opaque)
|
275
287
|
{
|
@@ -292,14 +304,14 @@ rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *conte
|
|
292
304
|
{
|
293
305
|
struct html_renderopt *options = opaque;
|
294
306
|
|
295
|
-
if ((options->flags & HTML_SAFELINK) != 0 && !
|
307
|
+
if ((options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
|
296
308
|
return 0;
|
297
309
|
|
298
310
|
BUFPUTSL(ob, "<a href=\"");
|
299
311
|
if (link && link->size) bufput(ob, link->data, link->size);
|
300
312
|
if (title && title->size) {
|
301
313
|
BUFPUTSL(ob, "\" title=\"");
|
302
|
-
|
314
|
+
sdhtml_escape(ob, title->data, title->size); }
|
303
315
|
BUFPUTSL(ob, "\">");
|
304
316
|
if (content && content->size) bufput(ob, content->data, content->size);
|
305
317
|
BUFPUTSL(ob, "</a>");
|
@@ -353,11 +365,14 @@ rndr_paragraph(struct buf *ob, struct buf *text, void *opaque)
|
|
353
365
|
if (i > org)
|
354
366
|
bufput(ob, text->data + org, i - org);
|
355
367
|
|
356
|
-
|
368
|
+
/*
|
369
|
+
* do not insert a line break if this newline
|
370
|
+
* is the last character on the paragraph
|
371
|
+
*/
|
372
|
+
if (i >= text->size - 1)
|
357
373
|
break;
|
358
|
-
|
359
|
-
|
360
|
-
bufputs(ob, options->close_tag);
|
374
|
+
|
375
|
+
rndr_linebreak(ob, opaque);
|
361
376
|
i++;
|
362
377
|
}
|
363
378
|
} else {
|
@@ -396,8 +411,7 @@ rndr_hrule(struct buf *ob, void *opaque)
|
|
396
411
|
{
|
397
412
|
struct html_renderopt *options = opaque;
|
398
413
|
if (ob->size) bufputc(ob, '\n');
|
399
|
-
|
400
|
-
bufputs(ob, options->close_tag);
|
414
|
+
bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
|
401
415
|
}
|
402
416
|
|
403
417
|
static int
|
@@ -406,25 +420,15 @@ rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt,
|
|
406
420
|
struct html_renderopt *options = opaque;
|
407
421
|
if (!link || !link->size) return 0;
|
408
422
|
BUFPUTSL(ob, "<img src=\"");
|
409
|
-
|
423
|
+
sdhtml_escape(ob, link->data, link->size);
|
410
424
|
BUFPUTSL(ob, "\" alt=\"");
|
411
425
|
if (alt && alt->size)
|
412
|
-
|
426
|
+
sdhtml_escape(ob, alt->data, alt->size);
|
413
427
|
if (title && title->size) {
|
414
428
|
BUFPUTSL(ob, "\" title=\"");
|
415
|
-
|
416
|
-
|
417
|
-
bufputc(ob, '"');
|
418
|
-
bufputs(ob, options->close_tag);
|
419
|
-
return 1;
|
420
|
-
}
|
429
|
+
sdhtml_escape(ob, title->data, title->size); }
|
421
430
|
|
422
|
-
|
423
|
-
rndr_linebreak(struct buf *ob, void *opaque)
|
424
|
-
{
|
425
|
-
struct html_renderopt *options = opaque;
|
426
|
-
BUFPUTSL(ob, "<br");
|
427
|
-
bufputs(ob, options->close_tag);
|
431
|
+
bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
|
428
432
|
return 1;
|
429
433
|
}
|
430
434
|
|
@@ -436,13 +440,13 @@ rndr_raw_html(struct buf *ob, struct buf *text, void *opaque)
|
|
436
440
|
if ((options->flags & HTML_SKIP_HTML) != 0)
|
437
441
|
return 1;
|
438
442
|
|
439
|
-
if ((options->flags & HTML_SKIP_STYLE) != 0 &&
|
443
|
+
if ((options->flags & HTML_SKIP_STYLE) != 0 && sdhtml_tag(text->data, text->size, "style"))
|
440
444
|
return 1;
|
441
445
|
|
442
|
-
if ((options->flags & HTML_SKIP_LINKS) != 0 &&
|
446
|
+
if ((options->flags & HTML_SKIP_LINKS) != 0 && sdhtml_tag(text->data, text->size, "a"))
|
443
447
|
return 1;
|
444
448
|
|
445
|
-
if ((options->flags & HTML_SKIP_IMAGES) != 0 &&
|
449
|
+
if ((options->flags & HTML_SKIP_IMAGES) != 0 && sdhtml_tag(text->data, text->size, "img"))
|
446
450
|
return 1;
|
447
451
|
|
448
452
|
bufput(ob, text->data, text->size);
|
@@ -456,26 +460,24 @@ rndr_table(struct buf *ob, struct buf *header, struct buf *body, void *opaque)
|
|
456
460
|
BUFPUTSL(ob, "<table><thead>\n");
|
457
461
|
if (header)
|
458
462
|
bufput(ob, header->data, header->size);
|
459
|
-
BUFPUTSL(ob, "
|
463
|
+
BUFPUTSL(ob, "</thead><tbody>\n");
|
460
464
|
if (body)
|
461
465
|
bufput(ob, body->data, body->size);
|
462
|
-
BUFPUTSL(ob, "
|
466
|
+
BUFPUTSL(ob, "</tbody></table>\n");
|
463
467
|
}
|
464
468
|
|
465
469
|
static void
|
466
470
|
rndr_tablerow(struct buf *ob, struct buf *text, void *opaque)
|
467
471
|
{
|
468
|
-
if (ob->size) bufputc(ob, '\n');
|
469
472
|
BUFPUTSL(ob, "<tr>\n");
|
470
473
|
if (text)
|
471
474
|
bufput(ob, text->data, text->size);
|
472
|
-
BUFPUTSL(ob, "
|
475
|
+
BUFPUTSL(ob, "</tr>\n");
|
473
476
|
}
|
474
477
|
|
475
478
|
static void
|
476
479
|
rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
|
477
480
|
{
|
478
|
-
if (ob->size) bufputc(ob, '\n');
|
479
481
|
switch (align) {
|
480
482
|
case MKD_TABLE_ALIGN_L:
|
481
483
|
BUFPUTSL(ob, "<td align=\"left\">");
|
@@ -496,14 +498,24 @@ rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
|
|
496
498
|
|
497
499
|
if (text)
|
498
500
|
bufput(ob, text->data, text->size);
|
499
|
-
BUFPUTSL(ob, "</td
|
501
|
+
BUFPUTSL(ob, "</td>\n");
|
502
|
+
}
|
503
|
+
|
504
|
+
static int
|
505
|
+
rndr_superscript(struct buf *ob, struct buf *text, void *opaque)
|
506
|
+
{
|
507
|
+
if (!text || !text->size) return 0;
|
508
|
+
BUFPUTSL(ob, "<sup>");
|
509
|
+
bufput(ob, text->data, text->size);
|
510
|
+
BUFPUTSL(ob, "</sup>");
|
511
|
+
return 1;
|
500
512
|
}
|
501
513
|
|
502
514
|
static void
|
503
515
|
rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
|
504
516
|
{
|
505
517
|
if (text)
|
506
|
-
|
518
|
+
sdhtml_escape(ob, text->data, text->size);
|
507
519
|
}
|
508
520
|
|
509
521
|
static void
|
@@ -546,7 +558,7 @@ toc_finalize(struct buf *ob, void *opaque)
|
|
546
558
|
}
|
547
559
|
|
548
560
|
void
|
549
|
-
|
561
|
+
sdhtml_toc_renderer(struct mkd_renderer *renderer, void *extra)
|
550
562
|
{
|
551
563
|
static const struct mkd_renderer toc_render = {
|
552
564
|
NULL,
|
@@ -571,6 +583,7 @@ upshtml_toc_renderer(struct mkd_renderer *renderer)
|
|
571
583
|
NULL,
|
572
584
|
rndr_triple_emphasis,
|
573
585
|
rndr_strikethrough,
|
586
|
+
rndr_superscript,
|
574
587
|
|
575
588
|
NULL,
|
576
589
|
NULL,
|
@@ -584,17 +597,15 @@ upshtml_toc_renderer(struct mkd_renderer *renderer)
|
|
584
597
|
struct html_renderopt *options;
|
585
598
|
options = calloc(1, sizeof(struct html_renderopt));
|
586
599
|
options->flags = HTML_TOC;
|
600
|
+
options->extra = extra;
|
587
601
|
|
588
602
|
memcpy(renderer, &toc_render, sizeof(struct mkd_renderer));
|
589
603
|
renderer->opaque = options;
|
590
604
|
}
|
591
605
|
|
592
606
|
void
|
593
|
-
|
607
|
+
sdhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags, void *extra)
|
594
608
|
{
|
595
|
-
static const char *xhtml_close = "/>\n";
|
596
|
-
static const char *html_close = ">\n";
|
597
|
-
|
598
609
|
static const struct mkd_renderer renderer_default = {
|
599
610
|
rndr_blockcode,
|
600
611
|
rndr_blockquote,
|
@@ -618,6 +629,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
|
|
618
629
|
rndr_raw_html,
|
619
630
|
rndr_triple_emphasis,
|
620
631
|
rndr_strikethrough,
|
632
|
+
rndr_superscript,
|
621
633
|
|
622
634
|
NULL,
|
623
635
|
rndr_normal_text,
|
@@ -631,7 +643,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
|
|
631
643
|
struct html_renderopt *options;
|
632
644
|
options = calloc(1, sizeof(struct html_renderopt));
|
633
645
|
options->flags = render_flags;
|
634
|
-
options->
|
646
|
+
options->extra = extra;
|
635
647
|
|
636
648
|
memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
|
637
649
|
renderer->opaque = options;
|
@@ -652,7 +664,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
|
|
652
664
|
}
|
653
665
|
|
654
666
|
void
|
655
|
-
|
667
|
+
sdhtml_free_renderer(struct mkd_renderer *renderer)
|
656
668
|
{
|
657
669
|
free(renderer->opaque);
|
658
670
|
}
|