#!/bin/env python
# A simple editor that can colourize Python code (Ka-Ping Yee, 8 April 1997)

import sys, StringIO
from keyword import iskeyword
from Tkinter import *
from tokenize import tokenize, TokenError
from tokenize import NAME, NUMBER, STRING, COMMENT, OP, ERRORTOKEN
KEYWORD, DOC = 1001, 1002

class Editor(Frame):
    def __init__(self, root=None, data=None):
        Frame.__init__(self, root)
        self.pack(side=TOP, expand=YES, fill=BOTH)

        self.scroll = Scrollbar(self)
        self.text = Text(self, wrap=WORD, yscrollcommand=self.scroll.set)
        self.scroll.config(command=self.text.yview)

        self.button = Button(self, text="Colourize", command=self.colourize)
        self.button.pack(side=BOTTOM)
        self.scroll.pack(side=RIGHT, fill=Y)
        self.text.pack(side=LEFT, expand=Y, fill=BOTH)

        if data: self.text.insert(END, data)

        self.tagcolour = {ERRORTOKEN: 'red', STRING: 'maroon',
            DOC: 'grey50', KEYWORD: 'black', NAME: 'dark blue',
            COMMENT: 'purple2', NUMBER: 'salmon3', OP: 'green4'}

    def colourize(self):
        textfile = StringIO.StringIO(self.text.get('1.0', END))

        args = [''] * (DOC + 1)
        for tag in self.tagcolour.keys():
            args[tag] = [self.text._w, "tag", "add", tag]

        def tokeneater(type, token, start, end, line,
            havetag=self.tagcolour.has_key, args=args):
            if type == NAME and iskeyword(token): type = KEYWORD
            elif type == STRING and token[:3] in ("'''", '"""'): type = DOC
            if havetag(type):
                startpos = str(start[0]) + '.' + str(start[1])
                endpos = startpos + ' + %d c' % (len(token))
                args[type].append(startpos)
                args[type].append(endpos)

        try: tokenize(textfile.readline, tokeneater)
        except TokenError, (message, start):
            args[ERRORTOKEN].append('%d.%d' % start)
            args[ERRORTOKEN].append('end')

        for tag, colour in self.tagcolour.items():
            self.text.tag_delete(tag)
            self.text.tag_configure(tag, foreground=colour)
            if len(args[tag]) > 4: apply(self.tk.call, tuple(args[tag]))

if len(sys.argv) > 1: text = open(sys.argv[1]).read()
else: text = None
Editor(data=text).mainloop()
