Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 1 | import time |
| 2 | import string |
| 3 | import re |
| 4 | import keyword |
| 5 | from Tkinter import * |
| 6 | from Delegator import Delegator |
| 7 | |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 8 | #$ event <<toggle-auto-coloring>> |
| 9 | #$ win <Control-slash> |
| 10 | #$ unix <Control-slash> |
| 11 | |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 12 | __debug__ = 0 |
| 13 | |
| 14 | |
| 15 | def any(name, list): |
| 16 | return "(?P<%s>" % name + string.join(list, "|") + ")" |
| 17 | |
| 18 | def make_pat(): |
| 19 | kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" |
| 20 | comment = any("COMMENT", [r"#[^\n]*"]) |
| 21 | sqstring = r"(\b[rR])?'([^'\\\n]|\\.)*'?" |
| 22 | dqstring = r'(\b[rR])?"([^"\\\n]|\\.)*"?' |
| 23 | sq3string = r"(\b[rR])?'''([^'\\]|\\.|'(?!''))*(''')?" |
| 24 | dq3string = r'(\b[rR])?"""([^"\\]|\\.|"(?!""))*(""")?' |
| 25 | string = any("STRING", [sq3string, dq3string, sqstring, dqstring]) |
| 26 | return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"]) |
| 27 | |
| 28 | prog = re.compile(make_pat(), re.S) |
| 29 | idprog = re.compile(r"\s+(\w+)", re.S) |
| 30 | |
| 31 | class ColorDelegator(Delegator): |
| 32 | |
| 33 | def __init__(self): |
| 34 | Delegator.__init__(self) |
| 35 | self.prog = prog |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 36 | self.idprog = idprog |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 37 | |
| 38 | def setdelegate(self, delegate): |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 39 | if self.delegate is not None: |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 40 | self.unbind("<<toggle-auto-coloring>>") |
| 41 | Delegator.setdelegate(self, delegate) |
| 42 | if delegate is not None: |
| 43 | self.config_colors() |
| 44 | self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event) |
| 45 | self.notify_range("1.0", "end") |
| 46 | |
| 47 | def config_colors(self): |
| 48 | for tag, cnf in self.tagdefs.items(): |
| 49 | if cnf: |
| 50 | apply(self.tag_configure, (tag,), cnf) |
Guido van Rossum | 6c5baeb | 1998-10-19 02:22:41 +0000 | [diff] [blame] | 51 | self.tag_raise('sel') |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 52 | |
| 53 | tagdefs = { |
| 54 | "COMMENT": {"foreground": "#dd0000"}, |
| 55 | "KEYWORD": {"foreground": "#ff7700"}, |
| 56 | "STRING": {"foreground": "#00aa00"}, |
| 57 | "DEFINITION": {"foreground": "#0000ff"}, |
| 58 | |
| 59 | "SYNC": {}, #{"background": "#ffff00"}, |
| 60 | "TODO": {}, #{"background": "#cccccc"}, |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 61 | |
Guido van Rossum | 6c5baeb | 1998-10-19 02:22:41 +0000 | [diff] [blame] | 62 | "BREAK": {"background": "#FF7777"}, |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 63 | |
| 64 | # The following is used by ReplaceDialog: |
| 65 | "hit": {"foreground": "#FFFFFF", "background": "#000000"}, |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 66 | } |
| 67 | |
| 68 | def insert(self, index, chars, tags=None): |
| 69 | index = self.index(index) |
| 70 | self.delegate.insert(index, chars, tags) |
| 71 | self.notify_range(index, index + "+%dc" % len(chars)) |
| 72 | |
| 73 | def delete(self, index1, index2=None): |
| 74 | index1 = self.index(index1) |
| 75 | self.delegate.delete(index1, index2) |
| 76 | self.notify_range(index1) |
| 77 | |
| 78 | after_id = None |
| 79 | allow_colorizing = 1 |
| 80 | colorizing = 0 |
| 81 | |
| 82 | def notify_range(self, index1, index2=None): |
| 83 | self.tag_add("TODO", index1, index2) |
| 84 | if self.after_id: |
| 85 | if __debug__: print "colorizing already scheduled" |
| 86 | return |
| 87 | if self.colorizing: |
| 88 | self.stop_colorizing = 1 |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 89 | if __debug__: print "stop colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 90 | if self.allow_colorizing: |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 91 | if __debug__: print "schedule colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 92 | self.after_id = self.after(1, self.recolorize) |
| 93 | |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 94 | close_when_done = None # Window to be closed when done colorizing |
| 95 | |
| 96 | def close(self, close_when_done=None): |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 97 | if self.after_id: |
| 98 | after_id = self.after_id |
| 99 | self.after_id = None |
| 100 | if __debug__: print "cancel scheduled recolorizer" |
| 101 | self.after_cancel(after_id) |
| 102 | self.allow_colorizing = 0 |
| 103 | self.stop_colorizing = 1 |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 104 | if close_when_done: |
| 105 | if not self.colorizing: |
| 106 | close_when_done.destroy() |
| 107 | else: |
| 108 | self.close_when_done = close_when_done |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 109 | |
| 110 | def toggle_colorize_event(self, event): |
| 111 | if self.after_id: |
| 112 | after_id = self.after_id |
| 113 | self.after_id = None |
| 114 | if __debug__: print "cancel scheduled recolorizer" |
| 115 | self.after_cancel(after_id) |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 116 | if self.allow_colorizing and self.colorizing: |
| 117 | if __debug__: print "stop colorizing" |
| 118 | self.stop_colorizing = 1 |
| 119 | self.allow_colorizing = not self.allow_colorizing |
| 120 | if self.allow_colorizing and not self.colorizing: |
| 121 | self.after_id = self.after(1, self.recolorize) |
| 122 | if __debug__: |
| 123 | print "auto colorizing turned", self.allow_colorizing and "on" or "off" |
| 124 | return "break" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 125 | |
| 126 | def recolorize(self): |
| 127 | self.after_id = None |
| 128 | if not self.delegate: |
| 129 | if __debug__: print "no delegate" |
| 130 | return |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 131 | if not self.allow_colorizing: |
| 132 | if __debug__: print "auto colorizing is off" |
| 133 | return |
| 134 | if self.colorizing: |
| 135 | if __debug__: print "already colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 136 | return |
| 137 | try: |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 138 | self.stop_colorizing = 0 |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 139 | self.colorizing = 1 |
| 140 | if __debug__: print "colorizing..." |
| 141 | t0 = time.clock() |
| 142 | self.recolorize_main() |
| 143 | t1 = time.clock() |
| 144 | if __debug__: print "%.3f seconds" % (t1-t0) |
| 145 | finally: |
| 146 | self.colorizing = 0 |
| 147 | if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 148 | if __debug__: print "reschedule colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 149 | self.after_id = self.after(1, self.recolorize) |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 150 | if self.close_when_done: |
| 151 | top = self.close_when_done |
| 152 | self.close_when_done = None |
| 153 | top.destroy() |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 154 | |
| 155 | def recolorize_main(self): |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 156 | next = "1.0" |
| 157 | was_ok = is_ok = 0 |
| 158 | while 1: |
| 159 | item = self.tag_nextrange("TODO", next) |
| 160 | if not item: |
| 161 | break |
| 162 | head, tail = item |
| 163 | self.tag_remove("SYNC", head, tail) |
| 164 | item = self.tag_prevrange("SYNC", head) |
| 165 | if item: |
| 166 | head = item[1] |
| 167 | else: |
| 168 | head = "1.0" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 169 | |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 170 | chars = "" |
| 171 | mark = head |
| 172 | is_ok = was_ok = 0 |
| 173 | while not (was_ok and is_ok): |
| 174 | next = self.index(mark + " lineend +1c") |
| 175 | was_ok = "SYNC" in self.tag_names(next + "-1c") |
| 176 | line = self.get(mark, next) |
| 177 | ##print head, "get", mark, next, "->", `line` |
| 178 | if not line: |
| 179 | return |
| 180 | for tag in self.tagdefs.keys(): |
| 181 | self.tag_remove(tag, mark, next) |
| 182 | chars = chars + line |
| 183 | m = self.prog.search(chars) |
| 184 | while m: |
| 185 | i, j = m.span() |
| 186 | for key, value in m.groupdict().items(): |
| 187 | if value: |
| 188 | a, b = m.span(key) |
| 189 | self.tag_add(key, |
| 190 | head + "+%dc" % a, |
| 191 | head + "+%dc" % b) |
| 192 | if value in ("def", "class"): |
| 193 | m1 = self.idprog.match(chars, b) |
| 194 | if m1: |
| 195 | a, b = m1.span(1) |
| 196 | self.tag_add("DEFINITION", |
| 197 | head + "+%dc" % a, |
| 198 | head + "+%dc" % b) |
| 199 | m = self.prog.search(chars, j) |
| 200 | is_ok = "SYNC" in self.tag_names(next + "-1c") |
| 201 | mark = next |
| 202 | if is_ok: |
| 203 | head = mark |
| 204 | chars = "" |
| 205 | self.update() |
| 206 | if self.stop_colorizing: |
| 207 | if __debug__: print "colorizing stopped" |
| 208 | return |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 209 | |
| 210 | |
| 211 | def main(): |
| 212 | from Percolator import Percolator |
| 213 | root = Tk() |
| 214 | root.wm_protocol("WM_DELETE_WINDOW", root.quit) |
| 215 | text = Text(background="white") |
| 216 | text.pack(expand=1, fill="both") |
| 217 | text.focus_set() |
| 218 | p = Percolator(text) |
| 219 | d = ColorDelegator() |
| 220 | p.insertfilter(d) |
| 221 | root.mainloop() |
| 222 | |
| 223 | if __name__ == "__main__": |
| 224 | main() |