Permalink
Browse files

* added partial implemention of the creole syntax. still missing subl…

…ists and tables.

* added CLI app for easy testing.
* dropped pygments dependency
* added BeautifulSoup prettification (if BeautifulSoup is installed)
  • Loading branch information...
ojii
ojii committed Oct 28, 2009
1 parent e4f70eb commit f5700fa0f5c7042f37961b33c760a80064058a86
Showing with 347 additions and 8 deletions.
  1. +13 −2 __init__.py
  2. +219 −0 bbtags/creole.py
  3. +1 −1 bbtags/table.py
  4. +8 −5 bbtags/text_formatting.py
  5. +106 −0 cli.py
View
@@ -30,6 +30,12 @@
from django.utils.translation import ugettext as _
except ImportError:
_ = lambda x: x
+
+try:
+ from BeautifulSoup import BeautifulSoup
+ prettify = lambda x: BeautifulSoup(x).prettify()
+except ImportError:
+ prettify = lambda x: x
AUTODISCOVERED = False
@@ -149,6 +155,10 @@ def __init__(self, parent, match, fullcontent):
self.parent = parent
self.match = match
self.nodes = []
+
+ def soft_raise(self, errmsg):
+ soft_raise(errmsg)
+ return self.raw_content
def append(self, text):
"""
@@ -182,6 +192,7 @@ def close(self, end):
"""
When closing the node just return the parent.
"""
+ self.end = end
self.raw_content = self.fullcontent[self.start:end]
return self.parent
@@ -595,7 +606,7 @@ def recurse(nodes, level, indent):
return l
try:
head = self.get_parse_tree(content, namespaces)
- except ParseError:
+ except ParserError:
return '-Parse Error'
visuals = ['-HeadNode']
visuals += recurse(head.nodes, 1, indent)
@@ -653,7 +664,7 @@ def parse(content, namespaces=['__all__'], strict=True, auto_discover=False):
content = head.parse()
# Replace linefeeds
content = convert_linefeeds(content)
- return content, sem.pull()
+ return prettify(content), sem.pull()
def autodiscover():
"""
View
@@ -0,0 +1,219 @@
+"""
+(Partial) Creole 1.0 implementation.
+"""
+from bbcode import *
+import re
+
+class CreoleSCTN(SelfClosingTagNode):
+ inner_name = 'content'
+ @property
+ def index(self):
+ if not hasattr(self, '_index'):
+ self._index = self.parent.nodes.index(self)
+ return self._index
+
+ def get_prev(self):
+ try:
+ return self.parent.nodes[self.index - 1]
+ except IndexError:
+ return None
+ def get_next(self):
+ try:
+ return self.parent.nodes[self.index + 1]
+ except IndexError:
+ return None
+
+ def kill_next(self):
+ del self.parent.nodes[self.index + 1]
+
+ def parse_inner(self):
+ content = self.match.groupdict()[self.inner_name]
+ next = self.get_next()
+ while next and next.raw_content in content:
+ content = content.replace(next.raw_content, next.parse())
+ self.kill_next()
+ return content
+
+
+class Italics(CreoleSCTN):
+ """
+ //text//
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'//(?P<content>([^/][^/]?)+)//')
+
+ def parse(self):
+ return '<i>%s</i>' % self.parse_inner()
+
+
+class Bold(CreoleSCTN):
+ """
+ **text**
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'\*\*(?P<content>([^*][^*]?)+)\*\*')
+
+ def parse(self):
+ return '<strong>%s</strong>' % self.parse_inner()
+
+
+class CreoleList(CreoleSCTN):
+
+ def parse(self):
+ prev = self.get_prev()
+ if not prev or not isinstance(prev, self.__class__):
+ output = '<%s>' % self.base_tag_name
+ else:
+ output = ''
+ content = self.match.groupdict()['content']
+ next = self.get_next()
+ if next and isinstance(next, self.__class__):
+ # If the next item is of same kind, just add a list item here.
+ output += '<li>%s</li>' % content
+ else:
+ # check if 'next' is actually in 'content'
+ content = self.parse_inner()
+ # close the list
+ output += '<li>%s</li></%s>' % (content, self.base_tag_name)
+ return output
+
+
+class BulletList(CreoleList):
+ """
+ * item
+ * item
+ """
+ not_in_all = True
+ base_tag_name = 'ul'
+ open_pattern = re.compile(r'^[ \t]*\*(?P<content>.+)(\n|\Z)', re.MULTILINE)
+
+
+class NumberedList(CreoleList):
+ """
+ # item
+ # item
+ """
+ not_in_all = True
+ base_tag_name = 'ol'
+ open_pattern = re.compile(r'^[ \t]*#(?P<content>.+)$', re.MULTILINE)
+
+class Link(SelfClosingTagNode):
+ """
+ [[URL|linkanme]]
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'\[\[(?P<url>[^|\]]+)\|?(?P<name>[^\]]+)?\]\]')
+
+ def parse(self):
+ gd = self.match.groupdict()
+ url = gd['url']
+ name = gd.get('name',None) or url
+ return '<a href="%s">%s</a>' % (url, name)
+
+
+class Heading(SelfClosingTagNode):
+ """
+ = text [=]
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'^(?P<level>={1,6})(?P<text>([^=]|=[^\Z\n=]|=?=?=?=?=?=[^\Z\n=]|\n)+)(?P<optlevel>={0,6})$', re.MULTILINE)
+
+ def parse(self):
+ gd = self.match.groupdict()
+ level = len(gd['level'])
+ optlevel = len(gd.get('optlevel','') or '')
+ if optlevel and optlevel != level:
+ return self.soft_raise("Uneven levels: %s (start) and %s (end)" % (level, optlevel))
+ text = gd['text']
+ return '<h%s>%s</h%s>' % (level, text, level)
+
+
+class HorizontalLine(SelfClosingTagNode):
+ """
+ ----
+ """
+ not_in_all = True
+ open_pattern = re.compile('^[ \t]*----[ \t]*$', re.MULTILINE)
+
+ def parse(self):
+ return '<hr />'
+
+
+class Image(SelfClosingTagNode):
+ """
+ {{URL|title}}
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'\{\{(?P<url>[^|}]+)\|?(?P<title>[^}]+)?\}\}')
+
+ def parse(self):
+ gd = self.match.groupdict()
+ url = gd['url']
+ title = gd.get('title',None) or url
+ return '<img src="%s" title="%s" />' % (url, title)
+
+
+class Code(TagNode):
+ """
+ {{{
+ #!language
+ text
+ }}}
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'^\{\{\{[ \t]*\n(#!(?P<language>\w+))?', re.MULTILINE)
+ close_pattern = re.compile(r'^\}\}\}$', re.MULTILINE)
+
+ def parse(self):
+ lang = self.match.groupdict().get('language', '')
+ inner = ''
+ for node in self.nodes:
+ inner += node.raw_content
+ try:
+ from pygments import highlight
+ from pygments.lexers import guess_lexer, get_lexer_by_name, TextLexer
+ from pygments.formatters import HtmlFormatter
+ from pygments.util import ClassNotFound
+ except ImportError:
+ return '<pre>%s</pre>' % inner
+ if lang:
+ try:
+ lexer = get_lexer_by_name(lang)
+ except ClassNotFound:
+ try:
+ lexer = guess_lexer(inner)
+ except ClassNotFound:
+ lexer = TextLexer()
+ else:
+ try:
+ lexer = guess_lexer(inner)
+ except ClassNotFound:
+ lexer = TextLexer()
+ formatter = HtmlFormatter(cssclass='code', noclasses=True, linenos='inline')
+ hilighted = highlight(inner, lexer, formatter)
+ return hilighted
+
+
+class TT(CreoleSCTN):
+ """
+ blah blah {{{ something }}} blah blah
+ """
+ not_in_all = True
+ open_pattern = re.compile(r'\{\{\{(?P<inner>.+)\}\}\}')
+
+ def parse(self):
+ # Get rid of the inner stuff
+ self.parse_inner()
+ return '<tt>%s</tt>' % self.match.groupdict()['inner']
+
+
+register(Italics)
+register(Bold)
+register(BulletList)
+register(NumberedList)
+register(Link)
+register(Heading)
+register(HorizontalLine)
+register(Image)
+register(Code)
+register(TT)
View
@@ -310,7 +310,7 @@ def parse(self):
if self.argument.isdigit():
return '<th colspan="%s">%s</th>' % (self.argument, self.parse_inner())
else:
- soft_fail("Head argument must be digit")
+ soft_raise("Head argument must be digit")
return '<th>%s</th>' % self.parse_inner()
View
@@ -411,14 +411,17 @@ class Code(ArgumentTagNode):
def parse(self):
"""
pygment highlighting
- """
- from pygments import highlight
- from pygments.lexers import guess_lexer, get_lexer_by_name, TextLexer
- from pygments.formatters import HtmlFormatter
- from pygments.util import ClassNotFound
+ """
inner = ''
for node in self.nodes:
inner += node.raw_content
+ try:
+ from pygments import highlight
+ from pygments.lexers import guess_lexer, get_lexer_by_name, TextLexer
+ from pygments.formatters import HtmlFormatter
+ from pygments.util import ClassNotFound
+ except ImportError:
+ return '<pre>%s</pre>' % inner
if self.argument:
try:
lexer = get_lexer_by_name(self.argument)
Oops, something went wrong.

0 comments on commit f5700fa

Please sign in to comment.