Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 43 additions & 21 deletions markdown/extensions/codehilite.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,18 @@ class CodeHilite(object):
* src: Source string or any object with a .readline attribute.

* linenums: (Boolean) Set line numbering to 'on' (True),
'off' (False) or 'auto'(None). Set to 'auto' by default.
'off' (False) or 'auto' (None). Set to 'auto' by default.

* guess_lang: (Boolean) Turn language auto-detection
'on' or 'off' (on by default).

* css_class: Set class name of wrapper div ('codehilite' by default).

* hl_lines: (List of integers) Lines to emphasize, 1-indexed.
* lexer_options: Dictionary containing options passed to the Pygments lexer
if use_pygments is set to True.

* formatter_options: Dictionary containing options passed to the Pygments
formatter if use_pygments is set to True.

Low Level Usage:
>>> code = CodeHilite()
Expand All @@ -75,7 +79,8 @@ class CodeHilite(object):

def __init__(self, src=None, linenums=None, guess_lang=True,
css_class="codehilite", lang=None, style='default',
noclasses=False, tab_length=4, hl_lines=None, use_pygments=True):
noclasses=False, tab_length=4, use_pygments=True,
lexer_options={}, formatter_options={}):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking we should just do **options here. That does two things:

  1. It avoids the extra level of nesting when defining the configs.
  2. It avoids separating lexer and formatter options.

As it stands now, Pygments uses **options in their lexer and formatter classes, and ignores any unknown option keywords. So as long as there are no naming conflicts between lexer and formatter options, we could pass all options to both the lexer and formatter. This has the added benefit of making it easier to define options from within documents (see #816).

self.src = src
self.lang = lang
self.linenums = linenums
Expand All @@ -84,8 +89,9 @@ def __init__(self, src=None, linenums=None, guess_lang=True,
self.style = style
self.noclasses = noclasses
self.tab_length = tab_length
self.hl_lines = hl_lines or []
self.use_pygments = use_pygments
self.lexer_options = lexer_options
self.formatter_options = formatter_options

def hilite(self):
"""
Expand All @@ -105,21 +111,24 @@ def hilite(self):

if pygments and self.use_pygments:
try:
lexer = get_lexer_by_name(self.lang)
lexer = get_lexer_by_name(self.lang, **self.lexer_options)
except ValueError:
try:
if self.guess_lang:
lexer = guess_lexer(self.src)
lexer = guess_lexer(self.src, **self.lexer_options)
else:
lexer = get_lexer_by_name('text')
lexer = get_lexer_by_name('text', **self.lexer_options)
except ValueError:
lexer = get_lexer_by_name('text')
formatter = get_formatter_by_name('html',
linenos=self.linenums,
cssclass=self.css_class,
style=self.style,
noclasses=self.noclasses,
hl_lines=self.hl_lines)
lexer = get_lexer_by_name('text', **self.lexer_options)
if self.linenums:
self.formatter_options["linenos"] = True
if "cssclass" not in self.formatter_options:
self.formatter_options["cssclass"] = self.css_class
if "style" not in self.formatter_options:
self.formatter_options["style"] = self.style
if self.noclasses:
self.formatter_options["noclasses"] = True
formatter = get_formatter_by_name('html', **self.formatter_options)
return highlight(self.src, lexer, formatter)
else:
# just escape and build markup usable by JS highlighting libs
Expand Down Expand Up @@ -186,7 +195,9 @@ def _parseHeader(self):
# Overridable and Shebang exists - use line numbers
self.linenums = True

self.hl_lines = parse_hl_lines(m.group('hl_lines'))
if pygments and self.use_pygments:
self.formatter_options["hl_lines"] = parse_hl_lines(
m.group('hl_lines'))
else:
# No match
lines.insert(0, fl)
Expand Down Expand Up @@ -220,7 +231,9 @@ def run(self, root):
style=self.config['pygments_style'],
noclasses=self.config['noclasses'],
tab_length=self.md.tab_length,
use_pygments=self.config['use_pygments']
use_pygments=self.config['use_pygments'],
lexer_options=self.config['lexer_options'],
formatter_options=self.config['formatter_options']
)
placeholder = self.md.htmlStash.store(code.hilite())
# Clear codeblock in etree instance
Expand All @@ -238,22 +251,31 @@ def __init__(self, **kwargs):
# define default configs
self.config = {
'linenums': [None,
"Use lines numbers. True=yes, False=no, None=auto"],
"Use line numbers. True=yes, False=no, None=auto"],
'guess_lang': [True,
"Automatic language detection - Default: True"],
'css_class': ["codehilite",
"Set class name for wrapper <div> - "
"Default: codehilite"],
'pygments_style': ['default',
'Pygments HTML Formatter Style '
'(Deprecated, use the style Pygments formatter '
'option instead) Pygments HTML Formatter Style '
'(Colorscheme) - Default: default'],
'noclasses': [False,
'Use inline styles instead of CSS classes - '
'Default false'],
'(Deprecated, use the noclasses Pygments formatter '
'option instead) Use inline styles instead of CSS '
'classes - Default false'],
'use_pygments': [True,
'Use Pygments to Highlight code blocks. '
'Disable if using a JavaScript library. '
'Default: True']
'Default: True'],
'lexer_options': [{},
'Dictionary containing options passed to the '
'Pygments lexer if use_pygments is set to True.'],
'formatter_options': [{},
'Dictionary containing options passed to '
'the Pygments formatter if use_pygments is '
'set to True.']
}

super(CodeHiliteExtension, self).__init__(**kwargs)
Expand Down