Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Redcarpet 2

  • Loading branch information...
commit 427146c4b6eb0f58f566a77d17467a67da4f7e3c 1 parent 9562d1b
@vmg vmg authored
View
269 README.markdown
@@ -1,61 +1,256 @@
-Ruby + Upskirt = Markdown that doesn't suck
-===========================================
+Redcarpet 2 is written with sugar, spice and everything nice
+============================================================
-Redcarpet is a Ruby wrapper for Upskirt. It is mostly based on Ryan
-Tomayko's RDiscount wrapper, and inspired by Rick Astley wearing a kilt.
+Redcarpet is Ruby library for Markdown processing that smells like
+butterflies and popcorn.
-Redcarpet is powered by the Upskirt library, which can be found at
+Redcarpet used to be a drop-in replacement for Redcloth. This is no longer the
+case since version 2 -- it now has its own API, but retains the old name. Yes,
+that does mean that Redcarpet 2 is not backwards-compatible with the 1.X
+versions.
- https://www.github.com/tanoku/upskirt
+Redcarpet is powered by the Sundown library, which can be found at
-You might want to find out more about Upskirt to see what makes these Ruby
-bindings so awesome.
+ https://www.github.com/tanoku/sundown
-Credits
--------
+You might want to find out more about Sundown to see what makes this Ruby
+library so awesome.
-* Natacha Porté, lady of Markdown
-* Vicent Martí, wannabe
-* With special thanks to Ryan Tomayko
+This library is written by people
+-------------------------------------------------------
-Install
--------
+Redcarpet 2 has been rewritten from scratch by Vicent Marti (@tanoku). Why
+are you not following me on Twitter?
-Redcarpet is readily available as a Ruby gem:
+Redcarpet would not be possible without the Sundown library and its authors
+(Natacha Porté, Vicent Martí, and its many awesome contributors).
+
+You can totally install it as a Gem
+-----------------------------------
+
+Redcarpet is readily available as a Ruby gem. It will build some native
+extensions, but the parser is standalone and requires no installed libraries.
$ [sudo] gem install redcarpet
-The Redcarpet source (including Upskirt as a submodule) is available at GitHub:
+The Redcarpet source (including Sundown as a submodule) is available at GitHub:
$ git clone git://github.com/tanoku/redcarpet.git
-Usage
------
+And it's like *really* simple to use
+------------------------------------
+
+The core of the Redcarpet library is the `Redcarpet::Markdown` class. Each
+instance of the class is attached to a `Renderer` object; the Markdown class
+performs parsing of a document and uses the attached renderer to generate
+output.
+
+The `Markdown` object is encouraged to be instantiated once with the required
+settings, and reused between parses.
+
+ Markdown.new(renderer, extensions={})
+
+ Initializes a Markdown parser
+
+ renderer - a renderer object, inheriting from Redcarpet::Render::Base.
+ If the given object has not been instantiated, the library
+ will do it with default arguments.
+
+ extensions - a hash containing the Markdown extesions which the parser
+ will identify. The following extensions are accepted:
+
+ :no_intra_emphasis - do not parse emphasis inside of words.
+ Strings such as `foo_bar_baz` will not generate `<em>`
+ tags.
+
+ :tables - parse tables, PHP-Markdown style
+
+ :fenced_code_blocks - parse fenced code blocks, PHP-Markdown
+ style .Blocks delimited with 3 or more `~` or backticks
+ will be considered as code, without the need to be
+ indented. An optional language name may be added at the
+ end of the opening fence for the code block
+
+ :autolink - parse links even when they are not enclosed in
+ `<>` characters. Autolinks for the http, https and ftp
+ protocols will be automatically detected. Email addresses
+ are also handled, and http links without protocol, but
+ starting with `www.`
+
+ :strikethrough - parse strikethrough, PHP-Markdown style
+ Two `~` characters mark the start of a strikethrough,
+ e.g. `this is ~~good~~ bad`
+
+ :lax_html_blocks - HTML blocks do not require to be surrounded
+ by an empty line as in the Markdown standard.
+
+ :space_after_headers - A space is always required between the
+ hash at the beggining of a header and its name, e.g.
+ `#this is my header` would not be a valid header.
+
+ :superscript - parse superscripts after the `^` character;
+ contiguous superscripts are nested together, and complex
+ values can be enclosed in parenthesis,
+ e.g. `this is the 2^(nd) time`
+
+ Example:
+
+ markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
+ :autolink => true, :space_after_headers => true)
+
+Rendering with the `Markdown` object is done through `Markdown#render`.
+Unlike in the RedCloth API, the text to render is passed as an argument
+and not stored inside the `Markdown` instance, to encourage reusability.
+
+ Markdown.render(text)
+
+ Render a Markdown document with the attached renderer
+
+ text - a Markdown document
+
+ Example:
+
+ markdown.render("This is *bongos*, indeed.")
+ #=> "<p>This is <em>bongos</em>, indeed</p>"
+
+
+Darling, I packed you a couple renderers for lunch
+--------------------------------------------------
+
+Redcarpet comes with two built-in renderers, `Redcarpet::Render::HTML` and
+`Redcarpet::Render::XHTML`, which output HTML and XHTML, respectively. These
+renderers are actually implemented in C, and hence offer a brilliant
+performance, several degrees of magnitude faster than other Ruby Markdown
+solutions.
+
+The `HTML` renderer has an alternate version, `Redcarpet::Render::HTML_toc`,
+which will output a table of contents in HTML based on the headers of the
+Markdown document.
+
+Furthermore, the abstract base class `Redcarpet::Render::Base` can be used
+to write a custom renderer purely in Ruby, or extending an existing renderer.
+See the following section for more information.
+
+
+And you can even cook your own
+------------------------------
+
+Custom renderers are created by inheriting from an existing renderer. The
+builtin renderers, `HTML` and `XHTML` may be extended as such:
+
+~~~~~ ruby
+# create a custom renderer that allows highlighting of code blocks
+class HTMLwithAlbino < Redcarpet::Render::HTML
+ def block_code(code, language)
+ Albino.safe_colorize(code, language)
+ end
+end
+
+markdown = Redcarpet::Markdown.new(HTMLwithAlbino, :fenced_code_blocks => true)
+~~~~~
+
+But new renderers can also be created from scratch (see `lib/render_man.rb` for
+an example implementation of a Manpage renderer)
+
+~~~~~~ ruby
+class ManPage < Redcarpet::Render::Base
+ # you get the drill -- keep going from here
+end
+~~~~~
+
+The following instance methods may be implemented by the renderer:
+
+ # Block-level calls
+ # If the return value of the method is `nil`, the block
+ # will be skipped.
+ # If the method for a document element is not implemented,
+ # the block will be skipped.
+ #
+ # Example:
+ #
+ # class RenderWithoutCode < Redcarpet::Render::HTML
+ # def block_code(code, language)
+ # nil
+ # end
+ # end
+ #
+ block_code(code, language)
+ block_quote(quote)
+ block_html(raw_html)
+ header(text, header_level)
+ hrule()
+ list(contents, list_type)
+ list_item(text)
+ paragraph(text)
+ table(header, body)
+ table_row(content)
+ table_cell(content, alignment)
+
+ # Span-level calls
+ # A return value of `nil` will not output any data
+ # If the method for a document element is not implemented,
+ # the contents of the span will be copied verbatim
+ autolink(link, link_type)
+ codespan(code)
+ double_emphasis(text)
+ emphasis(text)
+ image(link, title, alt_text)
+ linebreak()
+ link(link, title, content)
+ raw_html(raw_html)
+ triple_emphasis(text)
+ strikethrough(text)
+ superscript(text)
+
+ # Low level rendering
+ entity(text)
+ normal_text(text)
+
+ # Header of the document
+ # Rendered before any another elements
+ doc_header()
+
+ # Footer of the document
+ # Rendered after all the other elements
+ doc_footer()
+
+ # Pre/post-process
+ # Special callback: preprocess or postprocess the whole
+ # document before or after the rendering process begins
+ preprocess(full_document)
+ postprocess(full_document)
+
+
+Also, now our Pants are much smarter
+------------------------------------
-Redcarpet implements the basic protocol popularized by RedCloth:
+Redcarpet 2 comes with a standalone [SmartyPants](
+http://daringfireball.net/projects/smartypants/) implementation. It is fully
+compilant with the original implementation. It is the fastest SmartyPants
+parser there is, with a difference of several orders of magnitude.
-~~~~~~ {ruby}
-require 'redcarpet'
-markdown = Redcarpet.new("Hello World!")
-puts markdown.to_html
-~~~~~~
+The SmartyPants parser can be found in `Redcarpet::Render::SmartyPants`. It has
+been implemented as a module, so it can be used standalone or as a mixin.
-[Additional processing options](http://rdoc.info/github/tanoku/redcarpet/master/Redcarpet#autolink-instance_method) can be turned on when creating the
-Redcarpet object:
+When mixed with a Renderer class, it will override the `postprocess` method
+to perform SmartyPants replacements once the rendering is complete
-~~~~~~ {ruby}
-markdown = Redcarpet.new("Hello World!", :smart, :filter_html)
-~~~~~~
+~~~~ ruby
+# Mixin
+class HTMLWithPants < Redcarpet::Render::HTML
+ include Redcarpet::Render::SmartyPants
+end
-Note that by default, Redcarpet parses standard Markdown (with no extensions)
-and offers a sane subset of parse options which allow you to modify the rendering
-output and to enable MD extensions on a per-case basis.
+# Standalone
+Redcarpet::Render::SmartyPants.render("<p>Oh SmartyPants, you're so crazy...</p>")
+~~~~~
-Redcarpet also offers a wrapper class, `RedcarpetCompat` with the same flags
-and behavior as the RDiscount library, which acts as a drop-in replacement.
+SmartyPants works on top of already-rendered HTML, and will ignore replacements
+inside the content of HTML tags and inside specific HTML blocks such as
+`<code>` or `<pre>`.
-License
--------
+Boring legal stuff
+------------------
Copyright (c) 2011, Vicent Martí
View
28 Rakefile
@@ -70,9 +70,6 @@ task :install => package('.gem') do
sh "gem install #{package('.gem')}"
end
-desc 'Update the gemspec'
-task :update_gem => file('redcarpet.gemspec')
-
directory 'pkg/'
file package('.gem') => %w[pkg/ redcarpet.gemspec] + $spec.files do |f|
@@ -91,9 +88,10 @@ def source_version
line.match(/.*VERSION = '(.*)'/)[1]
end
-file 'redcarpet.gemspec' => FileList['Rakefile','lib/redcarpet.rb'] do |f|
+task :update_gem do
# read spec file and split out manifest section
- spec = File.read(f.name)
+ GEMFILE = 'redcarpet.gemspec'
+ spec = File.read(GEMFILE)
head, manifest, tail = spec.split(" # = MANIFEST =\n")
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
@@ -107,26 +105,26 @@ file 'redcarpet.gemspec' => FileList['Rakefile','lib/redcarpet.rb'] do |f|
# piece file back together and write...
manifest = " s.files = %w[\n#{files}\n ]\n"
spec = [head,manifest,tail].join(" # = MANIFEST =\n")
- File.open(f.name, 'w') { |io| io.write(spec) }
- puts "updated #{f.name}"
+ File.open(GEMFILE, 'w') { |io| io.write(spec) }
+ puts "updated #{GEMFILE}"
end
-desc 'Gather required Upskirt sources into extension directory'
-task :gather => 'upskirt/src/markdown.h' do |t|
+desc 'Gather required Sundown sources into extension directory'
+task :gather => 'sundown/src/markdown.h' do |t|
files =
FileList[
- 'upskirt/src/{markdown,buffer,array,autolink}.h',
- 'upskirt/src/{markdown,buffer,array,autolink}.c',
- 'upskirt/html/{html,html_smartypants}.c',
- 'upskirt/html/html.h',
+ 'sundown/src/{markdown,buffer,array,autolink}.h',
+ 'sundown/src/{markdown,buffer,array,autolink}.c',
+ 'sundown/html/{html,html_smartypants}.c',
+ 'sundown/html/html.h',
]
cp files, 'ext/redcarpet/',
:preserve => true,
:verbose => true
end
-file 'upskirt/src/markdown.h' do |t|
- abort "The Upskirt submodule is required."
+file 'sundown/src/markdown.h' do |t|
+ abort "The Sundown submodule is required."
end
View
2  bin/redcarpet
@@ -10,4 +10,4 @@ if ARGV.include?('--help')
end
require 'redcarpet'
-STDOUT.write(Redcarpet.new(ARGF.read).to_html)
+STDOUT.write(Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(ARGF.read))
View
10 ext/redcarpet/autolink.c
@@ -22,7 +22,7 @@
#include <ctype.h>
int
-is_safe_link(const char *link, size_t link_len)
+sd_autolink_issafe(const char *link, size_t link_len)
{
static const size_t valid_uris_count = 4;
static const char *valid_uris[] = {
@@ -147,7 +147,7 @@ check_domain(char *data, size_t size)
}
size_t
-ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
+sd_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end;
@@ -177,7 +177,7 @@ ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset,
}
size_t
-ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
+sd_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end, rewind;
int nb = 0, np = 0;
@@ -226,7 +226,7 @@ ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offse
}
size_t
-ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
+sd_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size)
{
size_t link_end, rewind = 0, domain_len;
@@ -236,7 +236,7 @@ ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset,
while (rewind < offset && isalpha(data[-rewind - 1]))
rewind++;
- if (!is_safe_link(data - rewind, size + rewind))
+ if (!sd_autolink_issafe(data - rewind, size + rewind))
return 0;
link_end = STRLEN("://");
View
11 ext/redcarpet/autolink.h
@@ -15,18 +15,21 @@
*/
#ifndef UPSKIRT_AUTOLINK_H
-#define UPSKIRT_AUTOLINK_H_H
+#define UPSKIRT_AUTOLINK_H
#include "buffer.h"
+extern int
+sd_autolink_issafe(const char *link, size_t link_len);
+
extern size_t
-ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
+sd_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
extern size_t
-ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
+sd_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
extern size_t
-ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
+sd_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset, size_t size);
#endif
View
154 ext/redcarpet/html.c
@@ -23,14 +23,17 @@
#include <stdio.h>
#include <ctype.h>
+#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)
+
struct html_renderopt {
+ void *extra;
+
struct {
int header_count;
int current_level;
} toc_data;
unsigned int flags;
- const char *close_tag;
};
static inline void
@@ -45,9 +48,9 @@ put_scaped_char(struct buf *ob, char c)
}
}
-/* upshtml_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
+/* sdhtml_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
void
-upshtml_escape(struct buf *ob, const char *src, size_t size)
+sdhtml_escape(struct buf *ob, const char *src, size_t size)
{
size_t i = 0, org;
while (i < size) {
@@ -66,37 +69,37 @@ upshtml_escape(struct buf *ob, const char *src, size_t size)
}
}
-static int
-is_html_tag(struct buf *tag, const char *tagname)
+int
+sdhtml_tag(const char *tag_data, size_t tag_size, const char *tagname)
{
- size_t i = 0;
-
- if (i < tag->size && tag->data[0] != '<')
- return 0;
+ size_t i;
+ int closed = 0;
- i++;
+ if (tag_size < 3 || tag_data[0] != '<')
+ return HTML_TAG_NONE;
- while (i < tag->size && isspace(tag->data[i]))
- i++;
+ i = 1;
- if (i < tag->size && tag->data[i] == '/')
- i++;
-
- while (i < tag->size && isspace(tag->data[i]))
+ if (tag_data[i] == '/') {
+ closed = 1;
i++;
+ }
- for (; i < tag->size; ++i, ++tagname) {
+ for (; i < tag_size; ++i, ++tagname) {
if (*tagname == 0)
break;
- if (tag->data[i] != *tagname)
- return 0;
+ if (tag_data[i] != *tagname)
+ return HTML_TAG_NONE;
}
- if (i == tag->size)
- return 0;
+ if (i == tag_size)
+ return HTML_TAG_NONE;
+
+ if (isspace(tag_data[i]) || tag_data[i] == '>')
+ return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
- return (isspace(tag->data[i]) || tag->data[i] == '>');
+ return HTML_TAG_NONE;
}
/********************
@@ -111,7 +114,7 @@ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *op
return 0;
if ((options->flags & HTML_SAFELINK) != 0 &&
- !is_safe_link(link->data, link->size) &&
+ !sd_autolink_issafe(link->data, link->size) &&
type != MKDA_EMAIL)
return 0;
@@ -127,9 +130,9 @@ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *op
* want to print the `mailto:` prefix
*/
if (bufprefix(link, "mailto:") == 0) {
- upshtml_escape(ob, link->data + 7, link->size - 7);
+ sdhtml_escape(ob, link->data + 7, link->size - 7);
} else {
- upshtml_escape(ob, link->data, link->size);
+ sdhtml_escape(ob, link->data, link->size);
}
BUFPUTSL(ob, "</a>");
@@ -159,7 +162,7 @@ rndr_blockcode(struct buf *ob, struct buf *text, struct buf *lang, void *opaque)
org++;
if (cls) bufputc(ob, ' ');
- upshtml_escape(ob, lang->data + org, i - org);
+ sdhtml_escape(ob, lang->data + org, i - org);
}
}
@@ -168,7 +171,7 @@ rndr_blockcode(struct buf *ob, struct buf *text, struct buf *lang, void *opaque)
BUFPUTSL(ob, "<pre><code>");
if (text)
- upshtml_escape(ob, text->data, text->size);
+ sdhtml_escape(ob, text->data, text->size);
BUFPUTSL(ob, "</code></pre>\n");
}
@@ -204,16 +207,16 @@ rndr_blockcode_github(struct buf *ob, struct buf *text, struct buf *lang, void *
i++;
if (lang->data[0] == '.')
- upshtml_escape(ob, lang->data + 1, i - 1);
+ sdhtml_escape(ob, lang->data + 1, i - 1);
else
- upshtml_escape(ob, lang->data, i);
+ sdhtml_escape(ob, lang->data, i);
BUFPUTSL(ob, "\"><code>");
} else
BUFPUTSL(ob, "<pre><code>");
if (text)
- upshtml_escape(ob, text->data, text->size);
+ sdhtml_escape(ob, text->data, text->size);
BUFPUTSL(ob, "</code></pre>\n");
}
@@ -221,16 +224,17 @@ rndr_blockcode_github(struct buf *ob, struct buf *text, struct buf *lang, void *
static void
rndr_blockquote(struct buf *ob, struct buf *text, void *opaque)
{
+ if (ob->size) bufputc(ob, '\n');
BUFPUTSL(ob, "<blockquote>\n");
if (text) bufput(ob, text->data, text->size);
- BUFPUTSL(ob, "</blockquote>");
+ BUFPUTSL(ob, "</blockquote>\n");
}
static int
rndr_codespan(struct buf *ob, struct buf *text, void *opaque)
{
BUFPUTSL(ob, "<code>");
- if (text) upshtml_escape(ob, text->data, text->size);
+ if (text) sdhtml_escape(ob, text->data, text->size);
BUFPUTSL(ob, "</code>");
return 1;
}
@@ -270,6 +274,14 @@ rndr_emphasis(struct buf *ob, struct buf *text, void *opaque)
return 1;
}
+static int
+rndr_linebreak(struct buf *ob, void *opaque)
+{
+ struct html_renderopt *options = opaque;
+ bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");
+ return 1;
+}
+
static void
rndr_header(struct buf *ob, struct buf *text, int level, void *opaque)
{
@@ -292,14 +304,14 @@ rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *conte
{
struct html_renderopt *options = opaque;
- if ((options->flags & HTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
+ if ((options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
return 0;
BUFPUTSL(ob, "<a href=\"");
if (link && link->size) bufput(ob, link->data, link->size);
if (title && title->size) {
BUFPUTSL(ob, "\" title=\"");
- upshtml_escape(ob, title->data, title->size); }
+ sdhtml_escape(ob, title->data, title->size); }
BUFPUTSL(ob, "\">");
if (content && content->size) bufput(ob, content->data, content->size);
BUFPUTSL(ob, "</a>");
@@ -353,11 +365,14 @@ rndr_paragraph(struct buf *ob, struct buf *text, void *opaque)
if (i > org)
bufput(ob, text->data + org, i - org);
- if (i >= text->size)
+ /*
+ * do not insert a line break if this newline
+ * is the last character on the paragraph
+ */
+ if (i >= text->size - 1)
break;
-
- BUFPUTSL(ob, "<br");
- bufputs(ob, options->close_tag);
+
+ rndr_linebreak(ob, opaque);
i++;
}
} else {
@@ -396,8 +411,7 @@ rndr_hrule(struct buf *ob, void *opaque)
{
struct html_renderopt *options = opaque;
if (ob->size) bufputc(ob, '\n');
- BUFPUTSL(ob, "<hr");
- bufputs(ob, options->close_tag);
+ bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
}
static int
@@ -406,25 +420,15 @@ rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt,
struct html_renderopt *options = opaque;
if (!link || !link->size) return 0;
BUFPUTSL(ob, "<img src=\"");
- upshtml_escape(ob, link->data, link->size);
+ sdhtml_escape(ob, link->data, link->size);
BUFPUTSL(ob, "\" alt=\"");
if (alt && alt->size)
- upshtml_escape(ob, alt->data, alt->size);
+ sdhtml_escape(ob, alt->data, alt->size);
if (title && title->size) {
BUFPUTSL(ob, "\" title=\"");
- upshtml_escape(ob, title->data, title->size); }
-
- bufputc(ob, '"');
- bufputs(ob, options->close_tag);
- return 1;
-}
+ sdhtml_escape(ob, title->data, title->size); }
-static int
-rndr_linebreak(struct buf *ob, void *opaque)
-{
- struct html_renderopt *options = opaque;
- BUFPUTSL(ob, "<br");
- bufputs(ob, options->close_tag);
+ bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
return 1;
}
@@ -436,13 +440,13 @@ rndr_raw_html(struct buf *ob, struct buf *text, void *opaque)
if ((options->flags & HTML_SKIP_HTML) != 0)
return 1;
- if ((options->flags & HTML_SKIP_STYLE) != 0 && is_html_tag(text, "style"))
+ if ((options->flags & HTML_SKIP_STYLE) != 0 && sdhtml_tag(text->data, text->size, "style"))
return 1;
- if ((options->flags & HTML_SKIP_LINKS) != 0 && is_html_tag(text, "a"))
+ if ((options->flags & HTML_SKIP_LINKS) != 0 && sdhtml_tag(text->data, text->size, "a"))
return 1;
- if ((options->flags & HTML_SKIP_IMAGES) != 0 && is_html_tag(text, "img"))
+ if ((options->flags & HTML_SKIP_IMAGES) != 0 && sdhtml_tag(text->data, text->size, "img"))
return 1;
bufput(ob, text->data, text->size);
@@ -456,26 +460,24 @@ rndr_table(struct buf *ob, struct buf *header, struct buf *body, void *opaque)
BUFPUTSL(ob, "<table><thead>\n");
if (header)
bufput(ob, header->data, header->size);
- BUFPUTSL(ob, "\n</thead><tbody>\n");
+ BUFPUTSL(ob, "</thead><tbody>\n");
if (body)
bufput(ob, body->data, body->size);
- BUFPUTSL(ob, "\n</tbody></table>");
+ BUFPUTSL(ob, "</tbody></table>\n");
}
static void
rndr_tablerow(struct buf *ob, struct buf *text, void *opaque)
{
- if (ob->size) bufputc(ob, '\n');
BUFPUTSL(ob, "<tr>\n");
if (text)
bufput(ob, text->data, text->size);
- BUFPUTSL(ob, "\n</tr>");
+ BUFPUTSL(ob, "</tr>\n");
}
static void
rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
{
- if (ob->size) bufputc(ob, '\n');
switch (align) {
case MKD_TABLE_ALIGN_L:
BUFPUTSL(ob, "<td align=\"left\">");
@@ -496,14 +498,24 @@ rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
if (text)
bufput(ob, text->data, text->size);
- BUFPUTSL(ob, "</td>");
+ BUFPUTSL(ob, "</td>\n");
+}
+
+static int
+rndr_superscript(struct buf *ob, struct buf *text, void *opaque)
+{
+ if (!text || !text->size) return 0;
+ BUFPUTSL(ob, "<sup>");
+ bufput(ob, text->data, text->size);
+ BUFPUTSL(ob, "</sup>");
+ return 1;
}
static void
rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
{
if (text)
- upshtml_escape(ob, text->data, text->size);
+ sdhtml_escape(ob, text->data, text->size);
}
static void
@@ -546,7 +558,7 @@ toc_finalize(struct buf *ob, void *opaque)
}
void
-upshtml_toc_renderer(struct mkd_renderer *renderer)
+sdhtml_toc_renderer(struct mkd_renderer *renderer, void *extra)
{
static const struct mkd_renderer toc_render = {
NULL,
@@ -571,6 +583,7 @@ upshtml_toc_renderer(struct mkd_renderer *renderer)
NULL,
rndr_triple_emphasis,
rndr_strikethrough,
+ rndr_superscript,
NULL,
NULL,
@@ -584,17 +597,15 @@ upshtml_toc_renderer(struct mkd_renderer *renderer)
struct html_renderopt *options;
options = calloc(1, sizeof(struct html_renderopt));
options->flags = HTML_TOC;
+ options->extra = extra;
memcpy(renderer, &toc_render, sizeof(struct mkd_renderer));
renderer->opaque = options;
}
void
-upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
+sdhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags, void *extra)
{
- static const char *xhtml_close = "/>\n";
- static const char *html_close = ">\n";
-
static const struct mkd_renderer renderer_default = {
rndr_blockcode,
rndr_blockquote,
@@ -618,6 +629,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
rndr_raw_html,
rndr_triple_emphasis,
rndr_strikethrough,
+ rndr_superscript,
NULL,
rndr_normal_text,
@@ -631,7 +643,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
struct html_renderopt *options;
options = calloc(1, sizeof(struct html_renderopt));
options->flags = render_flags;
- options->close_tag = (render_flags & HTML_USE_XHTML) ? xhtml_close : html_close;
+ options->extra = extra;
memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
renderer->opaque = options;
@@ -652,7 +664,7 @@ upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
}
void
-upshtml_free_renderer(struct mkd_renderer *renderer)
+sdhtml_free_renderer(struct mkd_renderer *renderer)
{
free(renderer->opaque);
}
View
19 ext/redcarpet/html.h
@@ -34,20 +34,29 @@ typedef enum {
HTML_USE_XHTML = (1 << 11),
} render_mode;
+typedef enum {
+ HTML_TAG_NONE = 0,
+ HTML_TAG_OPEN,
+ HTML_TAG_CLOSE,
+} html_tag;
+
void
-upshtml_escape(struct buf *ob, const char *src, size_t size);
+sdhtml_escape(struct buf *ob, const char *src, size_t size);
+
+int
+sdhtml_tag(const char *tag_data, size_t tag_size, const char *tagname);
extern void
-upshtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags);
+sdhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags, void *extra);
extern void
-upshtml_toc_renderer(struct mkd_renderer *renderer);
+sdhtml_toc_renderer(struct mkd_renderer *renderer, void *extra);
extern void
-upshtml_free_renderer(struct mkd_renderer *renderer);
+sdhtml_free_renderer(struct mkd_renderer *renderer);
extern void
-upshtml_smartypants(struct buf *ob, struct buf *text);
+sdhtml_smartypants(struct buf *ob, struct buf *text);
#endif
View
46 ext/redcarpet/html_smartypants.c
@@ -161,16 +161,14 @@ smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, char previ
static size_t
smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, char previous_char, const char *text, size_t size)
{
- if (size >= 2) {
- if (text[1] == '-') {
- BUFPUTSL(ob, "&mdash;");
- return 1;
- }
+ if (size >= 3 && text[1] == '-' && text[2] == '-') {
+ BUFPUTSL(ob, "&mdash;");
+ return 2;
+ }
- if (word_boundary(previous_char) && word_boundary(text[1])) {
- BUFPUTSL(ob, "&ndash;");
- return 0;
- }
+ if (size >= 2 && text[1] == '-') {
+ BUFPUTSL(ob, "&ndash;");
+ return 1;
}
bufputc(ob, text[0]);
@@ -264,11 +262,37 @@ smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, char previ
static size_t
smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, char previous_char, const char *text, size_t size)
{
- size_t i = 0;
+ static const char *skip_tags[] = {"pre", "code", "kbd", "script"};
+ static const size_t skip_tags_count = 4;
+
+ size_t tag, i = 0;
while (i < size && text[i] != '>')
i++;
+ for (tag = 0; tag < skip_tags_count; ++tag) {
+ if (sdhtml_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN)
+ break;
+ }
+
+ if (tag < skip_tags_count) {
+ for (;;) {
+ while (i < size && text[i] != '<')
+ i++;
+
+ if (i == size)
+ break;
+
+ if (sdhtml_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE)
+ break;
+
+ i++;
+ }
+
+ while (i < size && text[i] != '>')
+ i++;
+ }
+
bufput(ob, text, i + 1);
return i;
}
@@ -304,7 +328,7 @@ static struct {
#endif
void
-upshtml_smartypants(struct buf *ob, struct buf *text)
+sdhtml_smartypants(struct buf *ob, struct buf *text)
{
size_t i;
struct smartypants_data smrt = {0, 0};
View
238 ext/redcarpet/markdown.c
@@ -60,6 +60,7 @@ static size_t char_autolink_url(struct buf *ob, struct render *rndr, char *data,
static size_t char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
static size_t char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
static size_t char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
+static size_t char_superscript(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
enum markdown_char_t {
MD_CHAR_NONE = 0,
@@ -72,7 +73,8 @@ enum markdown_char_t {
MD_CHAR_ENTITITY,
MD_CHAR_AUTOLINK_URL,
MD_CHAR_AUTOLINK_EMAIL,
- MD_CHAR_AUTOLINK_WWW
+ MD_CHAR_AUTOLINK_WWW,
+ MD_CHAR_SUPERSCRIPT,
};
static char_trigger markdown_char_ptrs[] = {
@@ -87,6 +89,7 @@ static char_trigger markdown_char_ptrs[] = {
&char_autolink_url,
&char_autolink_email,
&char_autolink_www,
+ &char_superscript,
};
/* render • structure containing one particular render */
@@ -145,13 +148,14 @@ static struct html_tag block_tags[] = {
{ "h6", 2 },
{ "ol", 2 },
{ "ul", 2 },
-/*10*/ { "del", 3 },
+ { "del", 3 }, /* 10 */
{ "div", 3 },
-/*12*/ { "ins", 3 },
+ { "ins", 3 }, /* 12 */
{ "pre", 3 },
{ "form", 4 },
{ "math", 4 },
{ "table", 5 },
+ { "figure", 6 },
{ "iframe", 6 },
{ "script", 6 },
{ "fieldset", 8 },
@@ -224,7 +228,7 @@ find_block_tag(char *data, size_t size)
while (i < size && ((data[i] >= '0' && data[i] <= '9')
|| (data[i] >= 'A' && data[i] <= 'Z')
|| (data[i] >= 'a' && data[i] <= 'z')))
- i += 1;
+ i++;
if (i >= size) return 0;
/* binary search of the tag */
@@ -317,7 +321,7 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
if (data[i] == '\\') i += 2;
else if (data[i] == '>' || data[i] == '\'' ||
data[i] == '"' || isspace(data[i])) break;
- else i += 1;
+ else i++;
}
if (i >= size) return 0;
@@ -327,7 +331,7 @@ tag_length(char *data, size_t size, enum mkd_autolink *autolink)
}
/* looking for sometinhg looking like a tag end */
- while (i < size && data[i] != '>') i += 1;
+ while (i < size && data[i] != '>') i++;
if (i >= size) return 0;
return i + 1;
}
@@ -379,47 +383,72 @@ find_emph_char(char *data, size_t size, char c)
size_t i = 1;
while (i < size) {
- while (i < size && data[i] != c
- && data[i] != '`' && data[i] != '[')
- i += 1;
- if (data[i] == c) return i;
+ while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')
+ i++;
+
+ if (i == size)
+ return 0;
+
+ if (data[i] == c)
+ return i;
/* not counting escaped chars */
- if (i && data[i - 1] == '\\') { i += 1; continue; }
+ if (i && data[i - 1] == '\\') {
+ i++; continue;
+ }
/* skipping a code span */
if (data[i] == '`') {
size_t tmp_i = 0;
- i += 1;
+
+ i++;
while (i < size && data[i] != '`') {
if (!tmp_i && data[i] == c) tmp_i = i;
- i += 1; }
- if (i >= size) return tmp_i;
- i += 1; }
+ i++;
+ }
+
+ if (i >= size)
+ return tmp_i;
+ i++;
+ }
/* skipping a link */
else if (data[i] == '[') {
size_t tmp_i = 0;
char cc;
- i += 1;
+
+ i++;
while (i < size && data[i] != ']') {
if (!tmp_i && data[i] == c) tmp_i = i;
- i += 1; }
- i += 1;
- while (i < size && (data[i] == ' '
- || data[i] == '\t' || data[i] == '\n'))
- i += 1;
- if (i >= size) return tmp_i;
+ i++;
+ }
+
+ i++;
+ while (i < size && (data[i] == ' ' || data[i] == '\t' || data[i] == '\n'))
+ i++;
+
+ if (i >= size)
+ return tmp_i;
+
if (data[i] != '[' && data[i] != '(') { /* not a link*/
if (tmp_i) return tmp_i;
- else continue; }
+ else continue;
+ }
+
cc = data[i];
- i += 1;
+ i++;
while (i < size && data[i] != cc) {
if (!tmp_i && data[i] == c) tmp_i = i;
- i += 1; }
- if (i >= size) return tmp_i;
- i += 1; } }
+ i++;
+ }
+
+ if (i >= size)
+ return tmp_i;
+
+ i++;
+ }
+ }
+
return 0;
}
@@ -444,7 +473,7 @@ parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c
if (i >= size) return 0;
if (i + 1 < size && data[i + 1] == c) {
- i += 1;
+ i++;
continue;
}
@@ -668,7 +697,7 @@ char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size
end++;
if (end < size && data[end] == ';')
- end += 1; /* real entity */
+ end++; /* real entity */
else
return 0; /* lone '&' */
@@ -719,7 +748,7 @@ char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset
link = rndr_newbuf(rndr, BUFFER_SPAN);
- if ((link_len = ups_autolink__www(&rewind, link, data, offset, size)) > 0) {
+ if ((link_len = sd_autolink__www(&rewind, link, data, offset, size)) > 0) {
link_url = rndr_newbuf(rndr, BUFFER_SPAN);
BUFPUTSL(link_url, "http://");
bufput(link_url, link->data, link->size);
@@ -744,7 +773,7 @@ char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offs
link = rndr_newbuf(rndr, BUFFER_SPAN);
- if ((link_len = ups_autolink__email(&rewind, link, data, offset, size)) > 0) {
+ if ((link_len = sd_autolink__email(&rewind, link, data, offset, size)) > 0) {
ob->size -= rewind;
rndr->make.autolink(ob, link, MKDA_EMAIL, rndr->make.opaque);
}
@@ -764,7 +793,7 @@ char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset
link = rndr_newbuf(rndr, BUFFER_SPAN);
- if ((link_len = ups_autolink__url(&rewind, link, data, offset, size)) > 0) {
+ if ((link_len = sd_autolink__url(&rewind, link, data, offset, size)) > 0) {
ob->size -= rewind;
rndr->make.autolink(ob, link, MKDA_NORMAL, rndr->make.opaque);
}
@@ -791,7 +820,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
goto cleanup;
/* looking for the matching closing bracket */
- for (level = 1; i < size; i += 1) {
+ for (level = 1; i < size; i++) {
if (data[i] == '\n')
text_has_nl = 1;
@@ -812,7 +841,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
goto cleanup;
txt_e = i;
- i += 1;
+ i++;
/* skip any amount of whitespace or newline */
/* (this is much more laxist than original markdown syntax) */
@@ -822,7 +851,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
/* inline style link */
if (i < size && data[i] == '(') {
/* skipping initial whitespace */
- i += 1;
+ i++;
while (i < size && isspace(data[i]))
i++;
@@ -833,7 +862,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
while (i < size) {
if (data[i] == '\\') i += 2;
else if (data[i] == ')' || data[i] == '\'' || data[i] == '"') break;
- else i += 1;
+ else i++;
}
if (i >= size) goto cleanup;
@@ -847,7 +876,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
while (i < size) {
if (data[i] == '\\') i += 2;
else if (data[i] == ')') break;
- else i += 1;
+ else i++;
}
if (i >= size) goto cleanup;
@@ -892,7 +921,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
struct link_ref *lr;
/* looking for the id */
- i += 1;
+ i++;
link_b = i;
while (i < size && data[i] != ']') i++;
if (i >= size) goto cleanup;
@@ -928,7 +957,7 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
/* keeping link and title from link_ref */
link = lr->link;
title = lr->title;
- i += 1;
+ i++;
}
/* shortcut reference style link */
@@ -995,7 +1024,43 @@ char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t
return ret ? i : 0;
}
+static size_t
+char_superscript(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+{
+ size_t sup_start, sup_len;
+ struct buf *sup;
+
+ if (!rndr->make.superscript)
+ return 0;
+
+ if (size < 2)
+ return 0;
+
+ if (data[1] == '(') {
+ sup_start = sup_len = 2;
+
+ while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')
+ sup_len++;
+ if (sup_len == size)
+ return 0;
+ } else {
+ sup_start = sup_len = 1;
+
+ while (sup_len < size && !isspace(data[sup_len]))
+ sup_len++;
+ }
+
+ if (sup_len - sup_start == 0)
+ return (sup_start == 2) ? 3 : 0;
+
+ sup = rndr_newbuf(rndr, BUFFER_SPAN);
+ parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);
+ rndr->make.superscript(ob, sup, rndr->make.opaque);
+ rndr_popbuf(rndr, BUFFER_SPAN);
+
+ return (sup_start == 2) ? sup_len + 1 : sup_len;
+}
/*********************************
* BLOCK-LEVEL PARSING FUNCTIONS *
@@ -1006,7 +1071,7 @@ static size_t
is_empty(char *data, size_t size)
{
size_t i;
- for (i = 0; i < size && data[i] != '\n'; i += 1)
+ for (i = 0; i < size && data[i] != '\n'; i++)
if (data[i] != ' ' && data[i] != '\t') return 0;
return i + 1;
}
@@ -1020,9 +1085,9 @@ is_hrule(char *data, size_t size)
/* skipping initial spaces */
if (size < 3) return 0;
- if (data[0] == ' ') { i += 1;
- if (data[1] == ' ') { i += 1;
- if (data[2] == ' ') { i += 1; } } }
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
/* looking at the hrule char */
if (i + 2 >= size
@@ -1032,10 +1097,10 @@ is_hrule(char *data, size_t size)
/* the whole line must be the char or whitespace */
while (i < size && data[i] != '\n') {
- if (data[i] == c) n += 1;
+ if (data[i] == c) n++;
else if (data[i] != ' ' && data[i] != '\t')
return 0;
- i += 1; }
+ i++; }
return n >= 3;
}
@@ -1049,9 +1114,9 @@ is_codefence(char *data, size_t size, struct buf *syntax)
/* skipping initial spaces */
if (size < 3) return 0;
- if (data[0] == ' ') { i += 1;
- if (data[1] == ' ') { i += 1;
- if (data[2] == ' ') { i += 1; } } }
+ if (data[0] == ' ') { i++;
+ if (data[1] == ' ') { i++;
+ if (data[2] == ' ') { i++; } } }
/* looking at the hrule char */
if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))
@@ -1142,14 +1207,14 @@ is_headerline(char *data, size_t size)
/* test of level 1 header */
if (data[i] == '=') {
- for (i = 1; i < size && data[i] == '='; i += 1);
- while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1;
+ for (i = 1; i < size && data[i] == '='; i++);
+ while (i < size && (data[i] == ' ' || data[i] == '\t')) i++;
return (i >= size || data[i] == '\n') ? 1 : 0; }
/* test of level 2 header */
if (data[i] == '-') {
- for (i = 1; i < size && data[i] == '-'; i += 1);
- while (i < size && (data[i] == ' ' || data[i] == '\t')) i += 1;
+ for (i = 1; i < size && data[i] == '-'; i++);
+ while (i < size && (data[i] == ' ' || data[i] == '\t')) i++;
return (i >= size || data[i] == '\n') ? 2 : 0; }
return 0;
@@ -1160,9 +1225,9 @@ static size_t
prefix_quote(char *data, size_t size)
{
size_t i = 0;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
if (i < size && data[i] == '>') {
if (i + 1 < size && (data[i + 1] == ' ' || data[i+1] == '\t'))
return i + 2;
@@ -1185,11 +1250,11 @@ static size_t
prefix_oli(char *data, size_t size)
{
size_t i = 0;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
if (i >= size || data[i] < '0' || data[i] > '9') return 0;
- while (i < size && data[i] >= '0' && data[i] <= '9') i += 1;
+ while (i < size && data[i] >= '0' && data[i] <= '9') i++;
if (i + 1 >= size || data[i] != '.'
|| (data[i + 1] != ' ' && data[i + 1] != '\t')) return 0;
return i + 2;
@@ -1200,9 +1265,9 @@ static size_t
prefix_uli(char *data, size_t size)
{
size_t i = 0;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
- if (i < size && data[i] == ' ') i += 1;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
+ if (i < size && data[i] == ' ') i++;
if (i + 1 >= size
|| (data[i] != '*' && data[i] != '+' && data[i] != '-')
|| (data[i + 1] != ' ' && data[i + 1] != '\t'))
@@ -1363,7 +1428,7 @@ parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
break;
}
- for (end = beg + 1; end < size && data[end - 1] != '\n'; end += 1);
+ for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
if (beg < end) {
/* verbatim copy to the working buffer,
@@ -1677,10 +1742,10 @@ parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, in
if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
i = 3;
while (i < size && data[i] != '>')
- i += 1;
+ i++;
if (i + 1 < size) {
- i += 1;
+ i++;
j = is_empty(data + i, size - i);
if (j) {
work.size = i + j;
@@ -1991,34 +2056,34 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
/* id part: anything but a newline between brackets */
if (data[i] != '[') return 0;
- i += 1;
+ i++;
id_offset = i;
while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
- i += 1;
+ i++;
if (i >= end || data[i] != ']') return 0;
id_end = i;
/* spacer: colon (space | tab)* newline? (space | tab)* */
- i += 1;
+ i++;
if (i >= end || data[i] != ':') return 0;
- i += 1;
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
+ i++;
+ while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
- i += 1;
- if (i < end && data[i] == '\r' && data[i - 1] == '\n') i += 1; }
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
+ i++;
+ if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
+ while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
if (i >= end) return 0;
/* link: whitespace-free sequence, optionally between angle brackets */
- if (data[i] == '<') i += 1;
+ if (data[i] == '<') i++;
link_offset = i;
while (i < end && data[i] != ' ' && data[i] != '\t'
- && data[i] != '\n' && data[i] != '\r') i += 1;
+ && data[i] != '\n' && data[i] != '\r') i++;
if (data[i - 1] == '>') link_end = i - 1;
else link_end = i;
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1;
+ while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
if (i < end && data[i] != '\n' && data[i] != '\r'
&& data[i] != '\'' && data[i] != '"' && data[i] != '(')
return 0;
@@ -2031,17 +2096,17 @@ is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
/* optional (space|tab)* spacer after a newline */
if (line_end) {
i = line_end + 1;
- while (i < end && (data[i] == ' ' || data[i] == '\t')) i += 1; }
+ while (i < end && (data[i] == ' ' || data[i] == '\t')) i++; }
/* optional title: any non-newline sequence enclosed in '"()
alone on its line */
title_offset = title_end = 0;
if (i + 1 < end
&& (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
- i += 1;
+ i++;
title_offset = i;
/* looking for EOL */
- while (i < end && data[i] != '\n' && data[i] != '\r') i += 1;
+ while (i < end && data[i] != '\n' && data[i] != '\r') i++;
if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
title_end = i + 1;
else title_end = i;
@@ -2102,7 +2167,7 @@ static void expand_tabs(struct buf *ob, const char *line, size_t size)
/* markdown • parses the input buffer and renders it into the output buffer */
void
-ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsigned int extensions) {
+sd_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsigned int extensions) {
struct link_ref *lr;
struct buf *text;
size_t i, beg, end;
@@ -2124,7 +2189,7 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
parr_init(&rndr.work_bufs[BUFFER_BLOCK]);
parr_init(&rndr.work_bufs[BUFFER_SPAN]);
- for (i = 0; i < 256; i += 1)
+ for (i = 0; i < 256; i++)
rndr.active_char[i] = 0;
if (rndr.make.emphasis || rndr.make.double_emphasis || rndr.make.triple_emphasis) {
@@ -2153,6 +2218,9 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
rndr.active_char['w'] = MD_CHAR_AUTOLINK_WWW;
}
+ if (extensions & MKDEXT_SUPERSCRIPT)
+ rndr.active_char['^'] = MD_CHAR_SUPERSCRIPT;
+
/* Extension data */
rndr.ext_flags = extensions;
rndr.max_nesting = 16;
@@ -2165,7 +2233,7 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
else { /* skipping to the next line */
end = beg;
while (end < ib->size && ib->data[end] != '\n' && ib->data[end] != '\r')
- end += 1;
+ end++;
/* adding the line body if present */
if (end > beg)
@@ -2175,7 +2243,7 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
/* add one \n per newline */
if (ib->data[end] == '\n' || (end + 1 < ib->size && ib->data[end + 1] != '\n'))
bufputc(text, '\n');
- end += 1;
+ end++;
}
beg = end;
@@ -2203,7 +2271,7 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
/* clean-up */
bufrelease(text);
lr = rndr.refs.base;
- for (i = 0; i < (size_t)rndr.refs.size; i += 1) {
+ for (i = 0; i < (size_t)rndr.refs.size; i++) {
bufrelease(lr[i].id);
bufrelease(lr[i].link);
bufrelease(lr[i].title);
@@ -2225,7 +2293,7 @@ ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer,
}
void
-ups_version(int *ver_major, int *ver_minor, int *ver_revision)
+sd_version(int *ver_major, int *ver_minor, int *ver_revision)
{
*ver_major = UPSKIRT_VER_MAJOR;
*ver_minor = UPSKIRT_VER_MINOR;
View
16 ext/redcarpet/markdown.h
@@ -46,6 +46,7 @@ enum mkd_extensions {
MKDEXT_STRIKETHROUGH = (1 << 4),
MKDEXT_LAX_HTML_BLOCKS = (1 << 5),
MKDEXT_SPACE_HEADERS = (1 << 6),
+ MKDEXT_SUPERSCRIPT = (1 << 7),
};
/* mkd_renderer • functions for rendering parsed data */
@@ -75,6 +76,7 @@ struct mkd_renderer {
int (*raw_html_tag)(struct buf *ob, struct buf *tag, void *opaque);
int (*triple_emphasis)(struct buf *ob, struct buf *text, void *opaque);
int (*strikethrough)(struct buf *ob, struct buf *text, void *opaque);
+ int (*superscript)(struct buf *ob, struct buf *text, void *opaque);
/* low level callbacks - NULL copies input directly into the output */
void (*entity)(struct buf *ob, struct buf *entity, void *opaque);
@@ -100,23 +102,17 @@ struct mkd_renderer {
#define MKD_TABLE_ALIGN_R (1 << 1)
#define MKD_TABLE_ALIGN_CENTER (MKD_TABLE_ALIGN_L | MKD_TABLE_ALIGN_R)
-/*******************
- * Auxiliar methods
- *******************/
-int
-is_safe_link(const char *link, size_t link_len);
-
/**********************
* EXPORTED FUNCTIONS *
**********************/
-/* ups_markdown * parses the input buffer and renders it into the output buffer */
+/* sd_markdown * parses the input buffer and renders it into the output buffer */
extern void
-ups_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr, unsigned int extensions);
+sd_markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr, unsigned int extensions);
-/* ups_version * returns the library version as major.minor.rev */
+/* sd_version * returns the library version as major.minor.rev */
extern void
-ups_version(int *major, int *minor, int *revision);
+sd_version(int *major, int *minor, int *revision);
#endif
View
108 ext/redcarpet/rc_markdown.c
@@ -0,0 +1,108 @@
+#define RSTRING_NOT_MODIFIED
+
+#include <stdio.h>
+#include "ruby.h"
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include <ruby/encoding.h>
+#else
+#define rb_enc_copy(dst, src)
+#endif
+
+#include "markdown.h"
+
+VALUE rb_mRedcarpet;
+VALUE rb_cMarkdown;
+
+extern VALUE rb_cRenderBase;
+
+static void rb_redcarpet_md_flags(VALUE ruby_obj, unsigned int *enabled_extensions_p)
+{
+ unsigned int extensions = 0;
+
+ /**
+ * Markdown extensions -- all disabled by default
+ */
+ if (rb_funcall(ruby_obj, rb_intern("no_intra_emphasis"), 0) == Qtrue)
+ extensions |= MKDEXT_NO_INTRA_EMPHASIS;
+
+ if (rb_funcall(ruby_obj, rb_intern("tables"), 0) == Qtrue)
+ extensions |= MKDEXT_TABLES;
+
+ if (rb_funcall(ruby_obj, rb_intern("fenced_code_blocks"), 0) == Qtrue)
+ extensions |= MKDEXT_FENCED_CODE;
+
+ if (rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
+ extensions |= MKDEXT_AUTOLINK;
+
+ if (rb_funcall(ruby_obj, rb_intern("strikethrough"), 0) == Qtrue)
+ extensions |= MKDEXT_STRIKETHROUGH;
+
+ if (rb_funcall(ruby_obj, rb_intern("lax_html_blocks"), 0) == Qtrue)
+ extensions |= MKDEXT_LAX_HTML_BLOCKS;
+
+ if (rb_funcall(ruby_obj, rb_intern("space_after_headers"), 0) == Qtrue)
+ extensions |= MKDEXT_SPACE_HEADERS;
+
+ if (rb_funcall(ruby_obj, rb_intern("superscript"), 0) == Qtrue)
+ extensions |= MKDEXT_SUPERSCRIPT;
+
+ *enabled_extensions_p = extensions;
+}
+
+static VALUE rb_redcarpet_md_render_with(VALUE self, VALUE rb_rndr, VALUE text)
+{
+ VALUE result;
+
+ struct mkd_renderer *renderer;
+ struct buf input_buf, *output_buf;
+ unsigned int enabled_extensions = 0;
+
+ Check_Type(text, T_STRING);
+
+ if (!rb_obj_is_kind_of(rb_rndr, rb_cRenderBase))
+ rb_raise(rb_eTypeError, "Invalid Renderer instance");
+
+ if (rb_respond_to(rb_rndr, rb_intern("preprocess")))
+ text = rb_funcall(rb_rndr, rb_intern("preprocess"), 1, text);
+
+ memset(&input_buf, 0x0, sizeof(struct buf));
+ input_buf.data = RSTRING_PTR(text);
+ input_buf.size = RSTRING_LEN(text);
+
+ output_buf = bufnew(128);
+ bufgrow(output_buf, RSTRING_LEN(text) * 1.4f);
+
+ Data_Get_Struct(rb_rndr, struct mkd_renderer, renderer);
+
+ rb_redcarpet_md_flags(self, &enabled_extensions);
+ sd_markdown(output_buf, &input_buf, renderer, enabled_extensions);
+ result = rb_str_new(output_buf->data, output_buf->size);
+ rb_enc_copy(result, text);
+
+ bufrelease(output_buf);
+
+ if (rb_respond_to(rb_rndr, rb_intern("postprocess")))
+ result = rb_funcall(rb_rndr, rb_intern("postprocess"), 1, result);
+
+ return result;
+}
+
+static VALUE rb_redcarpet_md_render(VALUE self, VALUE text)
+{
+ return rb_redcarpet_md_render_with(self, rb_iv_get(self, "@renderer"), text);
+}
+
+extern void Init_redcarpet_rndr();
+
+void Init_redcarpet()
+{
+ rb_mRedcarpet = rb_define_module("Redcarpet");
+
+ rb_cMarkdown = rb_define_class_under(rb_mRedcarpet, "Markdown", rb_cObject);
+ rb_define_method(rb_cMarkdown, "render", rb_redcarpet_md_render, 1);
+ rb_define_method(rb_cMarkdown, "render_with", rb_redcarpet_md_render_with, 2);
+
+ Init_redcarpet_rndr();
+}
+
View
469 ext/redcarpet/rc_render.c
@@ -0,0 +1,469 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define RSTRING_NOT_MODIFIED
+
+#include <ruby.h>
+#include "markdown.h"
+#include "html.h"
+
+#ifdef HAVE_RUBY_ENCODING_H
+#include <ruby/encoding.h>
+#else
+#define rb_enc_copy(dst, src)
+#endif
+
+#define CSTR2SYM(s) (ID2SYM(rb_intern((s))))
+
+#define SPAN_CALLBACK(method_name, ...) {\
+ struct redcarpet_pload *load = opaque;\
+ VALUE ret = rb_funcall(load->self, rb_intern(method_name), __VA_ARGS__);\
+ if (NIL_P(ret)) return 0;\
+ Check_Type(ret, T_STRING);\
+ bufput(ob, RSTRING_PTR(ret), RSTRING_LEN(ret));\
+ return 1;\
+}
+
+#define BLOCK_CALLBACK(method_name, ...) {\
+ struct redcarpet_pload *load = opaque;\
+ VALUE ret = rb_funcall(load->self, rb_intern(method_name), __VA_ARGS__);\
+ if (NIL_P(ret)) return;\
+ Check_Type(ret, T_STRING);\
+ bufput(ob, RSTRING_PTR(ret), RSTRING_LEN(ret));\
+}
+
+extern VALUE rb_mRedcarpet;
+VALUE rb_mRender;
+VALUE rb_cRenderBase;
+VALUE rb_cRenderHTML;
+VALUE rb_cRenderHTML_TOC;
+VALUE rb_mSmartyPants;
+
+struct redcarpet_pload {
+ VALUE self;
+};
+
+static inline VALUE
+buf2str(struct buf *text)
+{
+ if (!text || !text->size) return Qnil;
+ return rb_str_new(text->data, text->size);
+}
+
+
+static void
+rndr_blockcode(struct buf *ob, struct buf *text, struct buf *lang, void *opaque)
+{
+ BLOCK_CALLBACK("block_code", 2, buf2str(text), buf2str(lang));
+}
+
+static void
+rndr_blockquote(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("block_quote", 1, buf2str(text));
+}
+
+static void
+rndr_raw_block(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("block_html", 1, buf2str(text));
+}
+
+static void
+rndr_header(struct buf *ob, struct buf *text, int level, void *opaque)
+{
+ BLOCK_CALLBACK("header", 2, buf2str(text), INT2FIX(level));
+}
+
+static void
+rndr_hrule(struct buf *ob, void *opaque)
+{
+ BLOCK_CALLBACK("hrule", 0);
+}
+
+static void
+rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque)
+{
+ BLOCK_CALLBACK("list", 2, buf2str(text),
+ (flags & MKD_LIST_ORDERED) ? CSTR2SYM("ordered") : CSTR2SYM("unordered"));
+}
+
+static void
+rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque)
+{
+ BLOCK_CALLBACK("list_item", 1, buf2str(text));
+}
+
+static void
+rndr_paragraph(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("paragraph", 1, buf2str(text));
+}
+
+static void
+rndr_table(struct buf *ob, struct buf *header, struct buf *body, void *opaque)
+{
+ BLOCK_CALLBACK("table", 2, buf2str(header), buf2str(body));
+}
+
+static void
+rndr_tablerow(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("table_row", 1, buf2str(text));
+}
+
+static void
+rndr_tablecell(struct buf *ob, struct buf *text, int align, void *opaque)
+{
+ VALUE rb_align;
+
+ switch (align) {
+ case MKD_TABLE_ALIGN_L:
+ rb_align = CSTR2SYM("left");
+ break;
+
+ case MKD_TABLE_ALIGN_R:
+ rb_align = CSTR2SYM("right");
+ break;
+
+ case MKD_TABLE_ALIGN_CENTER:
+ rb_align = CSTR2SYM("center");
+ break;
+
+ default:
+ rb_align = Qnil;
+ break;
+ }
+
+ BLOCK_CALLBACK("table_cell", 2, buf2str(text), rb_align);
+}
+
+
+
+
+/***
+ * SPAN LEVEL
+ */
+static int
+rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque)
+{
+ SPAN_CALLBACK("autolink", 2, buf2str(link),
+ type == MKDA_NORMAL ? CSTR2SYM("url") : CSTR2SYM("email"));
+}
+
+static int
+rndr_codespan(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("codespan", 1, buf2str(text));
+}
+
+static int
+rndr_double_emphasis(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("double_emphasis", 1, buf2str(text));
+}
+
+static int
+rndr_emphasis(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("emphasis", 1, buf2str(text));
+}
+
+static int
+rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, void *opaque)
+{
+ SPAN_CALLBACK("image", 3, buf2str(link), buf2str(title), buf2str(alt));
+}
+
+static int
+rndr_linebreak(struct buf *ob, void *opaque)
+{
+ SPAN_CALLBACK("linebreak", 0);
+}
+
+static int
+rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque)
+{
+ SPAN_CALLBACK("link", 3, buf2str(link), buf2str(title), buf2str(content));
+}
+
+static int
+rndr_raw_html(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("raw_html", 1, buf2str(text));
+}
+
+static int
+rndr_triple_emphasis(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("triple_emphasis", 1, buf2str(text));
+}
+
+static int
+rndr_strikethrough(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("strikethrough", 1, buf2str(text));
+}
+
+static int
+rndr_superscript(struct buf *ob, struct buf *text, void *opaque)
+{
+ SPAN_CALLBACK("superscript", 1, buf2str(text));
+}
+
+/**
+ * direct writes
+ */
+static void
+rndr_entity(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("entity", 1, buf2str(text));
+}
+
+static void
+rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
+{
+ BLOCK_CALLBACK("normal_text", 1, buf2str(text));
+}
+
+static void
+rndr_doc_header(struct buf *ob, void *opaque)
+{
+ BLOCK_CALLBACK("doc_header", 0);
+}
+
+static void
+rndr_doc_footer(struct buf *ob, void *opaque)
+{
+ BLOCK_CALLBACK("doc_footer", 0);
+}
+
+static struct mkd_renderer rb_redcarpet_rndr = {
+ rndr_blockcode,
+ rndr_blockquote,
+ rndr_raw_block,
+ rndr_header,
+ rndr_hrule,
+ rndr_list,
+ rndr_listitem,
+ rndr_paragraph,
+ rndr_table,
+ rndr_tablerow,
+ rndr_tablecell,
+
+ rndr_autolink,
+ rndr_codespan,
+ rndr_double_emphasis,
+ rndr_emphasis,
+ rndr_image,
+ rndr_linebreak,
+ rndr_link,
+ rndr_raw_html,
+ rndr_triple_emphasis,
+ rndr_strikethrough,
+ rndr_superscript,
+
+ rndr_entity,
+ rndr_normal_text,
+
+ rndr_doc_header,
+ rndr_doc_footer,
+
+ NULL
+};
+
+static const char *rb_redcarpet_method_names[] = {
+ "block_code",
+ "block_quote",
+ "block_html",
+ "header",
+ "hrule",
+ "list",
+ "list_item",
+ "paragraph",
+ "table",
+ "table_row",
+ "table_cell",
+
+ "autolink",
+ "codespan",
+ "double_emphasis",
+ "emphasis",
+ "image",
+ "linebreak",
+ "link",
+ "raw_html",
+ "triple_emphasis",
+ "strikethrough",
+ "superscript",
+
+ "entity",
+ "normal_text",
+
+ "doc_header",
+ "doc_footer"
+};
+
+static const size_t rb_redcarpet_method_count = sizeof(rb_redcarpet_method_names)/sizeof(char *);
+
+static void rb_redcarpet_rbase_free(struct mkd_renderer *rndr)
+{
+ xfree(rndr->opaque);
+ xfree(rndr);
+}
+
+static VALUE rb_redcarpet_rbase_alloc(VALUE klass)
+{
+ struct mkd_renderer *rndr = ALLOC(struct mkd_renderer);
+ memset(rndr, 0x0, sizeof(struct mkd_renderer));
+ return Data_Wrap_Struct(klass, NULL, rb_redcarpet_rbase_free, rndr);
+}
+
+static void rb_redcarpet__overload(VALUE self)
+{
+ size_t i;
+ struct mkd_renderer *rndr;
+
+ void **source = (void **)&rb_redcarpet_rndr;
+ void **dest;
+
+ Data_Get_Struct(self, struct mkd_renderer, rndr);
+ dest = (void **)rndr;
+
+ for (i = 0; i < rb_redcarpet_method_count; ++i) {
+ if (rb_respond_to(self, rb_intern(rb_redcarpet_method_names[i])))
+ dest[i] = source[i];
+ }
+}
+
+static VALUE rb_redcarpet_rbase_init(VALUE self)
+{
+ struct mkd_renderer *rndr;
+ struct redcarpet_pload *payload;
+
+ Data_Get_Struct(self, struct mkd_renderer, rndr);
+ rb_redcarpet__overload(self);
+
+ payload = ALLOC(struct redcarpet_pload);
+ payload->self = self;
+
+ rndr->opaque = payload;
+ return Qnil;
+}
+
+static VALUE rb_redcarpet_html_init(int argc, VALUE *argv, VALUE self)
+{
+ struct mkd_renderer *rndr;
+ unsigned int render_flags = 0;
+ VALUE hash;
+
+ Data_Get_Struct(self, struct mkd_renderer, rndr);
+
+ if (rb_scan_args(argc, argv, "01", &hash) == 1)
+ {
+ Check_Type(hash, T_HASH);
+
+ /* filter_html */
+ if (rb_hash_aref(hash, CSTR2SYM("filter_html")) == Qtrue)
+ render_flags |= HTML_SKIP_HTML;
+
+ /* no_image */
+ if (rb_hash_aref(hash, CSTR2SYM("no_image")) == Qtrue)
+ render_flags |= HTML_SKIP_IMAGES;
+
+ /* no_links */
+ if (rb_hash_aref(hash, CSTR2SYM("no_links")) == Qtrue)
+ render_flags |= HTML_SKIP_LINKS;
+
+ /* filter_style */
+ if (rb_hash_aref(hash, CSTR2SYM("no_styles")) == Qtrue)
+ render_flags |= HTML_SKIP_STYLE;
+
+ /* safelink */
+ if (rb_hash_aref(hash, CSTR2SYM("safe_links_only")) == Qtrue)
+ render_flags |= HTML_SAFELINK;
+
+ if (rb_hash_aref(hash, CSTR2SYM("with_toc_data")) == Qtrue)
+ render_flags |= HTML_TOC;
+
+ if (rb_hash_aref(hash, CSTR2SYM("hard_wrap")) == Qtrue)
+ render_flags |= HTML_HARD_WRAP;
+
+ if (rb_hash_aref(hash, CSTR2SYM("xhtml")) == Qtrue)
+ render_flags |= HTML_USE_XHTML;
+ }
+
+ sdhtml_renderer(rndr, render_flags, (void *)self);
+
+ if (rb_obj_class(self) != rb_cRenderHTML)
+ rb_redcarpet__overload(self);
+
+ return Qnil;
+}
+
+static VALUE rb_redcarpet_htmltoc_init(VALUE self)
+{
+ struct mkd_renderer *rndr;
+ Data_Get_Struct(self, struct mkd_renderer, rndr);
+
+ sdhtml_toc_renderer(rndr, (void *)self);
+
+ if (rb_obj_class(self) != rb_cRenderHTML_TOC)
+ rb_redcarpet__overload(self);
+
+ return Qnil;
+}
+
+static VALUE rb_redcarpet_smartypants_render(VALUE self, VALUE text)
+{
+ VALUE result;
+ struct buf input_buf, *output_buf;
+
+ Check_Type(text, T_STRING);
+
+ memset(&input_buf, 0x0, sizeof(struct buf));
+ input_buf.data = RSTRING_PTR(text);
+ input_buf.size = RSTRING_LEN(text);
+
+ output_buf = bufnew(128);
+ bufgrow(output_buf, RSTRING_LEN(text) * 1.1f);
+
+ sdhtml_smartypants(output_buf, &input_buf);
+ result = rb_str_new(output_buf->data, output_buf->size);
+ rb_enc_copy(result, text);
+
+ bufrelease(output_buf);
+ return result;
+}
+
+
+void Init_redcarpet_rndr()
+{
+ rb_mRender = rb_define_module_under(rb_mRedcarpet, "Render");
+
+ rb_cRenderBase = rb_define_class_under(rb_mRender, "Base", rb_cObject);
+ rb_define_alloc_func(rb_cRenderBase, rb_redcarpet_rbase_alloc);
+ rb_define_method(rb_cRenderBase, "initialize", rb_redcarpet_rbase_init, 0);
+
+ rb_cRenderHTML = rb_define_class_under(rb_mRender, "HTML", rb_cRenderBase);
+ rb_define_method(rb_cRenderHTML, "initialize", rb_redcarpet_html_init, -1);
+
+ rb_cRenderHTML_TOC = rb_define_class_under(rb_mRender, "HTML_TOC", rb_cRenderBase);
+ rb_define_method(rb_cRenderHTML_TOC, "initialize", rb_redcarpet_htmltoc_init, 0);
+
+ rb_mSmartyPants = rb_define_module_under(rb_mRender, "SmartyPants");
+ rb_define_singleton_method(rb_mSmartyPants, "postprocess", rb_redcarpet_smartypants_render, 1);
+}
View
161 ext/redcarpet/redcarpet.c
@@ -1,161 +0,0 @@
-#define RSTRING_NOT_MODIFIED
-
-#include <stdio.h>
-#include "ruby.h"
-
-#ifdef HAVE_RUBY_ENCODING_H
-#include <ruby/encoding.h>
-#else
-#define rb_enc_copy(dst, src)
-#endif
-
-#include "markdown.h"
-#include "html.h"
-
-typedef enum
-{
- REDCARPET_RENDER_HTML,
- REDCARPET_RENDER_TOC
-} RendererType;
-
-static VALUE rb_cRedcarpet;
-
-static void rb_redcarpet__get_flags(VALUE ruby_obj,
- unsigned int *enabled_extensions_p,
- unsigned int *render_flags_p)
-{
- unsigned int render_flags = HTML_EXPAND_TABS;
- unsigned int extensions = 0;