2018-03-12 23:04:18 +11:00
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
from logging import getLogger
|
|
|
|
|
|
|
|
from django.conf import settings
|
2018-04-06 03:16:02 +10:00
|
|
|
from django.template.defaultfilters import escape, linebreaksbr
|
|
|
|
from django.template.loader import render_to_string
|
2018-04-06 05:18:22 +10:00
|
|
|
from django.utils.safestring import mark_safe
|
2018-03-12 23:04:18 +11:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from pygments import highlight
|
|
|
|
from pygments.formatters.html import HtmlFormatter
|
|
|
|
from pygments.lexers import get_lexer_by_name
|
|
|
|
from pygments.lexers.python import PythonLexer
|
|
|
|
from pygments.util import ClassNotFound
|
|
|
|
|
|
|
|
logger = getLogger(__file__)
|
|
|
|
|
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
class NakedHtmlFormatter(HtmlFormatter):
|
2018-04-06 03:16:02 +10:00
|
|
|
"""Pygments HTML formatter with no further HTML tags."""
|
2011-05-30 09:03:04 +10:00
|
|
|
def wrap(self, source, outfile):
|
|
|
|
return self._wrap_code(source)
|
2013-03-20 00:08:56 +11:00
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
def _wrap_code(self, source):
|
|
|
|
for i, t in source:
|
|
|
|
yield i, t
|
|
|
|
|
2018-03-12 23:04:18 +11:00
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Highlight Code Snippets
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
class Highlighter(object):
|
|
|
|
template_name = 'dpaste/highlight/code.html'
|
|
|
|
|
|
|
|
def highlight(self, code_string, lexer_name=None):
|
|
|
|
"""Subclasses need to override this."""
|
|
|
|
return code_string
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_lexer_display_name(lexer_name, fallback=_('(Deprecated Lexer)')):
|
|
|
|
for l in TEXT_FORMATTER + CODE_FORMATTER:
|
|
|
|
if l[0] == lexer_name:
|
|
|
|
return l[1]
|
|
|
|
return fallback
|
|
|
|
|
|
|
|
def render(self, code_string, lexer_name, **kwargs):
|
2018-04-13 07:28:16 +10:00
|
|
|
highlighted_string = self.highlight(code_string, lexer_name=lexer_name)
|
2018-04-06 03:16:02 +10:00
|
|
|
context = {
|
|
|
|
'highlighted': highlighted_string,
|
|
|
|
'highlighted_splitted': highlighted_string.splitlines(),
|
|
|
|
'lexer_name': lexer_name,
|
|
|
|
'lexer_display_name': self.get_lexer_display_name(lexer_name),
|
|
|
|
}
|
|
|
|
context.update(kwargs)
|
|
|
|
return render_to_string(self.template_name, context)
|
|
|
|
|
2018-03-12 23:04:18 +11:00
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
class PlainTextHighlighter(Highlighter):
|
|
|
|
"""Plain Text. Just replace linebreaks."""
|
|
|
|
template_name = 'dpaste/highlight/text.html'
|
|
|
|
|
2018-04-13 07:28:16 +10:00
|
|
|
def highlight(self, code_string, **kwargs):
|
2018-04-06 03:16:02 +10:00
|
|
|
return linebreaksbr(code_string)
|
|
|
|
|
2018-04-06 05:18:22 +10:00
|
|
|
|
|
|
|
|
|
|
|
class MarkdownHighlighter(PlainTextHighlighter):
|
|
|
|
"""Markdown"""
|
2018-04-06 05:22:47 +10:00
|
|
|
extensions = ('tables', 'fenced-code', 'footnotes', 'autolink,',
|
|
|
|
'strikethrough', 'underline', 'quote', 'superscript',
|
|
|
|
'math')
|
|
|
|
render_flags = ('skip-html',)
|
2018-04-06 05:18:22 +10:00
|
|
|
|
2018-04-13 07:28:16 +10:00
|
|
|
def highlight(self, code_string, **kwargs):
|
2018-04-06 05:18:22 +10:00
|
|
|
import misaka
|
2018-04-06 05:47:29 +10:00
|
|
|
return mark_safe(misaka.html(code_string,
|
|
|
|
extensions=self.extensions,
|
2018-04-06 05:22:47 +10:00
|
|
|
render_flags=self.render_flags))
|
2018-04-06 05:18:22 +10:00
|
|
|
|
|
|
|
|
2018-04-06 05:47:29 +10:00
|
|
|
class RestructuredTextHighlighter(PlainTextHighlighter):
|
|
|
|
"""Restructured Text"""
|
|
|
|
rst_part_name = 'html_body'
|
|
|
|
publish_args = {
|
|
|
|
'writer_name': 'html5_polyglot',
|
|
|
|
'settings_overrides': {
|
|
|
|
'raw_enabled': False,
|
|
|
|
'file_insertion_enabled': False,
|
2018-04-13 07:28:16 +10:00
|
|
|
'halt_level': 5,
|
|
|
|
'report_level': 2,
|
2018-04-07 17:14:43 +10:00
|
|
|
'warning_stream': '/dev/null',
|
2018-04-06 05:47:29 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 07:28:16 +10:00
|
|
|
def highlight(self, code_string, **kwargs):
|
2018-04-06 05:47:29 +10:00
|
|
|
from docutils.core import publish_parts
|
|
|
|
self.publish_args['source'] = code_string
|
|
|
|
parts = publish_parts(**self.publish_args)
|
|
|
|
return mark_safe(parts[self.rst_part_name])
|
|
|
|
|
2018-04-07 17:14:43 +10:00
|
|
|
|
2018-04-06 05:18:22 +10:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
class PlainCodeHighlighter(Highlighter):
|
|
|
|
"""Plain Code. No highlighting but Pygments like span tags around each line."""
|
|
|
|
|
2018-04-13 07:28:16 +10:00
|
|
|
def highlight(self, code_string, **kwargs):
|
2018-03-24 19:26:12 +11:00
|
|
|
return '\n'.join(['<span class="plain">{}</span>'.format(escape(l) or '​')
|
2014-04-13 01:01:09 +10:00
|
|
|
for l in code_string.splitlines()])
|
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
|
|
|
|
class PygmentsHighlighter(Highlighter):
|
|
|
|
"""
|
|
|
|
Highlight code string with Pygments. The lexer is automaticially
|
|
|
|
determined by the lexer name.
|
|
|
|
"""
|
2018-04-07 17:14:43 +10:00
|
|
|
formatter = NakedHtmlFormatter()
|
2018-04-06 03:34:26 +10:00
|
|
|
lexer = None
|
|
|
|
lexer_fallback = PythonLexer()
|
2018-04-06 03:16:02 +10:00
|
|
|
|
|
|
|
def highlight(self, code_string, lexer_name):
|
2018-04-06 03:34:26 +10:00
|
|
|
if not self.lexer:
|
|
|
|
try:
|
|
|
|
self.lexer = get_lexer_by_name(lexer_name)
|
|
|
|
except ClassNotFound:
|
|
|
|
logger.warning('Lexer for given name %s not found', lexer_name)
|
|
|
|
self.lexer = self.lexer_fallback
|
|
|
|
|
2018-04-07 17:14:43 +10:00
|
|
|
return highlight(code_string, self.lexer, self.formatter)
|
2018-04-06 03:34:26 +10:00
|
|
|
|
|
|
|
|
|
|
|
class SolidityHighlighter(PygmentsHighlighter):
|
|
|
|
"""Solidity Specific Highlighter. This uses a 3rd party Pygments lexer."""
|
|
|
|
|
|
|
|
def __init__(self):
|
2018-04-06 03:37:33 +10:00
|
|
|
# SolidityLexer does not necessarily need to be installed
|
|
|
|
# since its imported here and not used later.
|
2018-04-06 03:34:26 +10:00
|
|
|
from pygments_lexer_solidity import SolidityLexer
|
|
|
|
self.lexer = SolidityLexer()
|
2018-03-12 23:04:18 +11:00
|
|
|
|
2017-09-13 17:26:14 +10:00
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
def get_highlighter_class(lexer_name):
|
|
|
|
"""
|
|
|
|
Get Highlighter for lexer name.
|
2018-03-12 23:04:18 +11:00
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
If the found lexer tuple does not provide a Highlighter class,
|
|
|
|
use the generic Pygments highlighter.
|
2018-03-12 23:04:18 +11:00
|
|
|
|
2018-04-06 03:16:02 +10:00
|
|
|
If no suitable highlighter is found, return the generic
|
|
|
|
PlainCode Highlighter.
|
|
|
|
"""
|
|
|
|
for c in TEXT_FORMATTER + CODE_FORMATTER:
|
|
|
|
if c[0] == lexer_name:
|
|
|
|
if len(c) == 3:
|
|
|
|
return c[2]
|
|
|
|
return PygmentsHighlighter
|
|
|
|
return PlainCodeHighlighter
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Lexer List
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
# Lexer list. Each list contains a lexer tuple of:
|
|
|
|
#
|
|
|
|
# (Lexer key,
|
|
|
|
# Lexer Display Name,
|
|
|
|
# Lexer Highlight Class)
|
|
|
|
#
|
|
|
|
# If the Highlight Class is not given, PygmentsHighlighter is used.
|
|
|
|
|
|
|
|
# Default Highlight Types
|
|
|
|
PLAIN_TEXT = '_text' # lexer name whats rendered as text (paragraphs)
|
|
|
|
PLAIN_CODE = '_code' # lexer name of code with no hihglighting
|
|
|
|
|
|
|
|
TEXT_FORMATTER = [
|
|
|
|
(PLAIN_TEXT, 'Plain Text', PlainTextHighlighter),
|
2018-04-06 05:18:22 +10:00
|
|
|
('_markdown', 'Markdown', MarkdownHighlighter),
|
2018-04-06 05:47:29 +10:00
|
|
|
('_rst', 'reStructuredText', RestructuredTextHighlighter),
|
2018-04-06 05:18:22 +10:00
|
|
|
#('_textile', 'Textile', MarkdownHighlighter),
|
2018-04-06 03:16:02 +10:00
|
|
|
]
|
|
|
|
|
|
|
|
CODE_FORMATTER = [
|
|
|
|
(PLAIN_CODE, 'Plain Code', PlainCodeHighlighter),
|
|
|
|
('abap', 'ABAP'),
|
|
|
|
('apacheconf', 'ApacheConf'),
|
|
|
|
('applescript', 'AppleScript'),
|
|
|
|
('as', 'ActionScript'),
|
|
|
|
('bash', 'Bash'),
|
|
|
|
('bbcode', 'BBCode'),
|
|
|
|
('c', 'C'),
|
|
|
|
('cpp', 'C++'),
|
|
|
|
('clojure', 'Clojure'),
|
|
|
|
('cobol', 'COBOL'),
|
|
|
|
('css', 'CSS'),
|
|
|
|
('cuda', 'CUDA'),
|
|
|
|
('dart', 'Dart'),
|
|
|
|
('delphi', 'Delphi'),
|
|
|
|
('diff', 'Diff'),
|
|
|
|
('django', 'Django'),
|
|
|
|
('erlang', 'Erlang'),
|
|
|
|
('fortran', 'Fortran'),
|
|
|
|
('go', 'Go'),
|
|
|
|
('groovy', 'Groovy'),
|
|
|
|
('haml', 'Haml'),
|
|
|
|
('haskell', 'Haskell'),
|
|
|
|
('html', 'HTML'),
|
|
|
|
('http', 'HTTP'),
|
|
|
|
('ini', 'INI'),
|
|
|
|
('irc', 'IRC'),
|
|
|
|
('java', 'Java'),
|
|
|
|
('js', 'JavaScript'),
|
|
|
|
('json', 'JSON'),
|
|
|
|
('lua', 'Lua'),
|
|
|
|
('make', 'Makefile'),
|
|
|
|
('mako', 'Mako'),
|
|
|
|
('mason', 'Mason'),
|
|
|
|
('matlab', 'Matlab'),
|
|
|
|
('modula2', 'Modula'),
|
|
|
|
('monkey', 'Monkey'),
|
|
|
|
('mysql', 'MySQL'),
|
|
|
|
('numpy', 'NumPy'),
|
|
|
|
('objc', 'Obj-C'),
|
|
|
|
('ocaml', 'OCaml'),
|
|
|
|
('perl', 'Perl'),
|
|
|
|
('php', 'PHP'),
|
|
|
|
('postscript', 'PostScript'),
|
|
|
|
('powershell', 'PowerShell'),
|
|
|
|
('prolog', 'Prolog'),
|
|
|
|
('properties', 'Properties'),
|
|
|
|
('puppet', 'Puppet'),
|
|
|
|
('python', 'Python'),
|
|
|
|
('r', 'R'),
|
|
|
|
('rb', 'Ruby'),
|
|
|
|
('rst', 'reStructuredText'),
|
|
|
|
('rust', 'Rust'),
|
|
|
|
('sass', 'Sass'),
|
|
|
|
('scala', 'Scala'),
|
|
|
|
('scheme', 'Scheme'),
|
|
|
|
('scilab', 'Scilab'),
|
|
|
|
('scss', 'SCSS'),
|
|
|
|
('smalltalk', 'Smalltalk'),
|
|
|
|
('smarty', 'Smarty'),
|
2018-04-06 03:34:26 +10:00
|
|
|
('solidity', 'Solidity', SolidityHighlighter),
|
2018-04-06 03:16:02 +10:00
|
|
|
('sql', 'SQL'),
|
|
|
|
('tcl', 'Tcl'),
|
|
|
|
('tcsh', 'Tcsh'),
|
|
|
|
('tex', 'TeX'),
|
|
|
|
('vb.net', 'VB.net'),
|
|
|
|
('vim', 'VimL'),
|
|
|
|
('xml', 'XML'),
|
|
|
|
('xquery', 'XQuery'),
|
|
|
|
('xslt', 'XSLT'),
|
|
|
|
('yaml', 'YAML'),
|
|
|
|
]
|
|
|
|
|
|
|
|
# Generat a list of Form choices of all lexer.
|
|
|
|
LEXER_CHOICES = (
|
|
|
|
(_('Text'), [i[:2] for i in TEXT_FORMATTER]),
|
|
|
|
(_('Code'), [i[:2] for i in CODE_FORMATTER])
|
|
|
|
)
|
|
|
|
|
|
|
|
# List of all Lexer Keys
|
|
|
|
LEXER_KEYS = [i[0] for i in TEXT_FORMATTER] + [i[0] for i in CODE_FORMATTER]
|
|
|
|
|
|
|
|
# The default lexer which we fallback in case of
|
|
|
|
# an error or if not supplied in an API call.
|
|
|
|
LEXER_DEFAULT = getattr(settings, 'DPASTE_LEXER_DEFAULT', 'python')
|
|
|
|
|
|
|
|
# Lexers which have wordwrap enabled by default
|
|
|
|
LEXER_WORDWRAP = getattr(settings, 'DPASTE_LEXER_WORDWRAP',
|
|
|
|
('rst',)
|
|
|
|
)
|