blob: 8e4667a9877f55acb3839288178815c276d7431b [file] [log] [blame]
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +00001import time
2import string
3import re
4import keyword
5from Tkinter import *
6from Delegator import Delegator
7
Guido van Rossum504b0bf1999-01-02 21:28:54 +00008#$ event <<toggle-auto-coloring>>
9#$ win <Control-slash>
10#$ unix <Control-slash>
11
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000012__debug__ = 0
13
14
15def any(name, list):
16 return "(?P<%s>" % name + string.join(list, "|") + ")"
17
18def 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"])
27
28prog = re.compile(make_pat(), re.S)
29idprog = re.compile(r"\s+(\w+)", re.S)
30
31class ColorDelegator(Delegator):
32
33 def __init__(self):
34 Delegator.__init__(self)
35 self.prog = prog
Guido van Rossum504b0bf1999-01-02 21:28:54 +000036 self.idprog = idprog
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000037
38 def setdelegate(self, delegate):
Guido van Rossum504b0bf1999-01-02 21:28:54 +000039 if self.delegate is not None:
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000040 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")
46
47 def config_colors(self):
48 for tag, cnf in self.tagdefs.items():
49 if cnf:
50 apply(self.tag_configure, (tag,), cnf)
Guido van Rossum6c5baeb1998-10-19 02:22:41 +000051 self.tag_raise('sel')
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000052
53 tagdefs = {
54 "COMMENT": {"foreground": "#dd0000"},
55 "KEYWORD": {"foreground": "#ff7700"},
56 "STRING": {"foreground": "#00aa00"},
57 "DEFINITION": {"foreground": "#0000ff"},
58
59 "SYNC": {}, #{"background": "#ffff00"},
60 "TODO": {}, #{"background": "#cccccc"},
Guido van Rossum504b0bf1999-01-02 21:28:54 +000061
Guido van Rossum6c5baeb1998-10-19 02:22:41 +000062 "BREAK": {"background": "#FF7777"},
Guido van Rossum504b0bf1999-01-02 21:28:54 +000063
64 # The following is used by ReplaceDialog:
65 "hit": {"foreground": "#FFFFFF", "background": "#000000"},
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000066 }
67
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))
72
73 def delete(self, index1, index2=None):
74 index1 = self.index(index1)
75 self.delegate.delete(index1, index2)
76 self.notify_range(index1)
77
78 after_id = None
79 allow_colorizing = 1
80 colorizing = 0
81
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
Guido van Rossum504b0bf1999-01-02 21:28:54 +000089 if __debug__: print "stop colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000090 if self.allow_colorizing:
Guido van Rossum504b0bf1999-01-02 21:28:54 +000091 if __debug__: print "schedule colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000092 self.after_id = self.after(1, self.recolorize)
93
Guido van Rossum5051f4f1999-01-12 22:09:57 +000094 close_when_done = None # Window to be closed when done colorizing
95
96 def close(self, close_when_done=None):
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +000097 if self.after_id:
98 after_id = self.after_id
99 self.after_id = None
100 if __debug__: print "cancel scheduled recolorizer"
101 self.after_cancel(after_id)
102 self.allow_colorizing = 0
103 self.stop_colorizing = 1
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000104 if close_when_done:
105 if not self.colorizing:
106 close_when_done.destroy()
107 else:
108 self.close_when_done = close_when_done
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000109
110 def toggle_colorize_event(self, event):
111 if self.after_id:
112 after_id = self.after_id
113 self.after_id = None
114 if __debug__: print "cancel scheduled recolorizer"
115 self.after_cancel(after_id)
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000116 if self.allow_colorizing and self.colorizing:
117 if __debug__: print "stop colorizing"
118 self.stop_colorizing = 1
119 self.allow_colorizing = not self.allow_colorizing
120 if self.allow_colorizing and not self.colorizing:
121 self.after_id = self.after(1, self.recolorize)
122 if __debug__:
123 print "auto colorizing turned", self.allow_colorizing and "on" or "off"
124 return "break"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000125
126 def recolorize(self):
127 self.after_id = None
128 if not self.delegate:
129 if __debug__: print "no delegate"
130 return
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000131 if not self.allow_colorizing:
132 if __debug__: print "auto colorizing is off"
133 return
134 if self.colorizing:
135 if __debug__: print "already colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000136 return
137 try:
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000138 self.stop_colorizing = 0
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000139 self.colorizing = 1
140 if __debug__: print "colorizing..."
141 t0 = time.clock()
142 self.recolorize_main()
143 t1 = time.clock()
144 if __debug__: print "%.3f seconds" % (t1-t0)
145 finally:
146 self.colorizing = 0
147 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000148 if __debug__: print "reschedule colorizing"
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000149 self.after_id = self.after(1, self.recolorize)
Guido van Rossum5051f4f1999-01-12 22:09:57 +0000150 if self.close_when_done:
151 top = self.close_when_done
152 self.close_when_done = None
153 top.destroy()
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000154
155 def recolorize_main(self):
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000156 next = "1.0"
157 was_ok = is_ok = 0
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 Rossum3b4ca0d1998-10-10 18:48:31 +0000169
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000170 chars = ""
171 mark = head
172 is_ok = was_ok = 0
173 while not (was_ok and is_ok):
174 next = self.index(mark + " lineend +1c")
175 was_ok = "SYNC" in self.tag_names(next + "-1c")
176 line = self.get(mark, next)
177 ##print head, "get", mark, next, "->", `line`
178 if not line:
179 return
180 for tag in self.tagdefs.keys():
181 self.tag_remove(tag, mark, next)
182 chars = chars + line
183 m = self.prog.search(chars)
184 while m:
185 i, j = m.span()
186 for key, value in m.groupdict().items():
187 if value:
188 a, b = m.span(key)
189 self.tag_add(key,
190 head + "+%dc" % a,
191 head + "+%dc" % b)
192 if value in ("def", "class"):
193 m1 = self.idprog.match(chars, b)
194 if m1:
195 a, b = m1.span(1)
196 self.tag_add("DEFINITION",
197 head + "+%dc" % a,
198 head + "+%dc" % b)
199 m = self.prog.search(chars, j)
200 is_ok = "SYNC" in self.tag_names(next + "-1c")
201 mark = next
202 if is_ok:
203 head = mark
204 chars = ""
205 self.update()
206 if self.stop_colorizing:
207 if __debug__: print "colorizing stopped"
208 return
Guido van Rossum3b4ca0d1998-10-10 18:48:31 +0000209
210
211def main():
212 from Percolator import Percolator
213 root = Tk()
214 root.wm_protocol("WM_DELETE_WINDOW", root.quit)
215 text = Text(background="white")
216 text.pack(expand=1, fill="both")
217 text.focus_set()
218 p = Percolator(text)
219 d = ColorDelegator()
220 p.insertfilter(d)
221 root.mainloop()
222
223if __name__ == "__main__":
224 main()