Fix the HTML tarball target to generate the HTML if needed instead of
[python/dscho.git] / Tools / idle / ColorDelegator.py
blob357358fc48c038816a193f1fd8f986e8f7e75d2d
1 import time
2 import string
3 import re
4 import keyword
5 from Tkinter import *
6 from Delegator import Delegator
8 #$ event <<toggle-auto-coloring>>
9 #$ win <Control-slash>
10 #$ unix <Control-slash>
12 __debug__ = 0
15 def any(name, list):
16 return "(?P<%s>" % name + string.join(list, "|") + ")"
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"])
28 prog = re.compile(make_pat(), re.S)
29 idprog = re.compile(r"\s+(\w+)", re.S)
31 class ColorDelegator(Delegator):
33 def __init__(self):
34 Delegator.__init__(self)
35 self.prog = prog
36 self.idprog = idprog
38 def setdelegate(self, delegate):
39 if self.delegate is not None:
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")
47 def config_colors(self):
48 for tag, cnf in self.tagdefs.items():
49 if cnf:
50 apply(self.tag_configure, (tag,), cnf)
51 self.tag_raise('sel')
53 tagdefs = {
54 "COMMENT": {"foreground": "#dd0000"},
55 "KEYWORD": {"foreground": "#ff7700"},
56 "STRING": {"foreground": "#00aa00"},
57 "DEFINITION": {"foreground": "#0000ff"},
59 "SYNC": {}, #{"background": "#ffff00"},
60 "TODO": {}, #{"background": "#cccccc"},
62 "BREAK": {"background": "#FF7777"},
64 # The following is used by ReplaceDialog:
65 "hit": {"foreground": "#FFFFFF", "background": "#000000"},
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))
73 def delete(self, index1, index2=None):
74 index1 = self.index(index1)
75 self.delegate.delete(index1, index2)
76 self.notify_range(index1)
78 after_id = None
79 allow_colorizing = 1
80 colorizing = 0
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
89 if __debug__: print "stop colorizing"
90 if self.allow_colorizing:
91 if __debug__: print "schedule colorizing"
92 self.after_id = self.after(1, self.recolorize)
94 def close(self):
95 if self.after_id:
96 after_id = self.after_id
97 self.after_id = None
98 if __debug__: print "cancel scheduled recolorizer"
99 self.after_cancel(after_id)
100 self.allow_colorizing = 0
101 self.stop_colorizing = 1
103 def toggle_colorize_event(self, event):
104 if self.after_id:
105 after_id = self.after_id
106 self.after_id = None
107 if __debug__: print "cancel scheduled recolorizer"
108 self.after_cancel(after_id)
109 if self.allow_colorizing and self.colorizing:
110 if __debug__: print "stop colorizing"
111 self.stop_colorizing = 1
112 self.allow_colorizing = not self.allow_colorizing
113 if self.allow_colorizing and not self.colorizing:
114 self.after_id = self.after(1, self.recolorize)
115 if __debug__:
116 print "auto colorizing turned", self.allow_colorizing and "on" or "off"
117 return "break"
119 def recolorize(self):
120 self.after_id = None
121 if not self.delegate:
122 if __debug__: print "no delegate"
123 return
124 if not self.allow_colorizing:
125 if __debug__: print "auto colorizing is off"
126 return
127 if self.colorizing:
128 if __debug__: print "already colorizing"
129 return
130 try:
131 self.stop_colorizing = 0
132 self.colorizing = 1
133 if __debug__: print "colorizing..."
134 t0 = time.clock()
135 self.recolorize_main()
136 t1 = time.clock()
137 if __debug__: print "%.3f seconds" % (t1-t0)
138 finally:
139 self.colorizing = 0
140 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
141 if __debug__: print "reschedule colorizing"
142 self.after_id = self.after(1, self.recolorize)
144 def recolorize_main(self):
145 next = "1.0"
146 was_ok = is_ok = 0
147 while 1:
148 item = self.tag_nextrange("TODO", next)
149 if not item:
150 break
151 head, tail = item
152 self.tag_remove("SYNC", head, tail)
153 item = self.tag_prevrange("SYNC", head)
154 if item:
155 head = item[1]
156 else:
157 head = "1.0"
159 chars = ""
160 mark = head
161 is_ok = was_ok = 0
162 while not (was_ok and is_ok):
163 next = self.index(mark + " lineend +1c")
164 was_ok = "SYNC" in self.tag_names(next + "-1c")
165 line = self.get(mark, next)
166 ##print head, "get", mark, next, "->", `line`
167 if not line:
168 return
169 for tag in self.tagdefs.keys():
170 self.tag_remove(tag, mark, next)
171 chars = chars + line
172 m = self.prog.search(chars)
173 while m:
174 i, j = m.span()
175 for key, value in m.groupdict().items():
176 if value:
177 a, b = m.span(key)
178 self.tag_add(key,
179 head + "+%dc" % a,
180 head + "+%dc" % b)
181 if value in ("def", "class"):
182 m1 = self.idprog.match(chars, b)
183 if m1:
184 a, b = m1.span(1)
185 self.tag_add("DEFINITION",
186 head + "+%dc" % a,
187 head + "+%dc" % b)
188 m = self.prog.search(chars, j)
189 is_ok = "SYNC" in self.tag_names(next + "-1c")
190 mark = next
191 if is_ok:
192 head = mark
193 chars = ""
194 self.update()
195 if self.stop_colorizing:
196 if __debug__: print "colorizing stopped"
197 return
200 def main():
201 from Percolator import Percolator
202 root = Tk()
203 root.wm_protocol("WM_DELETE_WINDOW", root.quit)
204 text = Text(background="white")
205 text.pack(expand=1, fill="both")
206 text.focus_set()
207 p = Percolator(text)
208 d = ColorDelegator()
209 p.insertfilter(d)
210 root.mainloop()
212 if __name__ == "__main__":
213 main()