diff --git a/.spell-dict b/.spell-dict
index fbe4865a7..6408100fc 100644
--- a/.spell-dict
+++ b/.spell-dict
@@ -160,3 +160,4 @@ plugin
plugins
configs
pre
+formatters
diff --git a/docs/change_log/release-3.4.md b/docs/change_log/release-3.4.md
index 025d443d7..6dc840182 100644
--- a/docs/change_log/release-3.4.md
+++ b/docs/change_log/release-3.4.md
@@ -32,7 +32,7 @@ In addition, tests were moved to the modern test environment.
## New features
-The following new features have been included in the 3.3 release:
+The following new features have been included in the 3.4 release:
* Use `style` attribute in tables for alignment instead of `align` for better CSS
inter-operation. The old behavior is available by setting `use_align_attribute=True` when
@@ -55,6 +55,12 @@ The following new features have been included in the 3.3 release:
parameter which can be used to set the CSS class(es) on the `
` that contains the
Table of Contents (#1224).
+* The Codehilite extension now supports a `pygments_formatter` option that can be set to
+ use a custom formatter class with Pygments.
+ - If set to a string like `'html'`, we get the default formatter by that name.
+ - If set to a class (or any callable), it is called with all the options to get a
+ formatter instance.
+
## Bug fixes
The following bug fixes are included in the 3.4 release:
diff --git a/docs/extensions/code_hilite.md b/docs/extensions/code_hilite.md
index caaf011c0..6fa6190c7 100644
--- a/docs/extensions/code_hilite.md
+++ b/docs/extensions/code_hilite.md
@@ -234,6 +234,15 @@ The following options are provided to configure the output:
This option only applies when `use_pygments` is `False` as Pygments does not provide an option to include a
language prefix.
+* **`pygments_formatter`**{ #pygments_formatter }:
+ This option can be used to change the Pygments formatter used for highlighting the code blocks. By default, this
+ is set to the string `'html'`, which means it'll use the default `HtmlFormatter` provided by Pygments.
+
+ This can be set to a string representing any of the other default formatters, or set to a formatter class (or
+ any callable).
+
+ To see what formatters are available and how to subclass an existing formatter, please visit [Pygments
+ documentation on this topic][pygments formatters].
* Any other Pygments' options:
@@ -250,3 +259,4 @@ markdown.markdown(some_text, extensions=['codehilite'])
[html formatter]: https://pygments.org/docs/formatters/#HtmlFormatter
[lexer]: https://pygments.org/docs/lexers/
[spec]: https://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
+[pygments formatters]: https://pygments.org/docs/formatters/
diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py
index a768b73c9..b92ebdce7 100644
--- a/markdown/extensions/codehilite.py
+++ b/markdown/extensions/codehilite.py
@@ -23,6 +23,7 @@
from pygments import highlight
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.formatters import get_formatter_by_name
+ from pygments.util import ClassNotFound
pygments = True
except ImportError: # pragma: no cover
pygments = False
@@ -99,6 +100,7 @@ def __init__(self, src, **options):
self.guess_lang = options.pop('guess_lang', True)
self.use_pygments = options.pop('use_pygments', True)
self.lang_prefix = options.pop('lang_prefix', 'language-')
+ self.pygments_formatter = options.pop('pygments_formatter', 'html')
if 'linenos' not in options:
options['linenos'] = options.pop('linenums', None)
@@ -139,7 +141,13 @@ def hilite(self, shebang=True):
lexer = get_lexer_by_name('text', **self.options)
except ValueError: # pragma: no cover
lexer = get_lexer_by_name('text', **self.options)
- formatter = get_formatter_by_name('html', **self.options)
+ if isinstance(self.pygments_formatter, str):
+ try:
+ formatter = get_formatter_by_name(self.pygments_formatter, **self.options)
+ except ClassNotFound:
+ formatter = get_formatter_by_name('html', **self.options)
+ else:
+ formatter = self.pygments_formatter(**self.options)
return highlight(self.src, lexer, formatter)
else:
# just escape and build markup usable by JS highlighting libs
@@ -279,7 +287,11 @@ def __init__(self, **kwargs):
'lang_prefix': [
'language-',
'Prefix prepended to the language when use_pygments is false. Default: "language-"'
- ]
+ ],
+ 'pygments_formatter': ['html',
+ 'Use a specific formatter for Pygments hilighting.'
+ 'Default: "html"',
+ ],
}
for key, value in kwargs.items():
diff --git a/tests/test_syntax/extensions/test_fenced_code.py b/tests/test_syntax/extensions/test_fenced_code.py
index 2cdde98c3..f8c3e91d3 100644
--- a/tests/test_syntax/extensions/test_fenced_code.py
+++ b/tests/test_syntax/extensions/test_fenced_code.py
@@ -21,10 +21,12 @@
from markdown.test_tools import TestCase
import markdown
+import markdown.extensions.codehilite
import os
try:
import pygments # noqa
+ import pygments.formatters # noqa
has_pygments = True
except ImportError:
has_pygments = False
@@ -800,7 +802,7 @@ def testFencedLanguageAttrNoclasses(self):
extensions=['codehilite', 'fenced_code']
)
- def testFencedMultpleBlocksSameStyle(self):
+ def testFencedMultipleBlocksSameStyle(self):
if has_pygments:
# See also: https://github.com/Python-Markdown/markdown/issues/1240
expected = (
@@ -844,3 +846,129 @@ def testFencedMultpleBlocksSameStyle(self):
'fenced_code'
]
)
+
+ def testCustomPygmentsFormatter(self):
+ if has_pygments:
+ class CustomFormatter(pygments.formatters.HtmlFormatter):
+ def wrap(self, source, outfile):
+ return self._wrap_div(self._wrap_code(source))
+
+ def _wrap_code(self, source):
+ yield 0, ''
+ for i, t in source:
+ if i == 1:
+ t += ' '
+ yield i, t
+ yield 0, ''
+
+ expected = '''
+