blob: 250ca94905e09e5f27220a436ef5427f66dd1830 [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001import time
David Scherer7aced172000-08-15 01:13:23 +00002import re
3import keyword
Kurt B. Kaiser73360a32004-03-08 18:15:31 +00004import __builtin__
David Scherer7aced172000-08-15 01:13:23 +00005from Tkinter import *
6from Delegator import Delegator
Steven M. Gavae16d94b2001-11-03 05:07:28 +00007from configHandler import idleConf
David Scherer7aced172000-08-15 01:13:23 +00008
9#$ event <<toggle-auto-coloring>>
10#$ win <Control-slash>
11#$ unix <Control-slash>
12
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000013DEBUG = 0
David Scherer7aced172000-08-15 01:13:23 +000014
15
16def any(name, list):
Kurt B. Kaisera2876442002-09-15 22:09:16 +000017 return "(?P<%s>" % name + "|".join(list) + ")"
David Scherer7aced172000-08-15 01:13:23 +000018
19def make_pat():
20 kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000021 builtinlist = [str(name) for name in dir(__builtin__)
22 if not name.startswith('_')]
23 builtin = r"([^\\.]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
David Scherer7aced172000-08-15 01:13:23 +000024 comment = any("COMMENT", [r"#[^\n]*"])
25 sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
26 dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
27 sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
28 dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
29 string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000030 return kw + "|" + builtin + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
David Scherer7aced172000-08-15 01:13:23 +000031
32prog = re.compile(make_pat(), re.S)
33idprog = re.compile(r"\s+(\w+)", re.S)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000034asprog = re.compile(r".*?\b(as)\b", re.S)
David Scherer7aced172000-08-15 01:13:23 +000035
36class ColorDelegator(Delegator):
37
38 def __init__(self):
39 Delegator.__init__(self)
40 self.prog = prog
41 self.idprog = idprog
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000042 self.asprog = asprog
Steven M. Gavab77d3432002-03-02 07:16:21 +000043 self.LoadTagDefs()
David Scherer7aced172000-08-15 01:13:23 +000044
45 def setdelegate(self, delegate):
46 if self.delegate is not None:
47 self.unbind("<<toggle-auto-coloring>>")
48 Delegator.setdelegate(self, delegate)
49 if delegate is not None:
50 self.config_colors()
51 self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
52 self.notify_range("1.0", "end")
53
54 def config_colors(self):
55 for tag, cnf in self.tagdefs.items():
56 if cnf:
Raymond Hettinger931237e2003-07-09 18:48:24 +000057 self.tag_configure(tag, **cnf)
David Scherer7aced172000-08-15 01:13:23 +000058 self.tag_raise('sel')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000059
Steven M. Gavab77d3432002-03-02 07:16:21 +000060 def LoadTagDefs(self):
61 theme = idleConf.GetOption('main','Theme','name')
62 self.tagdefs = {
63 "COMMENT": idleConf.GetHighlight(theme, "comment"),
64 "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000065 "BUILTIN": idleConf.GetHighlight(theme, "builtin"),
Steven M. Gavab77d3432002-03-02 07:16:21 +000066 "STRING": idleConf.GetHighlight(theme, "string"),
67 "DEFINITION": idleConf.GetHighlight(theme, "definition"),
68 "SYNC": {'background':None,'foreground':None},
69 "TODO": {'background':None,'foreground':None},
70 "BREAK": idleConf.GetHighlight(theme, "break"),
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000071 "ERROR": idleConf.GetHighlight(theme, "error"),
Steven M. Gavab77d3432002-03-02 07:16:21 +000072 # The following is used by ReplaceDialog:
73 "hit": idleConf.GetHighlight(theme, "hit"),
74 }
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000075
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000076 if DEBUG: print 'tagdefs',self.tagdefs
David Scherer7aced172000-08-15 01:13:23 +000077
78 def insert(self, index, chars, tags=None):
79 index = self.index(index)
80 self.delegate.insert(index, chars, tags)
81 self.notify_range(index, index + "+%dc" % len(chars))
82
83 def delete(self, index1, index2=None):
84 index1 = self.index(index1)
85 self.delegate.delete(index1, index2)
86 self.notify_range(index1)
87
88 after_id = None
89 allow_colorizing = 1
90 colorizing = 0
91
92 def notify_range(self, index1, index2=None):
93 self.tag_add("TODO", index1, index2)
94 if self.after_id:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000095 if DEBUG: print "colorizing already scheduled"
David Scherer7aced172000-08-15 01:13:23 +000096 return
97 if self.colorizing:
98 self.stop_colorizing = 1
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000099 if DEBUG: print "stop colorizing"
David Scherer7aced172000-08-15 01:13:23 +0000100 if self.allow_colorizing:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000101 if DEBUG: print "schedule colorizing"
David Scherer7aced172000-08-15 01:13:23 +0000102 self.after_id = self.after(1, self.recolorize)
103
104 close_when_done = None # Window to be closed when done colorizing
105
106 def close(self, close_when_done=None):
107 if self.after_id:
108 after_id = self.after_id
109 self.after_id = None
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000110 if DEBUG: print "cancel scheduled recolorizer"
David Scherer7aced172000-08-15 01:13:23 +0000111 self.after_cancel(after_id)
112 self.allow_colorizing = 0
113 self.stop_colorizing = 1
114 if close_when_done:
115 if not self.colorizing:
116 close_when_done.destroy()
117 else:
118 self.close_when_done = close_when_done
119
120 def toggle_colorize_event(self, event):
121 if self.after_id:
122 after_id = self.after_id
123 self.after_id = None
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000124 if DEBUG: print "cancel scheduled recolorizer"
David Scherer7aced172000-08-15 01:13:23 +0000125 self.after_cancel(after_id)
126 if self.allow_colorizing and self.colorizing:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000127 if DEBUG: print "stop colorizing"
David Scherer7aced172000-08-15 01:13:23 +0000128 self.stop_colorizing = 1
129 self.allow_colorizing = not self.allow_colorizing
130 if self.allow_colorizing and not self.colorizing:
131 self.after_id = self.after(1, self.recolorize)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000132 if DEBUG:
David Scherer7aced172000-08-15 01:13:23 +0000133 print "auto colorizing turned", self.allow_colorizing and "on" or "off"
134 return "break"
135
136 def recolorize(self):
137 self.after_id = None
138 if not self.delegate:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000139 if DEBUG: print "no delegate"
David Scherer7aced172000-08-15 01:13:23 +0000140 return
141 if not self.allow_colorizing:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000142 if DEBUG: print "auto colorizing is off"
David Scherer7aced172000-08-15 01:13:23 +0000143 return
144 if self.colorizing:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000145 if DEBUG: print "already colorizing"
David Scherer7aced172000-08-15 01:13:23 +0000146 return
147 try:
148 self.stop_colorizing = 0
149 self.colorizing = 1
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000150 if DEBUG: print "colorizing..."
David Scherer7aced172000-08-15 01:13:23 +0000151 t0 = time.clock()
152 self.recolorize_main()
153 t1 = time.clock()
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000154 if DEBUG: print "%.3f seconds" % (t1-t0)
David Scherer7aced172000-08-15 01:13:23 +0000155 finally:
156 self.colorizing = 0
157 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000158 if DEBUG: print "reschedule colorizing"
David Scherer7aced172000-08-15 01:13:23 +0000159 self.after_id = self.after(1, self.recolorize)
160 if self.close_when_done:
161 top = self.close_when_done
162 self.close_when_done = None
163 top.destroy()
164
165 def recolorize_main(self):
166 next = "1.0"
167 while 1:
168 item = self.tag_nextrange("TODO", next)
169 if not item:
170 break
171 head, tail = item
172 self.tag_remove("SYNC", head, tail)
173 item = self.tag_prevrange("SYNC", head)
174 if item:
175 head = item[1]
176 else:
177 head = "1.0"
178
179 chars = ""
180 next = head
181 lines_to_get = 1
182 ok = 0
183 while not ok:
184 mark = next
185 next = self.index(mark + "+%d lines linestart" %
186 lines_to_get)
187 lines_to_get = min(lines_to_get * 2, 100)
188 ok = "SYNC" in self.tag_names(next + "-1c")
189 line = self.get(mark, next)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000190 ##print head, "get", mark, next, "->", repr(line)
David Scherer7aced172000-08-15 01:13:23 +0000191 if not line:
192 return
193 for tag in self.tagdefs.keys():
194 self.tag_remove(tag, mark, next)
195 chars = chars + line
196 m = self.prog.search(chars)
197 while m:
198 for key, value in m.groupdict().items():
199 if value:
200 a, b = m.span(key)
201 self.tag_add(key,
202 head + "+%dc" % a,
203 head + "+%dc" % b)
204 if value in ("def", "class"):
205 m1 = self.idprog.match(chars, b)
206 if m1:
207 a, b = m1.span(1)
208 self.tag_add("DEFINITION",
209 head + "+%dc" % a,
210 head + "+%dc" % b)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000211 elif value == "import":
212 # color all the "as" words on same line;
213 # cheap approximation to the truth
214 while 1:
215 m1 = self.asprog.match(chars, b)
216 if not m1:
217 break
218 a, b = m1.span(1)
219 self.tag_add("KEYWORD",
220 head + "+%dc" % a,
221 head + "+%dc" % b)
David Scherer7aced172000-08-15 01:13:23 +0000222 m = self.prog.search(chars, m.end())
223 if "SYNC" in self.tag_names(next + "-1c"):
224 head = next
225 chars = ""
226 else:
227 ok = 0
228 if not ok:
229 # We're in an inconsistent state, and the call to
230 # update may tell us to stop. It may also change
231 # the correct value for "next" (since this is a
232 # line.col string, not a true mark). So leave a
233 # crumb telling the next invocation to resume here
234 # in case update tells us to leave.
235 self.tag_add("TODO", next)
236 self.update()
237 if self.stop_colorizing:
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000238 if DEBUG: print "colorizing stopped"
David Scherer7aced172000-08-15 01:13:23 +0000239 return
240
241
242def main():
243 from Percolator import Percolator
244 root = Tk()
245 root.wm_protocol("WM_DELETE_WINDOW", root.quit)
246 text = Text(background="white")
247 text.pack(expand=1, fill="both")
248 text.focus_set()
249 p = Percolator(text)
250 d = ColorDelegator()
251 p.insertfilter(d)
252 root.mainloop()
253
254if __name__ == "__main__":
255 main()