Add remaining render callbacks.

This commit is contained in:
Frank Smit 2015-06-03 00:04:24 +02:00
parent 6117424b29
commit 3304d94255
3 changed files with 236 additions and 88 deletions

12
THANKS
View File

@ -1,8 +1,8 @@
People who contributed code and fixed bugs:
- tanoku
- heintz
- honza
- francescomari
- andrew-d
- tkf
- vmg
- heintz
- honza
- francescomari
- andrew-d
- tkf

View File

@ -11,9 +11,14 @@ __all__ = [
]
IUNIT = 1024
OUNIT = 64
MAX_NESTING = 16
def html(text, extensions=0, render_flags=0):
ib = lib.hoedown_buffer_new(1024)
ob = lib.hoedown_buffer_new(64)
ib = lib.hoedown_buffer_new(IUNIT)
ob = lib.hoedown_buffer_new(OUNIT)
renderer = lib.hoedown_html_renderer_new(0, 0)
document = lib.hoedown_document_new(renderer, 0, 16);
@ -30,16 +35,17 @@ def html(text, extensions=0, render_flags=0):
class Markdown:
def __init__(self, renderer):
def __init__(self, renderer, extensions=0):
# NOTE: Prevent the renderer from being garbage collected
self.renderer = renderer
self.extensions = extensions
def render(self, text):
ib = lib.hoedown_buffer_new(1024)
ib = lib.hoedown_buffer_new(IUNIT)
lib.hoedown_buffer_puts(ib, text.encode('utf-8'))
ob = lib.hoedown_buffer_new(64)
document = lib.hoedown_document_new(self.renderer.renderer, 0, 16);
ob = lib.hoedown_buffer_new(OUNIT)
document = lib.hoedown_document_new(self.renderer.renderer, self.extensions, MAX_NESTING);
lib.hoedown_document_render(document, ob, ib.data, ib.size);
lib.hoedown_buffer_free(ib);
@ -51,7 +57,7 @@ class Markdown:
lib.hoedown_buffer_free(ob);
callback_signatures = {
_callback_signatures = {
# block level callbacks - NULL skips the block
'blockcode': 'void (*blockcode)(hoedown_buffer *ob, const hoedown_buffer *text, const hoedown_buffer *lang, const hoedown_renderer_data *data)',
'blockquote': 'void (*blockquote)(hoedown_buffer *ob, const hoedown_buffer *content, const hoedown_renderer_data *data)',
@ -97,33 +103,19 @@ callback_signatures = {
}
# TODO: Do this in Python:
# static hoedown_renderer *
# null_renderer_new()
# {
# hoedown_renderer *renderer;
# renderer = hoedown_malloc(sizeof(hoedown_renderer));
# memset(renderer, 0x0, sizeof(hoedown_renderer));
# return renderer;
# }
# static void
# null_renderer_free(hoedown_renderer *renderer)
# {
# free(renderer);
# }
class BaseRenderer:
def __init__(self):
# TODO: Make a null renderer.
self.renderer = None
self.renderer = lib.null_renderer_new()
self.set_callbacks()
def __del__(self):
lib.null_renderer_free(self.renderer)
def set_callbacks(self):
callbacks = []
for name, func in getmembers(self, predicate=ismethod):
signature = callback_signatures.get(name)
signature = _callback_signatures.get(name)
if signature is None:
continue
@ -232,6 +224,31 @@ class BaseRenderer:
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
def _w_autolink(self, ob, link, type, data):
link = ffi.string(link.data, link.size).decode('utf-8')
type = int(type)
result = self.autolink(link, type)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_codespan(self, ob, text, data):
text = ffi.string(text.data, text.size).decode('utf-8')
result = self.codespan(text)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_double_emphasis(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.double_emphasis(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_emphasis(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.emphasis(content)
@ -240,10 +257,142 @@ class BaseRenderer:
return 1
return 0
def _w_underline(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.underline(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_highlight(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.highlight(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_quote(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.quote(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_image(self, ob, link, title, alt, data):
link = ffi.string(link.data, link.size).decode('utf-8')
title = ffi.string(title.data, title.size).decode('utf-8')
alt = ffi.string(alt.data, alt.size).decode('utf-8')
result = self.image(link, title, alt)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_quote(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.quote(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_linebreak(self, ob, data):
result = self.linebreak()
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_link(self, ob, content, link, title, data):
content = ffi.string(content.data, content.size).decode('utf-8')
link = ffi.string(link.data, link.size).decode('utf-8')
title = ffi.string(title.data, title.size).decode('utf-8')
result = self.link(content, link, title)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_triple_emphasis(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.triple_emphasis(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_strikethrough(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.strikethrough(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_superscript(self, ob, content, data):
content = ffi.string(content.data, content.size).decode('utf-8')
result = self.superscript(content)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_footnote_ref(self, ob, num, data):
num = int(num)
result = self.footnote_ref(num)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_math(self, ob, text, displaymode, data):
text = ffi.string(text.data, text.size).decode('utf-8')
displaymode = int(displaymode)
result = self.math(text, displaymode)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_raw_html(self, ob, text, data):
text = ffi.string(text.data, text.size).decode('utf-8')
result = self.raw_html(text)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
return 1
return 0
def _w_entity(self, ob, text, data):
text = ffi.string(text.data, text.size).decode('utf-8')
result = self.entity(text)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
def _w_normal_text(self, ob, text, data):
text = ffi.string(text.data, text.size).decode('utf-8')
result = self.normal_text(text)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
def _w_doc_header(self, ob, inline_render, data):
inline_render = int(inline_render)
result = self.doc_header(inline_render)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
def _w_doc_footer(self, ob, inline_render, data):
inline_render = int(inline_render)
result = self.doc_footer(inline_render)
if result:
lib.hoedown_buffer_puts(ob, result.encode('utf-8'))
class HtmlRenderer(BaseRenderer):
def __init__(self):
self.renderer = lib.hoedown_html_renderer_new(0, 0)
def __init__(self, flags=0):
self.renderer = lib.hoedown_html_renderer_new(flags, 0)
self.set_callbacks()
def __del__(self):

View File

@ -10,14 +10,13 @@ from subprocess import Popen, PIPE, STDOUT
import misaka
from misaka import Markdown, BaseRenderer, HtmlRenderer, SmartyPants, \
from misaka import Markdown, BaseRenderer, HtmlRenderer, \
EXT_NO_INTRA_EMPHASIS, EXT_TABLES, EXT_FENCED_CODE, EXT_AUTOLINK, \
EXT_STRIKETHROUGH, EXT_LAX_SPACING, EXT_SPACE_HEADERS, \
EXT_STRIKETHROUGH, EXT_SPACE_HEADERS, \
EXT_SUPERSCRIPT, \
HTML_SKIP_HTML, HTML_SKIP_STYLE, HTML_SKIP_IMAGES, HTML_SKIP_LINKS, \
HTML_EXPAND_TABS, HTML_SAFELINK, HTML_TOC, HTML_HARD_WRAP, \
HTML_USE_XHTML, HTML_ESCAPE, \
HTML_SMARTYPANTS
HTML_SKIP_HTML, \
HTML_HARD_WRAP, \
HTML_USE_XHTML, HTML_ESCAPE
from minitest import TestCase, ok, runner
@ -31,48 +30,48 @@ def clean_html(dirty_html):
return stdout.decode('utf-8')
class SmartyPantsTest(TestCase):
name = 'SmartyPants'
# class SmartyPantsTest(TestCase):
# name = 'SmartyPants'
def setup(self):
self.r = lambda html: misaka.html(html, render_flags=HTML_SMARTYPANTS)
# def setup(self):
# self.r = lambda html: misaka.html(html, render_flags=HTML_SMARTYPANTS)
def test_single_quotes_re(self):
html = self.r('<p>They\'re not for sale.</p>\n')
ok(html).diff('<p>They&rsquo;re not for sale.</p>\n')
# def test_single_quotes_re(self):
# html = self.r('<p>They\'re not for sale.</p>\n')
# ok(html).diff('<p>They&rsquo;re not for sale.</p>\n')
def test_single_quotes_ll(self):
html = self.r('<p>Well that\'ll be the day</p>\n')
ok(html).diff('<p>Well that&rsquo;ll be the day</p>\n')
# def test_single_quotes_ll(self):
# html = self.r('<p>Well that\'ll be the day</p>\n')
# ok(html).diff('<p>Well that&rsquo;ll be the day</p>\n')
def test_double_quotes_to_curly_quotes(self):
html = self.r('<p>"Quoted text"</p>\n')
ok(html).diff('<p>&ldquo;Quoted text&rdquo;</p>\n')
# def test_double_quotes_to_curly_quotes(self):
# html = self.r('<p>"Quoted text"</p>\n')
# ok(html).diff('<p>&ldquo;Quoted text&rdquo;</p>\n')
def test_single_quotes_ve(self):
html = self.r('<p>I\'ve been meaning to tell you ..</p>\n')
ok(html).diff('<p>I&rsquo;ve been meaning to tell you ..</p>\n')
# def test_single_quotes_ve(self):
# html = self.r('<p>I\'ve been meaning to tell you ..</p>\n')
# ok(html).diff('<p>I&rsquo;ve been meaning to tell you ..</p>\n')
def test_single_quotes_m(self):
html = self.r('<p>I\'m not kidding</p>\n')
ok(html).diff('<p>I&rsquo;m not kidding</p>\n')
# def test_single_quotes_m(self):
# html = self.r('<p>I\'m not kidding</p>\n')
# ok(html).diff('<p>I&rsquo;m not kidding</p>\n')
def test_single_quotes_d(self):
html = self.r('<p>what\'d you say?</p>\n')
ok(html).diff('<p>what&rsquo;d you say?</p>\n')
# def test_single_quotes_d(self):
# html = self.r('<p>what\'d you say?</p>\n')
# ok(html).diff('<p>what&rsquo;d you say?</p>\n')
class HtmlRenderTest(TestCase):
name = 'Html Renderer'
def setup(self):
pants = SmartyPants()
# pants = SmartyPants()
self.r = {
HTML_SKIP_HTML: HtmlRenderer(HTML_SKIP_HTML),
HTML_SKIP_IMAGES: HtmlRenderer(HTML_SKIP_IMAGES),
HTML_SKIP_LINKS: HtmlRenderer(HTML_SKIP_LINKS),
HTML_SAFELINK: HtmlRenderer(HTML_SAFELINK),
# HTML_SKIP_IMAGES: HtmlRenderer(HTML_SKIP_IMAGES),
# HTML_SKIP_LINKS: HtmlRenderer(HTML_SKIP_LINKS),
# HTML_SAFELINK: HtmlRenderer(HTML_SAFELINK),
HTML_ESCAPE: HtmlRenderer(HTML_ESCAPE),
HTML_HARD_WRAP: HtmlRenderer(HTML_HARD_WRAP)
}
@ -109,17 +108,17 @@ Through <em>NO</em> <script>DOUBLE NO</script>
markdown = self.render_with(HTML_SKIP_HTML, 'Lorem, \nipsum\n')
ok(markdown).diff('<p>Lorem,<br>\nipsum</p>\n')
def test_skip_image(self):
markdown = self.render_with(HTML_SKIP_IMAGES, '![dust mite](http://dust.mite/image.png) <img src="image.png" />')
ok(markdown).not_contains('<img')
# def test_skip_image(self):
# markdown = self.render_with(HTML_SKIP_IMAGES, '![dust mite](http://dust.mite/image.png) <img src="image.png" />')
# ok(markdown).not_contains('<img')
def test_skip_links(self):
markdown = self.render_with(HTML_SKIP_LINKS, '[This link](http://example.net/) <a href="links.html">links</a>')
ok(markdown).not_contains('<a ')
# def test_skip_links(self):
# markdown = self.render_with(HTML_SKIP_LINKS, '[This link](http://example.net/) <a href="links.html">links</a>')
# ok(markdown).not_contains('<a ')
def test_safelink(self):
markdown = self.render_with(HTML_SAFELINK, '[IRC](irc://chat.freenode.org/#freenode)')
ok(markdown).diff('<p>[IRC](irc://chat.freenode.org/#freenode)</p>\n')
# def test_safelink(self):
# markdown = self.render_with(HTML_SAFELINK, '[IRC](irc://chat.freenode.org/#freenode)')
# ok(markdown).diff('<p>[IRC](irc://chat.freenode.org/#freenode)</p>\n')
def test_hard_wrap(self):
markdown = self.render_with(HTML_HARD_WRAP, '''
@ -177,14 +176,14 @@ class MarkdownParserTest(TestCase):
'<p>A wise man once said:</p>\n\n' \
'<blockquote>\n<p>Isn&#39;t it wonderful just to be alive.</p>\n</blockquote>\n')
def test_html_block_not_wrapped_in_p(self):
markdown = self.render_with(
'Things to watch out for\n\n' \
'<ul>\n<li>Blah</li>\n</ul>\n',
extensions=EXT_LAX_SPACING)
ok(markdown).diff(
'<p>Things to watch out for</p>\n\n' \
'<ul>\n<li>Blah</li>\n</ul>\n')
# def test_html_block_not_wrapped_in_p(self):
# markdown = self.render_with(
# 'Things to watch out for\n\n' \
# '<ul>\n<li>Blah</li>\n</ul>\n',
# extensions=EXT_LAX_SPACING)
# ok(markdown).diff(
# '<p>Things to watch out for</p>\n\n' \
# '<ul>\n<li>Blah</li>\n</ul>\n')
# http://github.com/rtomayko/rdiscount/issues/#issue/13
def test_headings_with_trailing_space(self):
@ -252,11 +251,11 @@ This is some awesome code
ok(self.render_with(text)).not_contains('<code')
ok(self.render_with(text, extensions=EXT_FENCED_CODE)).contains('<code')
def test_fenced_code_blocks_without_space(self):
text = 'foo\nbar\n```\nsome\ncode\n```\nbaz'
# def test_fenced_code_blocks_without_space(self):
# text = 'foo\nbar\n```\nsome\ncode\n```\nbaz'
ok(self.render_with(text)).not_contains('<pre><code>')
ok(self.render_with(text, extensions=EXT_FENCED_CODE | EXT_LAX_SPACING)).contains('<pre><code>')
# ok(self.render_with(text)).not_contains('<pre><code>')
# ok(self.render_with(text, extensions=EXT_FENCED_CODE | EXT_LAX_SPACING)).contains('<pre><code>')
def test_linkable_headers(self):
markdown = self.r('### Hello [GitHub](http://github.com)')
@ -333,7 +332,7 @@ class UnicodeTest(TestCase):
def run_tests():
runner([
SmartyPantsTest,
# SmartyPantsTest,
HtmlRenderTest,
MarkdownParserTest,
MarkdownConformanceTest_10,