blob: 61e2be47c7e7fe57c00b453b1be21b31092e428c [file] [log] [blame]
David Scherer7aced172000-08-15 01:13:23 +00001import time
David Scherer7aced172000-08-15 01:13:23 +00002import re
3import keyword
Georg Brandl1a3284e2007-12-02 09:40:06 +00004import builtins
Georg Brandl14fc4272008-05-17 18:39:55 +00005from tkinter import *
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +00006from idlelib.Delegator import Delegator
7from idlelib.configHandler import idleConf
David Scherer7aced172000-08-15 01:13:23 +00008
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +00009DEBUG = False
David Scherer7aced172000-08-15 01:13:23 +000010
Thomas Wouters0e3f5912006-08-11 14:57:12 +000011def any(name, alternates):
12 "Return a named group pattern matching list of alternates."
13 return "(?P<%s>" % name + "|".join(alternates) + ")"
David Scherer7aced172000-08-15 01:13:23 +000014
15def make_pat():
16 kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
Georg Brandl1a3284e2007-12-02 09:40:06 +000017 builtinlist = [str(name) for name in dir(builtins)
Terry Jan Reedydc224f82012-01-16 03:20:27 -050018 if not name.startswith('_') and \
19 name not in keyword.kwlist]
Alex Martelli01c77c62006-08-24 02:58:11 +000020 # self.file = open("file") :
Thomas Wouters0e3f5912006-08-11 14:57:12 +000021 # 1st 'file' colorized normal, 2nd as builtin, 3rd as string
22 builtin = r"([^.'\"\\#]\b|^)" + any("BUILTIN", builtinlist) + r"\b"
David Scherer7aced172000-08-15 01:13:23 +000023 comment = any("COMMENT", [r"#[^\n]*"])
Ned Deily5e92a1e2012-05-29 22:55:43 -070024 stringprefix = r"(\br|u|ur|R|U|UR|Ur|uR|b|B|br|Br|bR|BR|rb|rB|Rb|RB)?"
25 sqstring = stringprefix + r"'[^'\\\n]*(\\.[^'\\\n]*)*'?"
26 dqstring = stringprefix + r'"[^"\\\n]*(\\.[^"\\\n]*)*"?'
27 sq3string = stringprefix + r"'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
28 dq3string = stringprefix + r'"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
David Scherer7aced172000-08-15 01:13:23 +000029 string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +000030 return kw + "|" + builtin + "|" + comment + "|" + string +\
31 "|" + any("SYNC", [r"\n"])
David Scherer7aced172000-08-15 01:13:23 +000032
33prog = re.compile(make_pat(), re.S)
34idprog = re.compile(r"\s+(\w+)", re.S)
Thomas Wouters0e3f5912006-08-11 14:57:12 +000035asprog = re.compile(r".*?\b(as)\b")
David Scherer7aced172000-08-15 01:13:23 +000036
37class ColorDelegator(Delegator):
38
39 def __init__(self):
40 Delegator.__init__(self)
41 self.prog = prog
42 self.idprog = idprog
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +000043 self.asprog = asprog
Steven M. Gavab77d3432002-03-02 07:16:21 +000044 self.LoadTagDefs()
David Scherer7aced172000-08-15 01:13:23 +000045
46 def setdelegate(self, delegate):
47 if self.delegate is not None:
48 self.unbind("<<toggle-auto-coloring>>")
49 Delegator.setdelegate(self, delegate)
50 if delegate is not None:
51 self.config_colors()
52 self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
53 self.notify_range("1.0", "end")
Roger Serwy7733be82013-04-07 12:41:16 -050054 else:
55 # No delegate - stop any colorizing
56 self.stop_colorizing = True
57 self.allow_colorizing = False
David Scherer7aced172000-08-15 01:13:23 +000058
59 def config_colors(self):
60 for tag, cnf in self.tagdefs.items():
61 if cnf:
Raymond Hettinger931237e2003-07-09 18:48:24 +000062 self.tag_configure(tag, **cnf)
David Scherer7aced172000-08-15 01:13:23 +000063 self.tag_raise('sel')
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000064
Steven M. Gavab77d3432002-03-02 07:16:21 +000065 def LoadTagDefs(self):
66 theme = idleConf.GetOption('main','Theme','name')
67 self.tagdefs = {
68 "COMMENT": idleConf.GetHighlight(theme, "comment"),
69 "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
Kurt B. Kaiser73360a32004-03-08 18:15:31 +000070 "BUILTIN": idleConf.GetHighlight(theme, "builtin"),
Steven M. Gavab77d3432002-03-02 07:16:21 +000071 "STRING": idleConf.GetHighlight(theme, "string"),
72 "DEFINITION": idleConf.GetHighlight(theme, "definition"),
73 "SYNC": {'background':None,'foreground':None},
74 "TODO": {'background':None,'foreground':None},
75 "BREAK": idleConf.GetHighlight(theme, "break"),
Kurt B. Kaiser92b5ca32002-12-17 21:16:12 +000076 "ERROR": idleConf.GetHighlight(theme, "error"),
Steven M. Gavab77d3432002-03-02 07:16:21 +000077 # The following is used by ReplaceDialog:
78 "hit": idleConf.GetHighlight(theme, "hit"),
79 }
Kurt B. Kaiser6655e4b2002-12-31 16:03:23 +000080
Guido van Rossumbe19ed72007-02-09 05:37:30 +000081 if DEBUG: print('tagdefs',self.tagdefs)
David Scherer7aced172000-08-15 01:13:23 +000082
83 def insert(self, index, chars, tags=None):
84 index = self.index(index)
85 self.delegate.insert(index, chars, tags)
86 self.notify_range(index, index + "+%dc" % len(chars))
87
88 def delete(self, index1, index2=None):
89 index1 = self.index(index1)
90 self.delegate.delete(index1, index2)
91 self.notify_range(index1)
92
93 after_id = None
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +000094 allow_colorizing = True
95 colorizing = False
David Scherer7aced172000-08-15 01:13:23 +000096
97 def notify_range(self, index1, index2=None):
98 self.tag_add("TODO", index1, index2)
99 if self.after_id:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000100 if DEBUG: print("colorizing already scheduled")
David Scherer7aced172000-08-15 01:13:23 +0000101 return
102 if self.colorizing:
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000103 self.stop_colorizing = True
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000104 if DEBUG: print("stop colorizing")
David Scherer7aced172000-08-15 01:13:23 +0000105 if self.allow_colorizing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000106 if DEBUG: print("schedule colorizing")
David Scherer7aced172000-08-15 01:13:23 +0000107 self.after_id = self.after(1, self.recolorize)
108
109 close_when_done = None # Window to be closed when done colorizing
110
111 def close(self, close_when_done=None):
112 if self.after_id:
113 after_id = self.after_id
114 self.after_id = None
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000115 if DEBUG: print("cancel scheduled recolorizer")
David Scherer7aced172000-08-15 01:13:23 +0000116 self.after_cancel(after_id)
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000117 self.allow_colorizing = False
118 self.stop_colorizing = True
David Scherer7aced172000-08-15 01:13:23 +0000119 if close_when_done:
120 if not self.colorizing:
121 close_when_done.destroy()
122 else:
123 self.close_when_done = close_when_done
124
125 def toggle_colorize_event(self, event):
126 if self.after_id:
127 after_id = self.after_id
128 self.after_id = None
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000129 if DEBUG: print("cancel scheduled recolorizer")
David Scherer7aced172000-08-15 01:13:23 +0000130 self.after_cancel(after_id)
131 if self.allow_colorizing and self.colorizing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000132 if DEBUG: print("stop colorizing")
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000133 self.stop_colorizing = True
David Scherer7aced172000-08-15 01:13:23 +0000134 self.allow_colorizing = not self.allow_colorizing
135 if self.allow_colorizing and not self.colorizing:
136 self.after_id = self.after(1, self.recolorize)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000137 if DEBUG:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000138 print("auto colorizing turned",\
139 self.allow_colorizing and "on" or "off")
David Scherer7aced172000-08-15 01:13:23 +0000140 return "break"
141
142 def recolorize(self):
143 self.after_id = None
144 if not self.delegate:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000145 if DEBUG: print("no delegate")
David Scherer7aced172000-08-15 01:13:23 +0000146 return
147 if not self.allow_colorizing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000148 if DEBUG: print("auto colorizing is off")
David Scherer7aced172000-08-15 01:13:23 +0000149 return
150 if self.colorizing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000151 if DEBUG: print("already colorizing")
David Scherer7aced172000-08-15 01:13:23 +0000152 return
153 try:
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000154 self.stop_colorizing = False
155 self.colorizing = True
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000156 if DEBUG: print("colorizing...")
Victor Stinnerfe98e2f2012-04-29 03:01:20 +0200157 t0 = time.perf_counter()
David Scherer7aced172000-08-15 01:13:23 +0000158 self.recolorize_main()
Victor Stinnerfe98e2f2012-04-29 03:01:20 +0200159 t1 = time.perf_counter()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000160 if DEBUG: print("%.3f seconds" % (t1-t0))
David Scherer7aced172000-08-15 01:13:23 +0000161 finally:
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000162 self.colorizing = False
David Scherer7aced172000-08-15 01:13:23 +0000163 if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000164 if DEBUG: print("reschedule colorizing")
David Scherer7aced172000-08-15 01:13:23 +0000165 self.after_id = self.after(1, self.recolorize)
166 if self.close_when_done:
167 top = self.close_when_done
168 self.close_when_done = None
169 top.destroy()
170
171 def recolorize_main(self):
172 next = "1.0"
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000173 while True:
David Scherer7aced172000-08-15 01:13:23 +0000174 item = self.tag_nextrange("TODO", next)
175 if not item:
176 break
177 head, tail = item
178 self.tag_remove("SYNC", head, tail)
179 item = self.tag_prevrange("SYNC", head)
180 if item:
181 head = item[1]
182 else:
183 head = "1.0"
184
185 chars = ""
186 next = head
187 lines_to_get = 1
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000188 ok = False
David Scherer7aced172000-08-15 01:13:23 +0000189 while not ok:
190 mark = next
191 next = self.index(mark + "+%d lines linestart" %
192 lines_to_get)
193 lines_to_get = min(lines_to_get * 2, 100)
194 ok = "SYNC" in self.tag_names(next + "-1c")
195 line = self.get(mark, next)
Walter Dörwald70a6b492004-02-12 17:35:32 +0000196 ##print head, "get", mark, next, "->", repr(line)
David Scherer7aced172000-08-15 01:13:23 +0000197 if not line:
198 return
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000199 for tag in self.tagdefs:
David Scherer7aced172000-08-15 01:13:23 +0000200 self.tag_remove(tag, mark, next)
201 chars = chars + line
202 m = self.prog.search(chars)
203 while m:
204 for key, value in m.groupdict().items():
205 if value:
206 a, b = m.span(key)
207 self.tag_add(key,
208 head + "+%dc" % a,
209 head + "+%dc" % b)
210 if value in ("def", "class"):
211 m1 = self.idprog.match(chars, b)
212 if m1:
213 a, b = m1.span(1)
214 self.tag_add("DEFINITION",
215 head + "+%dc" % a,
216 head + "+%dc" % b)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000217 elif value == "import":
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000218 # color all the "as" words on same line, except
219 # if in a comment; cheap approximation to the
220 # truth
221 if '#' in chars:
222 endpos = chars.index('#')
223 else:
224 endpos = len(chars)
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000225 while True:
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000226 m1 = self.asprog.match(chars, b, endpos)
Kurt B. Kaiser6df4bf22001-07-13 00:04:24 +0000227 if not m1:
228 break
229 a, b = m1.span(1)
230 self.tag_add("KEYWORD",
231 head + "+%dc" % a,
232 head + "+%dc" % b)
David Scherer7aced172000-08-15 01:13:23 +0000233 m = self.prog.search(chars, m.end())
234 if "SYNC" in self.tag_names(next + "-1c"):
235 head = next
236 chars = ""
237 else:
Kurt B. Kaiser0bc3d982004-03-15 04:26:37 +0000238 ok = False
David Scherer7aced172000-08-15 01:13:23 +0000239 if not ok:
240 # We're in an inconsistent state, and the call to
241 # update may tell us to stop. It may also change
242 # the correct value for "next" (since this is a
243 # line.col string, not a true mark). So leave a
244 # crumb telling the next invocation to resume here
245 # in case update tells us to leave.
246 self.tag_add("TODO", next)
247 self.update()
248 if self.stop_colorizing:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000249 if DEBUG: print("colorizing stopped")
David Scherer7aced172000-08-15 01:13:23 +0000250 return
251
Kurt B. Kaiserdf506ea2005-06-12 04:33:30 +0000252 def removecolors(self):
Kurt B. Kaisere0712772007-08-23 05:25:55 +0000253 for tag in self.tagdefs:
Kurt B. Kaiserdf506ea2005-06-12 04:33:30 +0000254 self.tag_remove(tag, "1.0", "end")
David Scherer7aced172000-08-15 01:13:23 +0000255
256def main():
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000257 from idlelib.Percolator import Percolator
David Scherer7aced172000-08-15 01:13:23 +0000258 root = Tk()
259 root.wm_protocol("WM_DELETE_WINDOW", root.quit)
260 text = Text(background="white")
261 text.pack(expand=1, fill="both")
262 text.focus_set()
263 p = Percolator(text)
264 d = ColorDelegator()
265 p.insertfilter(d)
266 root.mainloop()
267
268if __name__ == "__main__":
269 main()