blob: 059108f65c91743cbc3eff4f02aee24d60705413 [file] [log] [blame]
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00001import time
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00002import re
3import keyword
4from Tkinter import *
5from Delegator import Delegator
Jeremy Hylton6b3edf02000-03-07 17:55:32 +00006from IdleConf import idleconf
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00007
Guido van Rossum504b0bf1999-01-02 21:28:54 +00008#$ event <<toggle-auto-coloring>>
9#$ win <Control-slash>
10#$ unix <Control-slash>
11
Guido van Rossum3cc12502001-03-22 17:27:13 +000012DEBUG = 0
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000013
14
15def any(name, list):
Walter Dörwaldaaab30e2002-09-11 20:36:02 +000016 return "(?P<%s>" % name + "|".join(list) + ")"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000017
18def make_pat():
19 kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
20 comment = any("COMMENT", [r"#[^\n]*"])
Guido van Rossumda4b25e1999-04-21 15:49:34 +000021 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 Rossum3b4ca0d1998-10-10 18:48:31 +000025 string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
26 return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
27
28prog = re.compile(make_pat(), re.S)
29idprog = re.compile(r"\s+(\w+)", re.S)
Tim Petersd5d2cd12001-01-19 10:41:49 +000030asprog = re.compile(r".*?\b(as)\b", re.S)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000031
32class ColorDelegator(Delegator):
33
34 def __init__(self):
35 Delegator.__init__(self)
36 self.prog = prog
Guido van Rossum504b0bf1999-01-02 21:28:54 +000037 self.idprog = idprog
Tim Petersd5d2cd12001-01-19 10:41:49 +000038 self.asprog = asprog
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000039
40 def setdelegate(self, delegate):
Guido van Rossum504b0bf1999-01-02 21:28:54 +000041 if self.delegate is not None:
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000042 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 Rossum6c5baeb1998-10-19 02:22:41 +000053 self.tag_raise('sel')
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000054
Jeremy Hylton6b3edf02000-03-07 17:55:32 +000055 cconf = idleconf.getsection('Colors')
Guido van Rossum7de69751999-04-20 15:45:30 +000056
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000057 tagdefs = {
Jeremy Hyltone81f28b2000-03-03 23:06:45 +000058 "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 Rossum504b0bf1999-01-02 21:28:54 +000065 # The following is used by ReplaceDialog:
Jeremy Hyltone81f28b2000-03-03 23:06:45 +000066 "hit": cconf.getcolor("hit"),
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000067 }
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 Rossum3cc12502001-03-22 17:27:13 +000086 if DEBUG: print "colorizing already scheduled"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000087 return
88 if self.colorizing:
89 self.stop_colorizing = 1
Guido van Rossum3cc12502001-03-22 17:27:13 +000090 if DEBUG: print "stop colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000091 if self.allow_colorizing:
Guido van Rossum3cc12502001-03-22 17:27:13 +000092 if DEBUG: print "schedule colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000093 self.after_id = self.after(1, self.recolorize)
94
Guido van Rossum5051f4f1999-01-12 22:09:57 +000095 close_when_done = None # Window to be closed when done colorizing
96
97 def close(self, close_when_done=None):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000098 if self.after_id:
99 after_id = self.after_id
100 self.after_id = None
Guido van Rossum3cc12502001-03-22 17:27:13 +0000101 if DEBUG: print "cancel scheduled recolorizer"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000102 self.after_cancel(after_id)
103 self.allow_colorizing = 0
104 self.stop_colorizing = 1
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000105 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 Rossum3b4ca0d1998-10-10 18:48:31 +0000110
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 Rossum3cc12502001-03-22 17:27:13 +0000115 if DEBUG: print "cancel scheduled recolorizer"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000116 self.after_cancel(after_id)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000117 if self.allow_colorizing and self.colorizing:
Guido van Rossum3cc12502001-03-22 17:27:13 +0000118 if DEBUG: print "stop colorizing"
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000119 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 Rossum3cc12502001-03-22 17:27:13 +0000123 if DEBUG:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000124 print "auto colorizing turned", self.allow_colorizing and "on" or "off"
125 return "break"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000126
127 def recolorize(self):
128 self.after_id = None
129 if not self.delegate:
Guido van Rossum3cc12502001-03-22 17:27:13 +0000130 if DEBUG: print "no delegate"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000131 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000132 if not self.allow_colorizing:
Guido van Rossum3cc12502001-03-22 17:27:13 +0000133 if DEBUG: print "auto colorizing is off"
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000134 return
135 if self.colorizing:
Guido van Rossum3cc12502001-03-22 17:27:13 +0000136 if DEBUG: print "already colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000137 return
138 try:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000139 self.stop_colorizing = 0
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000140 self.colorizing = 1
Guido van Rossum3cc12502001-03-22 17:27:13 +0000141 if DEBUG: print "colorizing..."
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000142 t0 = time.clock()
143 self.recolorize_main()
144 t1 = time.clock()
Guido van Rossum3cc12502001-03-22 17:27:13 +0000145 if DEBUG: print "%.3f seconds" % (t1-t0)
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000146 finally:
147 self.colorizing = 0
148 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
Guido van Rossum3cc12502001-03-22 17:27:13 +0000149 if DEBUG: print "reschedule colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000150 self.after_id = self.after(1, self.recolorize)
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000151 if self.close_when_done:
152 top = self.close_when_done
153 self.close_when_done = None
154 top.destroy()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000155
156 def recolorize_main(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000157 next = "1.0"
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000158 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 Rossum3b4ca0d1998-10-10 18:48:31 +0000169
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000170 chars = ""
Guido van Rossum26c2cb41999-04-26 22:20:38 +0000171 next = head
Guido van Rossume62e76c1999-04-22 13:38:40 +0000172 lines_to_get = 1
Guido van Rossum26c2cb41999-04-26 22:20:38 +0000173 ok = 0
174 while not ok:
175 mark = next
Guido van Rossume62e76c1999-04-22 13:38:40 +0000176 next = self.index(mark + "+%d lines linestart" %
177 lines_to_get)
178 lines_to_get = min(lines_to_get * 2, 100)
Guido van Rossum26c2cb41999-04-26 22:20:38 +0000179 ok = "SYNC" in self.tag_names(next + "-1c")
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000180 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 Rossum504b0bf1999-01-02 21:28:54 +0000189 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 Petersd5d2cd12001-01-19 10:41:49 +0000202 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 Rossum26c2cb41999-04-26 22:20:38 +0000213 m = self.prog.search(chars, m.end())
214 if "SYNC" in self.tag_names(next + "-1c"):
215 head = next
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000216 chars = ""
Guido van Rossum26c2cb41999-04-26 22:20:38 +0000217 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 Rossum504b0bf1999-01-02 21:28:54 +0000227 self.update()
228 if self.stop_colorizing:
Guido van Rossum3cc12502001-03-22 17:27:13 +0000229 if DEBUG: print "colorizing stopped"
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000230 return
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000231
232
233def 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
245if __name__ == "__main__":
246 main()