Update output generated by test_scope
[python/dscho.git] / Lib / idlelib / ColorDelegator.py
blobb4d655950eac2bdfb0d0906e9b31c6113df699aa
1 import time
2 import string
3 import re
4 import keyword
5 from Tkinter import *
6 from Delegator import Delegator
7 from configHandler import idleConf
9 #$ event <<toggle-auto-coloring>>
10 #$ win <Control-slash>
11 #$ unix <Control-slash>
13 DEBUG = 0
16 def any(name, list):
17 return "(?P<%s>" % name + string.join(list, "|") + ")"
19 def make_pat():
20 kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
21 comment = any("COMMENT", [r"#[^\n]*"])
22 sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
23 dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
24 sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
25 dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
26 string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
27 return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
29 prog = re.compile(make_pat(), re.S)
30 idprog = re.compile(r"\s+(\w+)", re.S)
31 asprog = re.compile(r".*?\b(as)\b", re.S)
33 class ColorDelegator(Delegator):
35 def __init__(self):
36 Delegator.__init__(self)
37 self.prog = prog
38 self.idprog = idprog
39 self.asprog = asprog
41 def setdelegate(self, delegate):
42 if self.delegate is not None:
43 self.unbind("<<toggle-auto-coloring>>")
44 Delegator.setdelegate(self, delegate)
45 if delegate is not None:
46 self.config_colors()
47 self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
48 self.notify_range("1.0", "end")
50 def config_colors(self):
51 for tag, cnf in self.tagdefs.items():
52 if cnf:
53 apply(self.tag_configure, (tag,), cnf)
54 self.tag_raise('sel')
56 theme = idleConf.GetOption('main','Theme','name')
58 tagdefs = {
59 "COMMENT": idleConf.GetHighlight(theme, "comment"),
60 "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
61 "STRING": idleConf.GetHighlight(theme, "string"),
62 "DEFINITION": idleConf.GetHighlight(theme, "definition"),
63 "SYNC": idleConf.GetHighlight(theme, "sync"),
64 "TODO": idleConf.GetHighlight(theme, "todo"),
65 "BREAK": idleConf.GetHighlight(theme, "break"),
66 # The following is used by ReplaceDialog:
67 "hit": idleConf.GetHighlight(theme, "hit"),
70 if DEBUG: print 'tagdefs',tagdefs
72 def insert(self, index, chars, tags=None):
73 index = self.index(index)
74 self.delegate.insert(index, chars, tags)
75 self.notify_range(index, index + "+%dc" % len(chars))
77 def delete(self, index1, index2=None):
78 index1 = self.index(index1)
79 self.delegate.delete(index1, index2)
80 self.notify_range(index1)
82 after_id = None
83 allow_colorizing = 1
84 colorizing = 0
86 def notify_range(self, index1, index2=None):
87 self.tag_add("TODO", index1, index2)
88 if self.after_id:
89 if DEBUG: print "colorizing already scheduled"
90 return
91 if self.colorizing:
92 self.stop_colorizing = 1
93 if DEBUG: print "stop colorizing"
94 if self.allow_colorizing:
95 if DEBUG: print "schedule colorizing"
96 self.after_id = self.after(1, self.recolorize)
98 close_when_done = None # Window to be closed when done colorizing
100 def close(self, close_when_done=None):
101 if self.after_id:
102 after_id = self.after_id
103 self.after_id = None
104 if DEBUG: print "cancel scheduled recolorizer"
105 self.after_cancel(after_id)
106 self.allow_colorizing = 0
107 self.stop_colorizing = 1
108 if close_when_done:
109 if not self.colorizing:
110 close_when_done.destroy()
111 else:
112 self.close_when_done = close_when_done
114 def toggle_colorize_event(self, event):
115 if self.after_id:
116 after_id = self.after_id
117 self.after_id = None
118 if DEBUG: print "cancel scheduled recolorizer"
119 self.after_cancel(after_id)
120 if self.allow_colorizing and self.colorizing:
121 if DEBUG: print "stop colorizing"
122 self.stop_colorizing = 1
123 self.allow_colorizing = not self.allow_colorizing
124 if self.allow_colorizing and not self.colorizing:
125 self.after_id = self.after(1, self.recolorize)
126 if DEBUG:
127 print "auto colorizing turned", self.allow_colorizing and "on" or "off"
128 return "break"
130 def recolorize(self):
131 self.after_id = None
132 if not self.delegate:
133 if DEBUG: print "no delegate"
134 return
135 if not self.allow_colorizing:
136 if DEBUG: print "auto colorizing is off"
137 return
138 if self.colorizing:
139 if DEBUG: print "already colorizing"
140 return
141 try:
142 self.stop_colorizing = 0
143 self.colorizing = 1
144 if DEBUG: print "colorizing..."
145 t0 = time.clock()
146 self.recolorize_main()
147 t1 = time.clock()
148 if DEBUG: print "%.3f seconds" % (t1-t0)
149 finally:
150 self.colorizing = 0
151 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
152 if DEBUG: print "reschedule colorizing"
153 self.after_id = self.after(1, self.recolorize)
154 if self.close_when_done:
155 top = self.close_when_done
156 self.close_when_done = None
157 top.destroy()
159 def recolorize_main(self):
160 next = "1.0"
161 while 1:
162 item = self.tag_nextrange("TODO", next)
163 if not item:
164 break
165 head, tail = item
166 self.tag_remove("SYNC", head, tail)
167 item = self.tag_prevrange("SYNC", head)
168 if item:
169 head = item[1]
170 else:
171 head = "1.0"
173 chars = ""
174 next = head
175 lines_to_get = 1
176 ok = 0
177 while not ok:
178 mark = next
179 next = self.index(mark + "+%d lines linestart" %
180 lines_to_get)
181 lines_to_get = min(lines_to_get * 2, 100)
182 ok = "SYNC" in self.tag_names(next + "-1c")
183 line = self.get(mark, next)
184 ##print head, "get", mark, next, "->", `line`
185 if not line:
186 return
187 for tag in self.tagdefs.keys():
188 self.tag_remove(tag, mark, next)
189 chars = chars + line
190 m = self.prog.search(chars)
191 while m:
192 for key, value in m.groupdict().items():
193 if value:
194 a, b = m.span(key)
195 self.tag_add(key,
196 head + "+%dc" % a,
197 head + "+%dc" % b)
198 if value in ("def", "class"):
199 m1 = self.idprog.match(chars, b)
200 if m1:
201 a, b = m1.span(1)
202 self.tag_add("DEFINITION",
203 head + "+%dc" % a,
204 head + "+%dc" % b)
205 elif value == "import":
206 # color all the "as" words on same line;
207 # cheap approximation to the truth
208 while 1:
209 m1 = self.asprog.match(chars, b)
210 if not m1:
211 break
212 a, b = m1.span(1)
213 self.tag_add("KEYWORD",
214 head + "+%dc" % a,
215 head + "+%dc" % b)
216 m = self.prog.search(chars, m.end())
217 if "SYNC" in self.tag_names(next + "-1c"):
218 head = next
219 chars = ""
220 else:
221 ok = 0
222 if not ok:
223 # We're in an inconsistent state, and the call to
224 # update may tell us to stop. It may also change
225 # the correct value for "next" (since this is a
226 # line.col string, not a true mark). So leave a
227 # crumb telling the next invocation to resume here
228 # in case update tells us to leave.
229 self.tag_add("TODO", next)
230 self.update()
231 if self.stop_colorizing:
232 if DEBUG: print "colorizing stopped"
233 return
236 def main():
237 from Percolator import Percolator
238 root = Tk()
239 root.wm_protocol("WM_DELETE_WINDOW", root.quit)
240 text = Text(background="white")
241 text.pack(expand=1, fill="both")
242 text.focus_set()
243 p = Percolator(text)
244 d = ColorDelegator()
245 p.insertfilter(d)
246 root.mainloop()
248 if __name__ == "__main__":
249 main()