From 343dc115d9405d31f2d4d34f65c94b7efd108344 Mon Sep 17 00:00:00 2001 From: Skyler Lehmkuhl Date: Wed, 9 Jan 2013 23:44:01 -0500 Subject: [PATCH] Syntax highlighting? --- codeeditor.py | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++ svlgui.py | 6 +- 2 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 codeeditor.py diff --git a/codeeditor.py b/codeeditor.py new file mode 100644 index 0000000..f2a5830 --- /dev/null +++ b/codeeditor.py @@ -0,0 +1,254 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +from GUI import Application, ScrollableView, Window, Font, Colors +from pygments import highlight +from pygments.lexers import ActionScriptLexer +from pygments.formatter import Formatter +from pygments.filters import NameHighlightFilter +from pygments.token import Token + +def hex2rgb(hex): + a=hex[1] + b=hex[2] + c=hex[3] + if len(hex)==7: + d=hex[4] + e=hex[5] + f=hex[6] + ab = a+b + cd = c+d + ef = e+f + return (int(ab, 16)/256.0, int(cd, 16)/256.0, int(ef, 16)/256.0) + elif len(hex)==9: + d=hex[4] + e=hex[5] + f=hex[6] + g=hex[7] + h=hex[8] + ab = a+b + cd = c+d + ef = e+f + gh = g+h + return (int(ab, 16)/256.0, int(cd, 16)/256.0, int(ef, 16)/256.0, int(gh, 16)/256.0) + + else: + return (int(a, 16)/16.0, int(b, 16)/16.0, int(c, 16)/16.0) + +class EverythingHighlightFilter(NameHighlightFilter): + def __init__(self, names, tokentype, names2, tokentype2, names3, tokentype3): + NameHighlightFilter.__init__(self, names=names, tokentype=tokentype) + self.names2 = names2 + self.tokentype2 = tokentype2 + self.names3 = names3 + self.tokentype3 = tokentype3 + def filter(self, lexer, stream): + define = False + for ttype, value in stream: + if value in self.names: + ttype = self.tokentype + elif value in self.names2: + ttype = self.tokentype2 + elif value in self.names3: + ttype = self.tokentype3 + yield ttype, value + +class PyGUIFormatter(Formatter): + def __init__(self, **options): + Formatter.__init__(self, **options) + self.styles = {} + for token, style in self.style: + self.styles[token] = {} + if style['color']: + self.styles[token]['color'] = Colors.rgb(*hex2rgb(style['color'])) + + def format(self, tokensource, outfile): + for ttype, value in tokensource: + self.lines = 0 + self.tabs = 0 + self.cr.gsave() + if 'color' in self.styles[ttype]: + self.cr.textcolor = self.styles[ttype]['color'] + else: + if ttype==Token.Text: + self.lines+=value.count('\n') + self.tabs+=value.count('\t') + self.cr.show_text(value) + self.cr.stroke() + self.cr.grestore() + if self.lines: + self.cr.rmoveto(-self.cr.current_point[0],self.lines*self.height) + if self.tabs: + self.cr.rmoveto(self.tabwidth*self.tabs, 0) +class CodeEditor(ScrollableView): + def __init__(self): + ScrollableView.__init__(self) + self.text = "var a = {b:5, c:[3, 'df \\'']};\n_xscale\nif (this.hitTest(_root._xmouse, root._ymouse, false)) {\n\n\ttrace('hi');\n}" + self.font = Font('Courier', 16) + self.selecting = False + self.lexer = ActionScriptLexer() + self.cursorpos = 0 + self.scursorpos = 0 + self.formatter = PyGUIFormatter() + # self.filter = NameHighlightFilter( + self.filter = EverythingHighlightFilter( + names=['trace'], + tokentype=Token.Keyword, + names2=['_root', '_global'], + tokentype2=Token.Name.Builtin, + names3=['_alpha', 'blendMode', 'cacheAsBitmap', '_currentframe', '_droptarget', 'enabled', 'filters', + 'focusEnabled', '_focusRect', 'forceSmoothing', '_framesloaded', '_height', '_highquality', + 'hitArea', '_lockroot', 'menu', 'opaqueBackground', '_parent', '_quality', '_rotation', 'scale9Grid', + 'scrollRect', '_soundbuftime', 'tabChildren', 'tabEnabled', 'tabIndex', '_target', 'totalframes', + 'trackAsMenu', 'transform', '_url', 'useHandCursor', '_visible', '_width', '_x', '_xmouse', + '_xscale', '_y', '_ymouse', '_yscale'], + tokentype3=Token.Name.Variable.Class + ) + self.extent = (self.width, self.height) + # Not working - no idea why - disabled to add speed + self.lexer.add_filter(self.filter) + def container_resized(self, event): + self.extent = (self.width, self.height) + ScrollableView.container_resized(self, event) + def draw(self, cr, update_rect): + cr.fillcolor = Colors.rgb(1,1,1) + cr.fill_rect(update_rect) + d = self.font.descent + h = self.font.height + w = self.font.width(' ') + cr.font = self.font + if '\n' in self.text[:self.scursorpos]: + scw = self.font.width(self.text[self.text.rindex('\n',0,self.scursorpos):self.scursorpos]) + else: + scw = self.font.width(self.text[:self.scursorpos]) + if '\n' in self.text[:self.cursorpos]: + cw = self.font.width(self.text[self.text.rindex('\n',0,self.cursorpos):self.cursorpos]) + else: + cw = self.font.width(self.text[:self.cursorpos]) + selines = self.text[:self.scursorpos].count('\n')+1 + elines = self.text[:self.cursorpos].count('\n')+1 + if self.selecting: + cr.fillcolor = Colors.rgb(0.5,0.75,1) + cr.moveto(scw,d+h*selines) + cr.lineto(scw,-h+h*selines) + if selines!=elines: + cr.lineto(self.extent[0],-h+h*selines) + cr.lineto(self.extent[0],-h+h*elines) + cr.lineto(cw,-h+h*elines) + cr.lineto(cw,d+h*elines) + if selines != elines: + cr.lineto(0,d+h*elines) + cr.lineto(0,d+h*selines) + cr.fill() + # cr.fill_rect([scw,d+h*elines,cw,-h+h*elines]) + cr.newpath() + cr.moveto(0,self.font.height) + self.formatter.cr = cr + self.formatter.height = self.font.height + self.formatter.tabwidth = self.font.width('\t') + highlight(self.text, self.lexer, self.formatter) + cr.newpath() + cr.moveto(cw,d+h*elines) + cr.lineto(cw,-h+h*elines) + cr.stroke() + # cr.show_text(self.text) + # cr.stroke() + def mouse_down(self, event): + self.become_target() + x, y = event.position + self.selecting = False + # self.cursorpos = self.text.replace('\n',' ',int(y/self.font.height)-1).find('\n') + # if self.cursorpos==-1: + # self.cursorpos = len(self.text)-1 + # self.cursorpos+=1 + # self.cursorpos = min(self.cursorpos+int(x/self.font.width(' ')), max(self.text.find('\n',self.cursorpos+1),0) or len(self.text)) + lns = self.text.splitlines() + self.cursorpos = sum([len(i) for i in lns[:int(y/self.font.height)]]) + try: + self.cursorpos += min(int(x/self.font.width(' '))+1,lns[int(y/self.font.height)]) + except: + pass + self.scursorpos = self.cursorpos + if int(y/self.font.height): + self.cursorpos+=1 + self.invalidate_rect([0,0,self.extent[0],self.extent[1]]) + def mouse_drag(self, event): + x, y = event.position + self.selecting = True + lns = self.text.splitlines() + self.cursorpos = sum([len(i) for i in lns[:int(y/self.font.height)]]) + try: + self.cursorpos += min(int(x/self.font.width(' '))+1,lns[int(y/self.font.height)]) + except: + pass + if int(y/self.font.height): + self.cursorpos+=1 + self.invalidate_rect([0,0,self.extent[0],self.extent[1]]) + def key_down(self, event): + keydict = {127:"backspace",63272:"delete",63232:"up_arrow",63233:"down_arrow", + 63235:"right_arrow",63234:"left_arrow",13:"enter",9:"tab", + 63236:"F1",63237:"F2",63238:"F3",63239:"F4",63240:"F5", + 63241:"F6",63242:"F7",63243:"F8",27:"escape"} + if not event.unichars=='': + if ord(event.unichars) in keydict: + key = keydict[ord(event.unichars)] + else: + key = event.unichars + else: + key = event.key.upper() + if key == "\b": + if self.cursorpos>0: + self.text = self.text[:self.cursorpos-1]+self.text[self.cursorpos:] + elif key == "enter": + self.text = self.text[:self.cursorpos]+"\n"+self.text[self.cursorpos:] + self.height = self.font.height*(self.text.count('\n')+1) + self.cursorpos += 1 + elif key == "backspace": + if self.cursorpos>0: + self.text = self.text[:self.cursorpos-1]+self.text[self.cursorpos:] + self.cursorpos -= 1 + elif key == "delete": + if self.cursorpos0: + self.cursorpos -= 1 + elif key == "right_arrow": + if self.cursorpos