0001 import time 0002 import re 0003 import keyword 0004 import __builtin__ 0005 from Tkinter import * 0006 from Delegator import Delegator 0007 from configHandler import idleConf 0008 0009 DEBUG = False 0010 0011 def any(name, list): 0012 return "(?P<%s>" % name + "|".join(list) + ")" 0013 0014 def make_pat(): 0015 kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" 0016 builtinlist = [str(name) for name in dir(__builtin__) 0017 if not name.startswith('_')] 0018 # self.file = file("file") : 0019 # 1st 'file' colorized normal, 2nd as builtin, 3rd as comment 0020 builtin = r"([^.'\"\\]\b|^)" + any("BUILTIN", builtinlist) + r"\b" 0021 comment = any("COMMENT", [r"#[^\n]*"]) 0022 sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" 0023 dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' 0024 sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" 0025 dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' 0026 string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) 0027 return kw + "|" + builtin + "|" + comment + "|" + string +\ 0028 "|" + any("SYNC", [r"\n"]) 0029 0030 prog = re.compile(make_pat(), re.S) 0031 idprog = re.compile(r"\s+(\w+)", re.S) 0032 asprog = re.compile(r".*?\b(as)\b", re.S) 0033 0034 class ColorDelegator(Delegator): 0035 0036 def __init__(self): 0037 Delegator.__init__(self) 0038 self.prog = prog 0039 self.idprog = idprog 0040 self.asprog = asprog 0041 self.LoadTagDefs() 0042 0043 def setdelegate(self, delegate): 0044 if self.delegate is not None: 0045 self.unbind("<<toggle-auto-coloring>>") 0046 Delegator.setdelegate(self, delegate) 0047 if delegate is not None: 0048 self.config_colors() 0049 self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event) 0050 self.notify_range("1.0", "end") 0051 0052 def config_colors(self): 0053 for tag, cnf in self.tagdefs.items(): 0054 if cnf: 0055 self.tag_configure(tag, **cnf) 0056 self.tag_raise('sel') 0057 0058 def LoadTagDefs(self): 0059 theme = idleConf.GetOption('main','Theme','name') 0060 self.tagdefs = { 0061 "COMMENT": idleConf.GetHighlight(theme, "comment"), 0062 "KEYWORD": idleConf.GetHighlight(theme, "keyword"), 0063 "BUILTIN": idleConf.GetHighlight(theme, "builtin"), 0064 "STRING": idleConf.GetHighlight(theme, "string"), 0065 "DEFINITION": idleConf.GetHighlight(theme, "definition"), 0066 "SYNC": {'background':None,'foreground':None}, 0067 "TODO": {'background':None,'foreground':None}, 0068 "BREAK": idleConf.GetHighlight(theme, "break"), 0069 "ERROR": idleConf.GetHighlight(theme, "error"), 0070 # The following is used by ReplaceDialog: 0071 "hit": idleConf.GetHighlight(theme, "hit"), 0072 } 0073 0074 if DEBUG: print 'tagdefs',self.tagdefs 0075 0076 def insert(self, index, chars, tags=None): 0077 index = self.index(index) 0078 self.delegate.insert(index, chars, tags) 0079 self.notify_range(index, index + "+%dc" % len(chars)) 0080 0081 def delete(self, index1, index2=None): 0082 index1 = self.index(index1) 0083 self.delegate.delete(index1, index2) 0084 self.notify_range(index1) 0085 0086 after_id = None 0087 allow_colorizing = True 0088 colorizing = False 0089 0090 def notify_range(self, index1, index2=None): 0091 self.tag_add("TODO", index1, index2) 0092 if self.after_id: 0093 if DEBUG: print "colorizing already scheduled" 0094 return 0095 if self.colorizing: 0096 self.stop_colorizing = True 0097 if DEBUG: print "stop colorizing" 0098 if self.allow_colorizing: 0099 if DEBUG: print "schedule colorizing" 0100 self.after_id = self.after(1, self.recolorize) 0101 0102 close_when_done = None # Window to be closed when done colorizing 0103 0104 def close(self, close_when_done=None): 0105 if self.after_id: 0106 after_id = self.after_id 0107 self.after_id = None 0108 if DEBUG: print "cancel scheduled recolorizer" 0109 self.after_cancel(after_id) 0110 self.allow_colorizing = False 0111 self.stop_colorizing = True 0112 if close_when_done: 0113 if not self.colorizing: 0114 close_when_done.destroy() 0115 else: 0116 self.close_when_done = close_when_done 0117 0118 def toggle_colorize_event(self, event): 0119 if self.after_id: 0120 after_id = self.after_id 0121 self.after_id = None 0122 if DEBUG: print "cancel scheduled recolorizer" 0123 self.after_cancel(after_id) 0124 if self.allow_colorizing and self.colorizing: 0125 if DEBUG: print "stop colorizing" 0126 self.stop_colorizing = True 0127 self.allow_colorizing = not self.allow_colorizing 0128 if self.allow_colorizing and not self.colorizing: 0129 self.after_id = self.after(1, self.recolorize) 0130 if DEBUG: 0131 print "auto colorizing turned",\ 0132 self.allow_colorizing and "on" or "off" 0133 return "break" 0134 0135 def recolorize(self): 0136 self.after_id = None 0137 if not self.delegate: 0138 if DEBUG: print "no delegate" 0139 return 0140 if not self.allow_colorizing: 0141 if DEBUG: print "auto colorizing is off" 0142 return 0143 if self.colorizing: 0144 if DEBUG: print "already colorizing" 0145 return 0146 try: 0147 self.stop_colorizing = False 0148 self.colorizing = True 0149 if DEBUG: print "colorizing..." 0150 t0 = time.clock() 0151 self.recolorize_main() 0152 t1 = time.clock() 0153 if DEBUG: print "%.3f seconds" % (t1-t0) 0154 finally: 0155 self.colorizing = False 0156 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): 0157 if DEBUG: print "reschedule colorizing" 0158 self.after_id = self.after(1, self.recolorize) 0159 if self.close_when_done: 0160 top = self.close_when_done 0161 self.close_when_done = None 0162 top.destroy() 0163 0164 def recolorize_main(self): 0165 next = "1.0" 0166 while True: 0167 item = self.tag_nextrange("TODO", next) 0168 if not item: 0169 break 0170 head, tail = item 0171 self.tag_remove("SYNC", head, tail) 0172 item = self.tag_prevrange("SYNC", head) 0173 if item: 0174 head = item[1] 0175 else: 0176 head = "1.0" 0177 0178 chars = "" 0179 next = head 0180 lines_to_get = 1 0181 ok = False 0182 while not ok: 0183 mark = next 0184 next = self.index(mark + "+%d lines linestart" % 0185 lines_to_get) 0186 lines_to_get = min(lines_to_get * 2, 100) 0187 ok = "SYNC" in self.tag_names(next + "-1c") 0188 line = self.get(mark, next) 0189 ##print head, "get", mark, next, "->", repr(line) 0190 if not line: 0191 return 0192 for tag in self.tagdefs.keys(): 0193 self.tag_remove(tag, mark, next) 0194 chars = chars + line 0195 m = self.prog.search(chars) 0196 while m: 0197 for key, value in m.groupdict().items(): 0198 if value: 0199 a, b = m.span(key) 0200 self.tag_add(key, 0201 head + "+%dc" % a, 0202 head + "+%dc" % b) 0203 if value in ("def", "class"): 0204 m1 = self.idprog.match(chars, b) 0205 if m1: 0206 a, b = m1.span(1) 0207 self.tag_add("DEFINITION", 0208 head + "+%dc" % a, 0209 head + "+%dc" % b) 0210 elif value == "import": 0211 # color all the "as" words on same line; 0212 # cheap approximation to the truth 0213 while True: 0214 m1 = self.asprog.match(chars, b) 0215 if not m1: 0216 break 0217 a, b = m1.span(1) 0218 self.tag_add("KEYWORD", 0219 head + "+%dc" % a, 0220 head + "+%dc" % b) 0221 m = self.prog.search(chars, m.end()) 0222 if "SYNC" in self.tag_names(next + "-1c"): 0223 head = next 0224 chars = "" 0225 else: 0226 ok = False 0227 if not ok: 0228 # We're in an inconsistent state, and the call to 0229 # update may tell us to stop. It may also change 0230 # the correct value for "next" (since this is a 0231 # line.col string, not a true mark). So leave a 0232 # crumb telling the next invocation to resume here 0233 # in case update tells us to leave. 0234 self.tag_add("TODO", next) 0235 self.update() 0236 if self.stop_colorizing: 0237 if DEBUG: print "colorizing stopped" 0238 return 0239 0240 0241 def main(): 0242 from Percolator import Percolator 0243 root = Tk() 0244 root.wm_protocol("WM_DELETE_WINDOW", root.quit) 0245 text = Text(background="white") 0246 text.pack(expand=1, fill="both") 0247 text.focus_set() 0248 p = Percolator(text) 0249 d = ColorDelegator() 0250 p.insertfilter(d) 0251 root.mainloop() 0252 0253 if __name__ == "__main__": 0254 main() 0255
Generated by PyXR 0.9.4