changeset 4009:7563624e712c

Added github flavored markdown style rendering into markdown rendered. It has a san newline handling behavior. More here: https://help.github.com/articles/github-flavored-markdown
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 17 Jun 2013 22:47:36 +0200
parents d8364b7e3451
children f81b1fded4c9
files rhodecode/lib/markup_renderer.py
diffstat 1 files changed, 48 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/lib/markup_renderer.py	Mon Jun 17 20:40:48 2013 +0200
+++ b/rhodecode/lib/markup_renderer.py	Mon Jun 17 22:47:36 2013 +0200
@@ -40,7 +40,7 @@
     RST_PAT = re.compile(r're?st', re.IGNORECASE)
     PLAIN_PAT = re.compile(r'readme', re.IGNORECASE)
 
-    def __detect_renderer(self, source, filename=None):
+    def _detect_renderer(self, source, filename=None):
         """
         runs detection of what renderer should be used for generating html
         from a markup language
@@ -62,6 +62,49 @@
 
         return getattr(MarkupRenderer, detected_renderer)
 
+    @classmethod
+    def _flavored_markdown(cls, text):
+        """
+        Github style flavored markdown
+
+        :param text:
+        """
+        from hashlib import md5
+
+        # Extract pre blocks.
+        extractions = {}
+        def pre_extraction_callback(matchobj):
+            digest = md5(matchobj.group(0)).hexdigest()
+            extractions[digest] = matchobj.group(0)
+            return "{gfm-extraction-%s}" % digest
+        pattern = re.compile(r'<pre>.*?</pre>', re.MULTILINE | re.DOTALL)
+        text = re.sub(pattern, pre_extraction_callback, text)
+
+        # Prevent foo_bar_baz from ending up with an italic word in the middle.
+        def italic_callback(matchobj):
+            s = matchobj.group(0)
+            if list(s).count('_') >= 2:
+                return s.replace('_', '\_')
+            return s
+        text = re.sub(r'^(?! {4}|\t)\w+_\w+_\w[\w_]*', italic_callback, text)
+
+        # In very clear cases, let newlines become <br /> tags.
+        def newline_callback(matchobj):
+            if len(matchobj.group(1)) == 1:
+                return matchobj.group(0).rstrip() + '  \n'
+            else:
+                return matchobj.group(0)
+        pattern = re.compile(r'^[\w\<][^\n]*(\n+)', re.MULTILINE)
+        text = re.sub(pattern, newline_callback, text)
+
+        # Insert pre block extractions.
+        def pre_insert_callback(matchobj):
+            return '\n\n' + extractions[matchobj.group(1)]
+        text = re.sub(r'{gfm-extraction-([0-9a-f]{32})\}',
+                      pre_insert_callback, text)
+
+        return text
+
     def render(self, source, filename=None):
         """
         Renders a given filename using detected renderer
@@ -72,7 +115,7 @@
         :param source:
         """
 
-        renderer = self.__detect_renderer(source, filename)
+        renderer = self._detect_renderer(source, filename)
         readme_data = renderer(source)
         return readme_data
 
@@ -94,10 +137,12 @@
         return '<br />' + source.replace("\n", '<br />')
 
     @classmethod
-    def markdown(cls, source, safe=True):
+    def markdown(cls, source, safe=True, flavored=False):
         source = safe_unicode(source)
         try:
             import markdown as __markdown
+            if flavored:
+                source = cls._flavored_markdown(source)
             return __markdown.markdown(source, ['codehilite', 'extra'])
         except ImportError:
             log.warning('Install markdown to use this function')