Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 1 | import time |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 2 | import re |
| 3 | import keyword |
| 4 | from Tkinter import * |
| 5 | from Delegator import Delegator |
Jeremy Hylton | 6b3edf0 | 2000-03-07 17:55:32 +0000 | [diff] [blame] | 6 | from IdleConf import idleconf |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 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 | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 12 | DEBUG = 0 |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 13 | |
| 14 | |
| 15 | def any(name, list): |
Walter Dörwald | aaab30e | 2002-09-11 20:36:02 +0000 | [diff] [blame] | 16 | return "(?P<%s>" % name + "|".join(list) + ")" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 17 | |
| 18 | def make_pat(): |
| 19 | kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b" |
| 20 | comment = any("COMMENT", [r"#[^\n]*"]) |
Guido van Rossum | da4b25e | 1999-04-21 15:49:34 +0000 | [diff] [blame] | 21 | sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?" |
| 22 | dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?' |
| 23 | sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" |
| 24 | dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 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) |
Tim Peters | d5d2cd1 | 2001-01-19 10:41:49 +0000 | [diff] [blame] | 30 | asprog = re.compile(r".*?\b(as)\b", re.S) |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 31 | |
| 32 | class ColorDelegator(Delegator): |
| 33 | |
| 34 | def __init__(self): |
| 35 | Delegator.__init__(self) |
| 36 | self.prog = prog |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 37 | self.idprog = idprog |
Tim Peters | d5d2cd1 | 2001-01-19 10:41:49 +0000 | [diff] [blame] | 38 | self.asprog = asprog |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 39 | |
| 40 | def setdelegate(self, delegate): |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 41 | if self.delegate is not None: |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 42 | self.unbind("<<toggle-auto-coloring>>") |
| 43 | Delegator.setdelegate(self, delegate) |
| 44 | if delegate is not None: |
| 45 | self.config_colors() |
| 46 | self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event) |
| 47 | self.notify_range("1.0", "end") |
| 48 | |
| 49 | def config_colors(self): |
| 50 | for tag, cnf in self.tagdefs.items(): |
| 51 | if cnf: |
| 52 | apply(self.tag_configure, (tag,), cnf) |
Guido van Rossum | 6c5baeb | 1998-10-19 02:22:41 +0000 | [diff] [blame] | 53 | self.tag_raise('sel') |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 54 | |
Jeremy Hylton | 6b3edf0 | 2000-03-07 17:55:32 +0000 | [diff] [blame] | 55 | cconf = idleconf.getsection('Colors') |
Guido van Rossum | 7de6975 | 1999-04-20 15:45:30 +0000 | [diff] [blame] | 56 | |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 57 | tagdefs = { |
Jeremy Hylton | e81f28b | 2000-03-03 23:06:45 +0000 | [diff] [blame] | 58 | "COMMENT": cconf.getcolor("comment"), |
| 59 | "KEYWORD": cconf.getcolor("keyword"), |
| 60 | "STRING": cconf.getcolor("string"), |
| 61 | "DEFINITION": cconf.getcolor("definition"), |
| 62 | "SYNC": cconf.getcolor("sync"), |
| 63 | "TODO": cconf.getcolor("todo"), |
| 64 | "BREAK": cconf.getcolor("break"), |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 65 | # The following is used by ReplaceDialog: |
Jeremy Hylton | e81f28b | 2000-03-03 23:06:45 +0000 | [diff] [blame] | 66 | "hit": cconf.getcolor("hit"), |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | def insert(self, index, chars, tags=None): |
| 70 | index = self.index(index) |
| 71 | self.delegate.insert(index, chars, tags) |
| 72 | self.notify_range(index, index + "+%dc" % len(chars)) |
| 73 | |
| 74 | def delete(self, index1, index2=None): |
| 75 | index1 = self.index(index1) |
| 76 | self.delegate.delete(index1, index2) |
| 77 | self.notify_range(index1) |
| 78 | |
| 79 | after_id = None |
| 80 | allow_colorizing = 1 |
| 81 | colorizing = 0 |
| 82 | |
| 83 | def notify_range(self, index1, index2=None): |
| 84 | self.tag_add("TODO", index1, index2) |
| 85 | if self.after_id: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 86 | if DEBUG: print "colorizing already scheduled" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 87 | return |
| 88 | if self.colorizing: |
| 89 | self.stop_colorizing = 1 |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 90 | if DEBUG: print "stop colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 91 | if self.allow_colorizing: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 92 | if DEBUG: print "schedule colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 93 | self.after_id = self.after(1, self.recolorize) |
| 94 | |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 95 | close_when_done = None # Window to be closed when done colorizing |
| 96 | |
| 97 | def close(self, close_when_done=None): |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 98 | if self.after_id: |
| 99 | after_id = self.after_id |
| 100 | self.after_id = None |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 101 | if DEBUG: print "cancel scheduled recolorizer" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 102 | self.after_cancel(after_id) |
| 103 | self.allow_colorizing = 0 |
| 104 | self.stop_colorizing = 1 |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 105 | if close_when_done: |
| 106 | if not self.colorizing: |
| 107 | close_when_done.destroy() |
| 108 | else: |
| 109 | self.close_when_done = close_when_done |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 110 | |
| 111 | def toggle_colorize_event(self, event): |
| 112 | if self.after_id: |
| 113 | after_id = self.after_id |
| 114 | self.after_id = None |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 115 | if DEBUG: print "cancel scheduled recolorizer" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 116 | self.after_cancel(after_id) |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 117 | if self.allow_colorizing and self.colorizing: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 118 | if DEBUG: print "stop colorizing" |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 119 | self.stop_colorizing = 1 |
| 120 | self.allow_colorizing = not self.allow_colorizing |
| 121 | if self.allow_colorizing and not self.colorizing: |
| 122 | self.after_id = self.after(1, self.recolorize) |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 123 | if DEBUG: |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 124 | print "auto colorizing turned", self.allow_colorizing and "on" or "off" |
| 125 | return "break" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 126 | |
| 127 | def recolorize(self): |
| 128 | self.after_id = None |
| 129 | if not self.delegate: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 130 | if DEBUG: print "no delegate" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 131 | return |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 132 | if not self.allow_colorizing: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 133 | if DEBUG: print "auto colorizing is off" |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 134 | return |
| 135 | if self.colorizing: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 136 | if DEBUG: print "already colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 137 | return |
| 138 | try: |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 139 | self.stop_colorizing = 0 |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 140 | self.colorizing = 1 |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 141 | if DEBUG: print "colorizing..." |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 142 | t0 = time.clock() |
| 143 | self.recolorize_main() |
| 144 | t1 = time.clock() |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 145 | if DEBUG: print "%.3f seconds" % (t1-t0) |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 146 | finally: |
| 147 | self.colorizing = 0 |
| 148 | if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"): |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 149 | if DEBUG: print "reschedule colorizing" |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 150 | self.after_id = self.after(1, self.recolorize) |
Guido van Rossum | 5051f4f | 1999-01-12 22:09:57 +0000 | [diff] [blame] | 151 | if self.close_when_done: |
| 152 | top = self.close_when_done |
| 153 | self.close_when_done = None |
| 154 | top.destroy() |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 155 | |
| 156 | def recolorize_main(self): |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 157 | next = "1.0" |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 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 = "" |
Guido van Rossum | 26c2cb4 | 1999-04-26 22:20:38 +0000 | [diff] [blame] | 171 | next = head |
Guido van Rossum | e62e76c | 1999-04-22 13:38:40 +0000 | [diff] [blame] | 172 | lines_to_get = 1 |
Guido van Rossum | 26c2cb4 | 1999-04-26 22:20:38 +0000 | [diff] [blame] | 173 | ok = 0 |
| 174 | while not ok: |
| 175 | mark = next |
Guido van Rossum | e62e76c | 1999-04-22 13:38:40 +0000 | [diff] [blame] | 176 | next = self.index(mark + "+%d lines linestart" % |
| 177 | lines_to_get) |
| 178 | lines_to_get = min(lines_to_get * 2, 100) |
Guido van Rossum | 26c2cb4 | 1999-04-26 22:20:38 +0000 | [diff] [blame] | 179 | ok = "SYNC" in self.tag_names(next + "-1c") |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 180 | line = self.get(mark, next) |
| 181 | ##print head, "get", mark, next, "->", `line` |
| 182 | if not line: |
| 183 | return |
| 184 | for tag in self.tagdefs.keys(): |
| 185 | self.tag_remove(tag, mark, next) |
| 186 | chars = chars + line |
| 187 | m = self.prog.search(chars) |
| 188 | while m: |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 189 | for key, value in m.groupdict().items(): |
| 190 | if value: |
| 191 | a, b = m.span(key) |
| 192 | self.tag_add(key, |
| 193 | head + "+%dc" % a, |
| 194 | head + "+%dc" % b) |
| 195 | if value in ("def", "class"): |
| 196 | m1 = self.idprog.match(chars, b) |
| 197 | if m1: |
| 198 | a, b = m1.span(1) |
| 199 | self.tag_add("DEFINITION", |
| 200 | head + "+%dc" % a, |
| 201 | head + "+%dc" % b) |
Tim Peters | d5d2cd1 | 2001-01-19 10:41:49 +0000 | [diff] [blame] | 202 | elif value == "import": |
| 203 | # color all the "as" words on same line; |
| 204 | # cheap approximation to the truth |
| 205 | while 1: |
| 206 | m1 = self.asprog.match(chars, b) |
| 207 | if not m1: |
| 208 | break |
| 209 | a, b = m1.span(1) |
| 210 | self.tag_add("KEYWORD", |
| 211 | head + "+%dc" % a, |
| 212 | head + "+%dc" % b) |
Guido van Rossum | 26c2cb4 | 1999-04-26 22:20:38 +0000 | [diff] [blame] | 213 | m = self.prog.search(chars, m.end()) |
| 214 | if "SYNC" in self.tag_names(next + "-1c"): |
| 215 | head = next |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 216 | chars = "" |
Guido van Rossum | 26c2cb4 | 1999-04-26 22:20:38 +0000 | [diff] [blame] | 217 | else: |
| 218 | ok = 0 |
| 219 | if not ok: |
| 220 | # We're in an inconsistent state, and the call to |
| 221 | # update may tell us to stop. It may also change |
| 222 | # the correct value for "next" (since this is a |
| 223 | # line.col string, not a true mark). So leave a |
| 224 | # crumb telling the next invocation to resume here |
| 225 | # in case update tells us to leave. |
| 226 | self.tag_add("TODO", next) |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 227 | self.update() |
| 228 | if self.stop_colorizing: |
Guido van Rossum | 3cc1250 | 2001-03-22 17:27:13 +0000 | [diff] [blame] | 229 | if DEBUG: print "colorizing stopped" |
Guido van Rossum | 504b0bf | 1999-01-02 21:28:54 +0000 | [diff] [blame] | 230 | return |
Guido van Rossum | 3b4ca0d | 1998-10-10 18:48:31 +0000 | [diff] [blame] | 231 | |
| 232 | |
| 233 | def main(): |
| 234 | from Percolator import Percolator |
| 235 | root = Tk() |
| 236 | root.wm_protocol("WM_DELETE_WINDOW", root.quit) |
| 237 | text = Text(background="white") |
| 238 | text.pack(expand=1, fill="both") |
| 239 | text.focus_set() |
| 240 | p = Percolator(text) |
| 241 | d = ColorDelegator() |
| 242 | p.insertfilter(d) |
| 243 | root.mainloop() |
| 244 | |
| 245 | if __name__ == "__main__": |
| 246 | main() |