tadhg.com
tadhg.com
 

Python Syntax Highlighting for star-light

23:37 Fri 26 Jun 2009
[, , , , ]

A couple of years ago I plugged star-light, a syntax highlighter that’s entirely client-side. I’ve been happy with it, but wanted a Python mode for it. I was going to post some other code this evening, and then decided that I should just make the Python mode myself.

This led to fun with regular expressions.

I probably shouldn’t call it “fun”, though. In any case, I eventually got something that works relatively well:


@some_decorator()
def hello_world(arg, **kw):
    import pdb;
    if 1==1:
        print "Hello World"
        some_func('foo')
        some_other_func(list("string"))
    else:
        """
            No, we should never get to this condition.
            "grocer's quotes"
            Padding text.
        """
        pass
    return dict(message="Hello World printed")

I spent a lot longer on it than I would have liked, primarily due to the regular expression for multi-line comments:


var PYBLOCK_COMMENT1 = new RegExp(
    '"{3}'                  +   // opening triplet
    '('                     +   // open paren for main optional expression
    '([^"]*)'               +   // match not-" 0-inf times
    '("{1,2}[^"]+"{1,2})*'  +   // match "x1 followed by one or more not-"
                                // followed by "x1, all 0-inf times
    '([^"]*)'               +   // match not-" 0-inf times
    ')*'                    +   // close paren, make main expression optional
    '"{3}'                  +   // closing triplet
'');

If by some chance you’re using star-light, it’s relatively easy to add this mode. First, add this line to bindings.xml


<binding id="star-python.htc|star-light.htc" extends="#behavior"/>

Then add this line to star-light.css:


pre.python {
	behavior: url(star-python.htc) url(star-light.htc);
	-moz-binding: url(bindings.xml#star-python.htc|star-light.htc);
}

Finally, get this code and save it alongside the other modes as star-python.htc:


<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
    star-light - version 1.0.2 (2005/06/06)
    Copyright 2005, Dean Edwards
    Python mode Copyright 2009, Tadhg O'Higgins
	License: http://creativecommons.org/licenses/LGPL/2.1/
-->
<public:component xmlns:public="urn:HTMLComponent" lightweight="true">
<public:attach event="ondocumentready" handler="init"/>
<script type="text/javascript">
//<![CDATA[

var PUNCTUATION = new RegExp("!=|[\-+*|\~^/%=<>\\]\[{}\\(\),.:]")

var BUILTIN =   "__import__|abs|apply|basestring|bool|buffer|" +
                "callable|chr|classmethod|cmp|coerce|compile|complex|" +
                "delattr|dict|dir|divmod|" +
                "enumerate|eval|execfile|file|filter|float|" +
                "getattr|globals|hasattr|hash|help|hex|" +
                "id|input|int|intern|isinstance|issubclass|iter|" +
                "len|list|locals|long|" +
                "map|max|min|" +
                "object|oct|open|ord|pow|property|" +
                "range|raw_input|reduce|reload|repr|round|" +
                "setattr|slice|staticmethod|sum|super|str|tuple|type|" +
                "unichr|unicode|vars|" +
                "xrange|" +
                "zip";

var RESERVED =  "ArithmeticError|AssertionError|AttributeError|" +
                "DeprecationWarning|" +
                "EOFError|Ellipsis|EnvironmentError|Exception|False|" +
                "FloatingPointError|FutureWarning|" +
                "IOError|ImportError|IndentationError|IndexError|" +
                "KeyError|KeyboardInterrupt|LookupError|" +
                "MemoryError|NameError|None|NotImplemented|" +
                "NotImplementedError|" +
                "OSError|OverflowError|OverflowWarning|" + 
                "PendingDeprecationWarning|" +
                "ReferenceError|RuntimeError|RuntimeWarning|" +
                "StandardError|StopIteration|SyntaxError|SyntaxWarning|" +
                "SystemError|SystemExit|TabError|True|TypeError|" +
                "UnboundLocalError|UnicodeDecodeError|UnicodeEncodeError|" + 
                "UnicodeError|UnicodeTranslateError|UserWarning|ValueError|" +
                "Warning|WindowsError|" +
                "ZeroDivisionError|" +
                "and|assert|break|" +
                "class|continue|def|del|" +
                "elif|else|except|exec|finally|for|from|" +
                "global|" +
                "if|import|in|is|" +
                "lambda|" +
                "not|" +
                "or|pass|print|" +
                "raise|return|" +
                "try|" +
                "while|" +
                "yield";

var PYLINE_COMMENT = /#[^\n]*\n/

var PYBLOCK_COMMENT1 = new RegExp(
    '"{3}'                  +   // opening triplet
    '('                     +   // open paren for main optional expression
    '([^"]*)'               +   // match not-" 0-inf times
    '("{1,2}[^"]+"{1,2})*'  +   // match "x1 followed by one or more not-"
                                // followed by "x1, all 0-inf times
    '([^"]*)'               +   // match not-" 0-inf times
    ')*'                    +   // close paren, make main expression optional
    '"{3}'                  +   // closing triplet
'');

var PYBLOCK_COMMENT2 = new RegExp(
    "'{3}"                  +   // opening triplet
    "("                     +   // open paren for main optional expression
    "([^']*)"               +   // match not-' 0-inf times
    "('{1,2}[^']+'{1,2})*"  +   // match 'x1 followed by one or more not-'
                                // followed by 'x1, all 0-inf times
    "([^']*)"               +   // match not-' 0-inf times
    ")*"                    +   // close paren, make main expression optional
    "'{3}"                  +   // closing triplet
"");    
    
var DECORATOR = new RegExp(
    /\@[^\(]+/
);

function init() {
	// default text colour
	style.color = "black";

	// escape character
	parser.escapeChar = "\\";

	// comments
	parser.add(PYLINE_COMMENT, "color:green");
	parser.add(PYBLOCK_COMMENT1, "color:green; font-weight:bold;");
    parser.add(PYBLOCK_COMMENT2, "color:green; font-weight:bold;");
    
    //python decorators
	parser.add(DECORATOR, "color:orange;font-weight:bold;");

	// regular expressions
	parser.add(/([^\w\$\/'"*)])(\/[^\/\n\r\*][^\/\n\r]*\/g?i?)/, "color:maroon", "$2<span>$3</span>");

	// strings
	parser.add(STRING1, "color:maroon");
	parser.add(STRING2, "color:maroon");

	// numbers
	parser.add(NUMBER, "color:maroon");

	// urls/email
	urls = true;
	email = true;

	tabStop = 4;

	// python builtins
	parser.add(BUILTIN, "color:teal; font-weight:bold;");
    
    // python reserved words
	parser.add(RESERVED, "color:blue; font-weight:bold");
    
    // python punctuation:
    parser.add(PUNCTUATION, "color:red;font-weight:bold");
    
    // other:
	parser.add(/\w+/, "color:black");
    
};

//]]>
</script>
</public:component>

Leave a Reply