Back to index

moin  1.9.0~rc2
highlight.py
Go to the documentation of this file.
00001 # -*- coding: iso-8859-1 -*-
00002 """
00003     MoinMoin - highlighting parser using the Pygments highlighting library
00004 
00005     @copyright: 2008 Radomir Dopieralski <moindev@sheep.art.pl>
00006     @license: GNU GPL, see COPYING for details.
00007 """
00008 
00009 import re
00010 
00011 import pygments
00012 import pygments.util
00013 import pygments.lexers
00014 import pygments.formatter
00015 from pygments.token import Token
00016 
00017 from MoinMoin import config, wikiutil
00018 from MoinMoin.parser import parse_start_step
00019 from MoinMoin.support.python_compatibility import hash_new
00020 from MoinMoin.Page import Page
00021 
00022 Dependencies = []
00023 extensions = []
00024 extension_re = re.compile(r'^\*(\..*$)')
00025 for name, short, patterns, mime in pygments.lexers.get_all_lexers():
00026     for pattern in patterns:
00027         m = extension_re.match(pattern)
00028         if m and m.groups(0):
00029             extensions.extend(m.groups(0))
00030 
00031 
00032 class PygmentsFormatter(pygments.formatter.Formatter):
00033     """ a formatter for Pygments that uses the moin formatter for creating output """
00034     line_re = re.compile(r'(\n)')
00035 
00036     def __init__(self, formatter):
00037         pygments.formatter.Formatter.__init__(self)
00038         self.result = []
00039         self.formatter = formatter
00040 
00041     def get_class(self, ttype):
00042         if ttype in Token.Text:
00043             return None
00044         # Reuse existing MoinMoin css class names
00045         elif ttype in Token.Operator.Word:
00046             return 'ResWord'
00047         elif ttype in Token.Comment.Preproc:
00048             return 'Preprc'
00049         elif ttype in Token.Keyword.Constant:
00050             return 'ConsWord'
00051         elif ttype in Token.Keyword:
00052             return 'ResWord'
00053         elif ttype in Token.Name.Builtin:
00054             return 'ResWord'
00055         elif ttype in Token.Name.Constant:
00056             return 'ConsWord'
00057         elif ttype in Token.String.Char:
00058             return 'Char'
00059         elif ttype in Token.String.Escape:
00060             return 'SPChar'
00061         elif ttype in Token.String:
00062             return 'String'
00063         elif ttype in Token.Number:
00064             return 'Number'
00065         elif ttype in Token.Name:
00066             return 'ID'
00067         elif ttype in Token.Comment:
00068             return 'Comment'
00069         elif ttype in Token.Generic.Heading:
00070             return 'Comment'
00071         elif ttype in Token.Generic.Subheading:
00072             return 'DiffSeparator'
00073         elif ttype in Token.Generic.Inserted:
00074             return 'DiffAdded'
00075         elif ttype in Token.Generic.Deleted:
00076             return 'DiffRemoved'
00077         elif ttype in Token.Generic.Strong:
00078             return 'DiffChanged'
00079         else:
00080             # skip tags that have no class defined
00081             return None
00082             # ... or use the token's name when nothing apropriate
00083             # return str(ttype).replace(".", " ")
00084 
00085     def format(self, tokensource, outfile):
00086         line_ready = False
00087         fmt = self.formatter
00088         result = self.result
00089         for ttype, value in tokensource:
00090             class_ = self.get_class(ttype)
00091             if value:
00092                 for line in self.line_re.split(value):
00093                     if not line_ready:
00094                         result.append(fmt.code_line(1))
00095                         line_ready = True
00096                     if line == '\n':
00097                         result.append(fmt.code_line(0))
00098                         line_ready = False
00099                     else:
00100                         if class_:
00101                             result.append(fmt.code_token(1, class_))
00102                         result.append(fmt.text(line))
00103                         if class_:
00104                             result.append(fmt.code_token(0, class_))
00105         result[-2] = ''
00106         result[-1] = ''
00107 #        if line_ready:
00108 #            result.append(fmt.code_line(0))
00109 
00110 
00111 class Parser:
00112     parsername = "highlight"  # compatibility wrappers override this with the pygments lexer name
00113     Dependencies = Dependencies
00114     extensions = extensions
00115 
00116     def __init__(self, raw, request, filename=None, format_args='', **kw):
00117         self.request = request
00118         self.raw = raw.strip('\n')
00119         self.filename = filename
00120 
00121         if self.parsername == 'highlight':
00122             # user is directly using the highlight parser
00123             parts = format_args.split(None)
00124             if parts:
00125                 self.syntax = parts[0]
00126             else:
00127                 self.syntax = 'text'
00128             if len(parts) > 1:
00129                 params = ' '.join(parts[1:])
00130             else:
00131                 params = ''
00132         else:
00133             # a compatibility wrapper inherited from this class
00134             self.syntax = self.parsername
00135             params = format_args
00136         self.show_nums, self.num_start, self.num_step, attrs = parse_start_step(request, params)
00137 
00138     def format(self, formatter):
00139         _ = self.request.getText
00140         fmt = PygmentsFormatter(formatter)
00141         fmt.result.append(formatter.div(1, css_class="highlight %s" % self.syntax))
00142         self._code_id = hash_new('sha1', self.raw.encode(config.charset)).hexdigest()
00143         msg = None
00144         if self.filename is not None:
00145             try:
00146                 lexer = pygments.lexers.get_lexer_for_filename(self.filename)
00147             except pygments.util.ClassNotFound:
00148                 fmt.result.append(formatter.text(self.filename))
00149                 lexer = pygments.lexers.TextLexer()
00150         else:
00151             try:
00152                 lexer = pygments.lexers.get_lexer_by_name(self.syntax)
00153             except pygments.util.ClassNotFound:
00154                 f = self.request.formatter
00155                 url = ''.join([
00156                                f.url(1, href=Page(self.request, _("HelpOnParsers")).url(self.request, escape=0)),
00157                                _("HelpOnParsers"),
00158                                f.url(0)])
00159                 msg = _("Syntax highlighting not supported for '%(syntax)s', see %(highlight_help_page)s.") % {"syntax": wikiutil.escape(self.syntax),
00160                                                                                                                "highlight_help_page": url
00161                                                                                                               }
00162                 lexer = pygments.lexers.TextLexer()
00163         fmt.result.append(formatter.code_area(1, self._code_id, self.parsername, self.show_nums, self.num_start, self.num_step, msg))
00164         pygments.highlight(self.raw, lexer, fmt)
00165         fmt.result.append(formatter.code_area(0, self._code_id))
00166         fmt.result.append(formatter.div(0))
00167         self.request.write("".join(fmt.result))
00168