#!/usr/bin/env python # -*- coding: utf-8 -*- """ | This file is part of the web2py Web Framework | Copyrighted by Massimo Di Pierro | License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html) """ import re from pydal._compat import xrange from yatl.sanitizer import xmlescape __all__ = ["highlight"] class all_styles(object): """ Custom non-data descriptor for lazy initialization of Highlighter.all_styles class attribute. see: https://docs.python.org/2/reference/datamodel.html#implementing-descriptors or https://docs.python.org/3/reference/datamodel.html#implementing-descriptors """ def __get__(self, instance, owner): val = _get_all_styles(owner) setattr(owner, "all_styles", val) return val class Highlighter(object): """Does syntax highlighting.""" def __init__( self, mode, link=None, styles=None, ): """ Initialize highlighter: mode = language (PYTHON, WEB2PY, C, CPP, HTML, HTML_PLAIN) """ styles = styles or {} mode = mode.upper() if link and link[-1] != "/": link = link + "/" self.link = link self.styles = styles self.output = [] self.span_style = None if mode == "WEB2PY": (mode, self.suppress_tokens) = ("PYTHON", []) elif mode == "PYTHON": self.suppress_tokens = ["GOTOHTML"] elif mode == "CPP": (mode, self.suppress_tokens) = ("C", []) elif mode == "C": self.suppress_tokens = ["CPPKEYWORD"] elif mode == "HTML_PLAIN": (mode, self.suppress_tokens) = ("HTML", ["GOTOPYTHON"]) elif mode == "HTML": self.suppress_tokens = [] else: raise SyntaxError("Unknown mode: %s" % mode) self.mode = mode def c_tokenizer( self, token, match, style, ): """ Callback for C specific highlighting. """ value = xmlescape(match.group(), quote=False) self.change_style(token, style) self.output.append(value) def python_tokenizer( self, token, match, style, ): """ Callback for python specific highlighting. """ value = xmlescape(match.group(), quote=False) if token == "MULTILINESTRING": self.change_style(token, style) self.output.append(value) self.strMultilineString = match.group(1) return "PYTHONMultilineString" elif token == "ENDMULTILINESTRING": if match.group(1) == self.strMultilineString: self.output.append(value) self.strMultilineString = "" return "PYTHON" if style and style[:5] == "link:": self.change_style(None, None) (url, style) = style[5:].split(";", 1) if url == "None" or url == "": self.output.append('%s' % (style, value)) else: self.output.append( '%s' % (url, value, style, value) ) else: self.change_style(token, style) self.output.append(value) if token == "GOTOHTML": return "HTML" return None def html_tokenizer( self, token, match, style, ): """ Callback for HTML specific highlighting. """ value = xmlescape(match.group(), quote=False) self.change_style(token, style) self.output.append(value) if token == "GOTOPYTHON": return "PYTHON" return None all_styles = all_styles() def highlight(self, data): """ Syntax highlight some python code. Returns html version of code. """ i = 0 mode = self.mode while i < len(data): for token, o_re, style in Highlighter.all_styles[mode][1]: if token not in self.suppress_tokens: match = o_re.match(data, i) if match: if style: new_mode = Highlighter.all_styles[mode][0]( self, token, match, style % dict(link=self.link) ) else: new_mode = Highlighter.all_styles[mode][0]( self, token, match, style ) if new_mode is not None: mode = new_mode i += max(1, len(match.group())) break else: self.change_style(None, None) self.output.append(data[i]) i += 1 self.change_style(None, None) return "".join(self.output).expandtabs(4) def change_style(self, token, style): """ Generate output to change from existing style to another style only. """ if token in self.styles: style = self.styles[token] if self.span_style != style: if style != "Keep": if self.span_style is not None: self.output.append("") if style is not None: self.output.append('' % style) self.span_style = style def _get_all_styles(cls): return { "C": ( cls.c_tokenizer, ( ( "COMMENT", re.compile(r"//.*\r?\n"), "color: green; font-style: italic", ), ( "MULTILINECOMMENT", re.compile(r"/\*.*?\*/", re.DOTALL), "color: green; font-style: italic", ), ( "PREPROCESSOR", re.compile(r"\s*#.*?[^\\]\s*\n", re.DOTALL), "color: magenta; font-style: italic", ), ( "PUNC", re.compile(r"[-+*!&|^~/%\=<>\[\]{}(),.:]"), "font-weight: bold", ), ( "NUMBER", re.compile(r"0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+"), "color: red", ), ( "KEYWORD", re.compile( r"(sizeof|int|long|short|char|void|" + r"signed|unsigned|float|double|" + r"goto|break|return|continue|asm|" + r"case|default|if|else|switch|while|for|do|" + r"struct|union|enum|typedef|" + r"static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])" ), "color:#185369; font-weight: bold", ), ( "CPPKEYWORD", re.compile( r"(class|private|protected|public|template|new|delete|" + r"this|friend|using|inline|export|bool|throw|try|catch|" + r"operator|typeid|virtual)(?![a-zA-Z0-9_])" ), "color: blue; font-weight: bold", ), ( "STRING", re.compile(r'r?u?\'(.*?)(?\[\]{}(),.:]"), "font-weight: bold", ), ( "NUMBER", re.compile(r"0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+"), "color: red", ), ( "KEYWORD", re.compile( r"(def|class|break|continue|del|exec|finally|pass|" + r"print|raise|return|try|except|global|assert|lambda|" + r"yield|for|while|if|elif|else|and|in|is|not|or|import|" + r"from|True|False)(?![a-zA-Z0-9_])" ), "color:#185369; font-weight: bold", ), ( "WEB2PY", re.compile( r"(request|response|session|cache|redirect|local_import|HTTP|TR|XML|URL|BEAUTIFY|A|BODY|BR|B|CAT|CENTER|CODE|COL|COLGROUP|DIV|EM|EMBED|FIELDSET|LEGEND|FORM|H1|H2|H3|H4|H5|H6|IFRAME|HEAD|HR|HTML|I|IMG|INPUT|LABEL|LI|LINK|MARKMIN|MENU|META|OBJECT|OL|ON|OPTION|P|PRE|SCRIPT|SELECT|SPAN|STYLE|TABLE|THEAD|TBODY|TFOOT|TAG|TD|TEXTAREA|TH|TITLE|TT|T|UL|XHTML|IS_SLUG|IS_STRONG|IS_LOWER|IS_UPPER|IS_ALPHANUMERIC|IS_DATETIME|IS_DATETIME_IN_RANGE|IS_DATE|IS_DATE_IN_RANGE|IS_DECIMAL_IN_RANGE|IS_EMAIL|IS_EXPR|IS_FILE|IS_FLOAT_IN_RANGE|IS_IMAGE|IS_INT_IN_RANGE|IS_IN_SET|IS_IPV4|IS_LIST_OF|IS_LENGTH|IS_MATCH|IS_EQUAL_TO|IS_EMPTY_OR|IS_NULL_OR|IS_NOT_EMPTY|IS_TIME|IS_UPLOAD_FILENAME|IS_URL|CLEANUP|CRYPT|IS_IN_DB|IS_NOT_IN_DB|DAL|Field|SQLFORM|SQLTABLE|xmlescape|embed64)(?![a-zA-Z0-9_])" ), "link:%(link)s;text-decoration:None;color:#FF5C1F;", ), ("MAGIC", re.compile(r"self|None"), "color:#185369; font-weight: bold"), ("MULTILINESTRING", re.compile(r'r?u?(\'\'\'|""")'), "color: #FF9966"), ( "STRING", re.compile(r'r?u?\'(.*?)(?]*-->|"), "color: green; font-style: italic", ), ("XMLCRAP", re.compile(r"]*>"), "color: blue; font-style: italic"), ( "SCRIPT", re.compile(r"