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 CHANGED
@@ -1,61 +1,256 @@
1
- Ruby + Upskirt = Markdown that doesn't suck
2
- ===========================================
1
+ Redcarpet 2 is written with sugar, spice and everything nice
2
+ ============================================================
3
3
 
4
- Redcarpet is a Ruby wrapper for Upskirt. It is mostly based on Ryan
5
- Tomayko's RDiscount wrapper, and inspired by Rick Astley wearing a kilt.
4
+ Redcarpet is Ruby library for Markdown processing that smells like
5
+ butterflies and popcorn.
6
6
 
7
- Redcarpet is powered by the Upskirt library, which can be found at
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
- https://www.github.com/tanoku/upskirt
12
+ Redcarpet is powered by the Sundown library, which can be found at
10
13
 
11
- You might want to find out more about Upskirt to see what makes these Ruby
12
- bindings so awesome.
14
+ https://www.github.com/tanoku/sundown
13
15
 
14
- Credits
15
- -------
16
+ You might want to find out more about Sundown to see what makes this Ruby
17
+ library so awesome.
16
18
 
17
- * Natacha Porté, lady of Markdown
18
- * Vicent Martí, wannabe
19
- * With special thanks to Ryan Tomayko
19
+ This library is written by people
20
+ -------------------------------------------------------
20
21
 
21
- Install
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 is readily available as a Ruby gem:
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 Upskirt as a submodule) is available at GitHub:
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
- Usage
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 implements the basic protocol popularized by RedCloth:
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
- ~~~~~~ {ruby}
38
- require 'redcarpet'
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
- [Additional processing options](http://rdoc.info/github/tanoku/redcarpet/master/Redcarpet#autolink-instance_method) can be turned on when creating the
44
- Redcarpet object:
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
- ~~~~~~ {ruby}
47
- markdown = Redcarpet.new("Hello World!", :smart, :filter_html)
48
- ~~~~~~
238
+ ~~~~ ruby
239
+ # Mixin
240
+ class HTMLWithPants < Redcarpet::Render::HTML
241
+ include Redcarpet::Render::SmartyPants
242
+ end
49
243
 
50
- Note that by default, Redcarpet parses standard Markdown (with no extensions)
51
- and offers a sane subset of parse options which allow you to modify the rendering
52
- output and to enable MD extensions on a per-case basis.
244
+ # Standalone
245
+ Redcarpet::Render::SmartyPants.render("<p>Oh SmartyPants, you're so crazy...</p>")
246
+ ~~~~~
53
247
 
54
- Redcarpet also offers a wrapper class, `RedcarpetCompat` with the same flags
55
- and behavior as the RDiscount library, which acts as a drop-in replacement.
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
- License
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
- file 'redcarpet.gemspec' => FileList['Rakefile','lib/redcarpet.rb'] do |f|
91
+ task :update_gem do
95
92
  # read spec file and split out manifest section
96
- spec = File.read(f.name)
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(f.name, 'w') { |io| io.write(spec) }
111
- puts "updated #{f.name}"
108
+ File.open(GEMFILE, 'w') { |io| io.write(spec) }
109
+ puts "updated #{GEMFILE}"
112
110
  end
113
111
 
114
- desc 'Gather required Upskirt sources into extension directory'
115
- task :gather => 'upskirt/src/markdown.h' do |t|
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
- 'upskirt/src/{markdown,buffer,array,autolink}.h',
119
- 'upskirt/src/{markdown,buffer,array,autolink}.c',
120
- 'upskirt/html/{html,html_smartypants}.c',
121
- 'upskirt/html/html.h',
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 'upskirt/src/markdown.h' do |t|
129
- abort "The Upskirt submodule is required."
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
@@ -10,4 +10,4 @@ if ARGV.include?('--help')
10
10
  end
11
11
 
12
12
  require 'redcarpet'
13
- STDOUT.write(Redcarpet.new(ARGF.read).to_html)
13
+ STDOUT.write(Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(ARGF.read))
@@ -22,7 +22,7 @@
22
22
  #include <ctype.h>
23
23
 
24
24
  int
25
- is_safe_link(const char *link, size_t link_len)
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
- ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
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
- ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
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
- ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
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 (!is_safe_link(data - rewind, size + rewind))
239
+ if (!sd_autolink_issafe(data - rewind, size + rewind))
240
240
  return 0;
241
241
  link_end = STRLEN("://");
242
242
 
@@ -15,18 +15,21 @@
15
15
  */
16
16
 
17
17
  #ifndef UPSKIRT_AUTOLINK_H
18
- #define UPSKIRT_AUTOLINK_H_H
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
- ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
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
- ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
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
- ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
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
- /* upshtml_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
51
+ /* sdhtml_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
49
52
  void
50
- upshtml_escape(struct buf *ob, const char *src, size_t size)
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
- static int
70
- is_html_tag(struct buf *tag, const char *tagname)
72
+ int
73
+ sdhtml_tag(const char *tag_data, size_t tag_size, const char *tagname)
71
74
  {
72
- size_t i = 0;
73
-
74
- if (i < tag->size && tag->data[0] != '<')
75
- return 0;
75
+ size_t i;
76
+ int closed = 0;
76
77
 
77
- i++;
78
+ if (tag_size < 3 || tag_data[0] != '<')
79
+ return HTML_TAG_NONE;
78
80
 
79
- while (i < tag->size && isspace(tag->data[i]))
80
- i++;
81
+ i = 1;
81
82
 
82
- if (i < tag->size && tag->data[i] == '/')
83
- i++;
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 < tag->size; ++i, ++tagname) {
88
+ for (; i < tag_size; ++i, ++tagname) {
89
89
  if (*tagname == 0)
90
90
  break;
91
91
 
92
- if (tag->data[i] != *tagname)
93
- return 0;
92
+ if (tag_data[i] != *tagname)
93
+ return HTML_TAG_NONE;
94
94
  }
95
95
 
96
- if (i == tag->size)
97
- return 0;
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 (isspace(tag->data[i]) || tag->data[i] == '>');
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
- !is_safe_link(link->data, link->size) &&
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
- upshtml_escape(ob, link->data + 7, link->size - 7);
133
+ sdhtml_escape(ob, link->data + 7, link->size - 7);
131
134
  } else {
132
- upshtml_escape(ob, link->data, link->size);
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
- upshtml_escape(ob, lang->data + org, i - org);
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
- upshtml_escape(ob, text->data, text->size);
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
- upshtml_escape(ob, lang->data + 1, i - 1);
210
+ sdhtml_escape(ob, lang->data + 1, i - 1);
208
211
  else
209
- upshtml_escape(ob, lang->data, i);
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
- upshtml_escape(ob, text->data, text->size);
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) upshtml_escape(ob, text->data, text->size);
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 && !is_safe_link(link->data, link->size))
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
- upshtml_escape(ob, title->data, title->size); }
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
- if (i >= text->size)
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
- BUFPUTSL(ob, "<br");
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
- BUFPUTSL(ob, "<hr");
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
- upshtml_escape(ob, link->data, link->size);
423
+ sdhtml_escape(ob, link->data, link->size);
410
424
  BUFPUTSL(ob, "\" alt=\"");
411
425
  if (alt && alt->size)
412
- upshtml_escape(ob, alt->data, alt->size);
426
+ sdhtml_escape(ob, alt->data, alt->size);
413
427
  if (title && title->size) {
414
428
  BUFPUTSL(ob, "\" title=\"");
415
- upshtml_escape(ob, title->data, title->size); }
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
- static int
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 && is_html_tag(text, "style"))
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 && is_html_tag(text, "a"))
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 && is_html_tag(text, "img"))
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, "\n</thead><tbody>\n");
463
+ BUFPUTSL(ob, "</thead><tbody>\n");
460
464
  if (body)
461
465
  bufput(ob, body->data, body->size);
462
- BUFPUTSL(ob, "\n</tbody></table>");
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, "\n</tr>");
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
- upshtml_escape(ob, text->data, text->size);
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
- upshtml_toc_renderer(struct mkd_renderer *renderer)
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
- upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
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->close_tag = (render_flags & HTML_USE_XHTML) ? xhtml_close : html_close;
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
- upshtml_free_renderer(struct mkd_renderer *renderer)
667
+ sdhtml_free_renderer(struct mkd_renderer *renderer)
656
668
  {
657
669
  free(renderer->opaque);
658
670
  }