From ee6fb73b160721e666e72a0921135d6abae19fa4 Mon Sep 17 00:00:00 2001 From: Lucas van Dijk Date: Mon, 25 Jan 2010 21:11:26 +0100 Subject: Refactored codehilite and fenced code, so now fenced code will also be highlited when codehilite extension is enabled --- markdown/__init__.py | 27 ++++++------ markdown/extensions/codehilite.py | 90 +++++++++++++++++++++----------------- markdown/extensions/fenced_code.py | 71 ++++++++++++------------------ 3 files changed, 94 insertions(+), 94 deletions(-) diff --git a/markdown/__init__.py b/markdown/__init__.py index 211e3d5..26314f6 100644 --- a/markdown/__init__.py +++ b/markdown/__init__.py @@ -182,7 +182,7 @@ class Markdown: def __init__(self, extensions=[], extension_configs={}, - safe_mode = False, + safe_mode = False, output_format=DEFAULT_OUTPUT_FORMAT): """ Creates a new Markdown instance. @@ -200,12 +200,12 @@ class Markdown: * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). * "html4": Outputs HTML 4 * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" + Note that it is suggested that the more specific formats ("xhtml1" and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. + if it makes sense at that time. """ - + self.safeMode = safe_mode self.registeredExtensions = [] self.docType = "" @@ -300,9 +300,9 @@ class Markdown: # Map format keys to serializers self.output_formats = { - 'html' : html4.to_html_string, + 'html' : html4.to_html_string, 'html4' : html4.to_html_string, - 'xhtml' : etree.tostring, + 'xhtml' : etree.tostring, 'xhtml1': etree.tostring, } @@ -348,7 +348,8 @@ class Markdown: self.references.clear() for extension in self.registeredExtensions: - extension.reset() + if hasattr(extension, 'reset'): + extension.reset() def set_output_format(self, format): """ Set the output format for the class instance. """ @@ -546,8 +547,8 @@ def load_extension(ext_name, configs = []): # function called makeExtension() try: return module.makeExtension(configs.items()) - except AttributeError: - message(CRITICAL, "Failed to initiate extension '%s'" % ext_name) + except AttributeError, e: + message(CRITICAL, "Failed to initiate extension '%s': %s" % (ext_name, e)) def load_extensions(ext_names): @@ -588,15 +589,15 @@ def markdown(text, * "xhtml": Outputs latest supported version of XHTML (currently XHTML 1.1). * "html4": Outputs HTML 4 * "html": Outputs latest supported version of HTML (currently HTML 4). - Note that it is suggested that the more specific formats ("xhtml1" + Note that it is suggested that the more specific formats ("xhtml1" and "html4") be used as "xhtml" or "html" may change in the future - if it makes sense at that time. + if it makes sense at that time. Returns: An HTML document as a string. """ md = Markdown(extensions=load_extensions(extensions), - safe_mode=safe_mode, + safe_mode=safe_mode, output_format=output_format) return md.convert(text) @@ -608,7 +609,7 @@ def markdownFromFile(input = None, safe_mode = False, output_format = DEFAULT_OUTPUT_FORMAT): """Read markdown code from a file and write it to a file or a stream.""" - md = Markdown(extensions=load_extensions(extensions), + md = Markdown(extensions=load_extensions(extensions), safe_mode=safe_mode, output_format=output_format) md.convertFile(input, output, encoding) diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py index c5d496b..b9e1760 100644 --- a/markdown/extensions/codehilite.py +++ b/markdown/extensions/codehilite.py @@ -10,9 +10,9 @@ Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/). Project website: Contact: markdown@freewisdom.org - + License: BSD (see ../docs/LICENSE for details) - + Dependencies: * [Python 2.3+](http://python.org/) * [Markdown 2.0+](http://www.freewisdom.org/projects/python-markdown/) @@ -38,41 +38,45 @@ class CodeHilite: Basic Usage: >>> code = CodeHilite(src = 'some text') >>> html = code.hilite() - + * src: Source string or any object with a .readline attribute. - + * linenos: (Boolen) Turn line numbering 'on' or 'off' (off by default). * css_class: Set class name of wrapper div ('codehilite' by default). - + Low Level Usage: >>> code = CodeHilite() >>> code.src = 'some text' # String or anything with a .readline attr. >>> code.linenos = True # True or False; Turns line numbering on or of. >>> html = code.hilite() - + """ - def __init__(self, src=None, linenos=False, css_class="codehilite"): + def __init__(self, src=None, linenos=False, css_class="codehilite", + lang=None, style='default', noclasses=False): self.src = src - self.lang = None + self.lang = lang self.linenos = linenos self.css_class = css_class + self.style = style + self.noclasses = noclasses def hilite(self): """ - Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with - optional line numbers. The output should then be styled with css to - your liking. No styles are applied by default - only styling hooks - (i.e.: ). + Pass code to the [Pygments](http://pygments.pocoo.org/) highliter with + optional line numbers. The output should then be styled with css to + your liking. No styles are applied by default - only styling hooks + (i.e.: ). returns : A string of html. - + """ self.src = self.src.strip('\n') - - self._getLang() + + if self.lang == None: + self._getLang() try: from pygments import highlight @@ -96,8 +100,10 @@ class CodeHilite: lexer = guess_lexer(self.src) except ValueError: lexer = TextLexer() - formatter = HtmlFormatter(linenos=self.linenos, - cssclass=self.css_class) + formatter = HtmlFormatter(linenos=self.linenos, + cssclass=self.css_class, + style=self.style, + noclasses=self.noclasses) return highlight(self.src, lexer, formatter) def _escape(self, txt): @@ -114,8 +120,8 @@ class CodeHilite: txt = txt.replace('\t', ' '*TAB_LENGTH) txt = txt.replace(" "*4, "    ") txt = txt.replace(" "*3, "   ") - txt = txt.replace(" "*2, "  ") - + txt = txt.replace(" "*2, "  ") + # Add line numbers lines = txt.splitlines() txt = '
    \n' @@ -126,31 +132,31 @@ class CodeHilite: def _getLang(self): - """ + """ Determines language of a code block from shebang lines and whether said line should be removed or left in place. If the sheband line contains a path (even a single /) then it is assumed to be a real shebang lines and - left alone. However, if no path is given (e.i.: #!python or :::python) + left alone. However, if no path is given (e.i.: #!python or :::python) then it is assumed to be a mock shebang for language identifitation of a - code fragment and removed from the code block prior to processing for - code highlighting. When a mock shebang (e.i: #!python) is found, line - numbering is turned on. When colons are found in place of a shebang - (e.i.: :::python), line numbering is left in the current state - off + code fragment and removed from the code block prior to processing for + code highlighting. When a mock shebang (e.i: #!python) is found, line + numbering is turned on. When colons are found in place of a shebang + (e.i.: :::python), line numbering is left in the current state - off by default. - + """ import re - + #split text into lines lines = self.src.split("\n") #pull first line to examine fl = lines.pop(0) - + c = re.compile(r''' (?:(?:::+)|(?P[#]!)) # Shebang or 2 or more colons. - (?P(?:/\w+)*[/ ])? # Zero or 1 path - (?P[\w+-]*) # The language + (?P(?:/\w+)*[/ ])? # Zero or 1 path + (?P[\w+-]*) # The language ''', re.VERBOSE) # search first line for shebang m = c.search(fl) @@ -169,7 +175,7 @@ class CodeHilite: else: # No match lines.insert(0, fl) - + self.src = "\n".join(lines).strip("\n") @@ -184,14 +190,16 @@ class HiliteTreeprocessor(markdown.treeprocessors.Treeprocessor): for block in blocks: children = block.getchildren() if len(children) == 1 and children[0].tag == 'code': - code = CodeHilite(children[0].text, + code = CodeHilite(children[0].text, linenos=self.config['force_linenos'][0], - css_class=self.config['css_class'][0]) - placeholder = self.markdown.htmlStash.store(code.hilite(), + css_class=self.config['css_class'][0], + style=self.config['pygments_style'][0], + noclasses=self.config['noclasses'][0]) + placeholder = self.markdown.htmlStash.store(code.hilite(), safe=True) # Clear codeblock in etree instance block.clear() - # Change to p element which will later + # Change to p element which will later # be removed when inserting raw html block.tag = 'p' block.text = placeholder @@ -204,19 +212,23 @@ class CodeHiliteExtension(markdown.Extension): # define default configs self.config = { 'force_linenos' : [False, "Force line numbers - Default: False"], - 'css_class' : ["codehilite", + 'css_class' : ["codehilite", "Set class name for wrapper
    - Default: codehilite"], + 'pygments_style' : ['tango', 'Pygments HTML Formatter Style (Colorscheme) - Default: tango'], + 'noclasses': [False, 'Use inline styles instead of CSS classes - Default false'] } - + # Override defaults with user settings for key, value in configs: - self.setConfig(key, value) + self.setConfig(key, value) def extendMarkdown(self, md, md_globals): """ Add HilitePostprocessor to Markdown instance. """ hiliter = HiliteTreeprocessor(md) hiliter.config = self.config - md.treeprocessors.add("hilite", hiliter, "_begin") + md.treeprocessors.add("hilite", hiliter, "_begin") + + md.registerExtension(self) def makeExtension(configs={}): diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py index f16f94c..2b03bbc 100644 --- a/markdown/extensions/fenced_code.py +++ b/markdown/extensions/fenced_code.py @@ -62,15 +62,7 @@ Dependencies: """ import markdown, re - -try: - from pygments import highlight - from pygments.lexers import get_lexer_by_name - from pygments.formatters import HtmlFormatter -except ImportError: - HAS_PYGMENTS = False -else: - HAS_PYGMENTS = True +from markdown.extensions.codehilite import CodeHilite, CodeHiliteExtension # Global vars FENCED_BLOCK_RE = re.compile( \ @@ -80,32 +72,24 @@ FENCED_BLOCK_RE = re.compile( \ CODE_WRAP = '
    %s
    ' LANG_TAG = ' class="%s"' - class FencedCodeExtension(markdown.Extension): - def __init__(self, configs): - self.config = { - 'HIGHLIGHT_STYLE': ['tango', 'The pygments HTML Formatter style to use'], - 'NO_CSS_CLASSES': [True, 'Do not use any CSS classes, use inline styles instead'], - 'LINENOS': [True, 'Display line numbers'] - } - - for key, value in configs: - self.config[key][0] = value - def extendMarkdown(self, md, md_globals): """ Add FencedBlockPreprocessor to the Markdown instance. """ + md.registerExtension(self) md.preprocessors.add('fenced_code_block', - FencedBlockPreprocessor(md, self.config), + FencedBlockPreprocessor(md), "_begin") class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor): - def __init__(self, md, config): + def __init__(self, md): markdown.preprocessors.Preprocessor.__init__(self, md) - self.config = config + + self.checked_for_codehilite = False + self.codehilite_conf = {} def getConfig(self, key): if key in self.config: @@ -115,33 +99,36 @@ class FencedBlockPreprocessor(markdown.preprocessors.Preprocessor): def run(self, lines): """ Match and store Fenced Code Blocks in the HtmlStash. """ + + # Check for code hilite extension + if not self.checked_for_codehilite: + for ext in self.markdown.registeredExtensions: + if isinstance(ext, CodeHiliteExtension): + self.codehilite_conf = ext.config + break + + self.checked_for_codehilite = True + text = "\n".join(lines) while 1: m = FENCED_BLOCK_RE.search(text) if m: - highlighted = False - lang = '' if m.group('lang'): lang = LANG_TAG % m.group('lang') - # If pygments installed, highlight code - if HAS_PYGMENTS and m.group('lang'): - formatter = HtmlFormatter( - linenos=self.getConfig('LINENOS'), - style=self.getConfig('HIGHLIGHT_STYLE'), - noclasses=self.getConfig('NO_CSS_CLASSES') - ) - - try: - lexer = get_lexer_by_name(m.group('lang')) - except: - highlighted = False - else: - highlighted = True - code = highlight(m.group('code'), lexer, formatter) - - if not highlighted: + # If config is not empty, then the codehighlite extension + # is enabled, so we call it to highlite the code + if self.codehilite_conf: + highliter = CodeHilite(m.group('code'), + linenos=self.codehilite_conf['force_linenos'][0], + css_class=self.codehilite_conf['css_class'][0], + style=self.codehilite_conf['pygments_style'][0], + lang=(m.group('lang') if m.group('lang') else None), + noclasses=self.codehilite_conf['noclasses'][0]) + + code = highliter.hilite() + else: code = CODE_WRAP % (lang, self._escape(m.group('code'))) placeholder = self.markdown.htmlStash.store(code, safe=True) -- cgit v1.2.3