mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import time
 | |
| import re
 | |
| import keyword
 | |
| from Tkinter import *
 | |
| from Delegator import Delegator
 | |
| from configHandler import idleConf
 | |
| 
 | |
| #$ event <<toggle-auto-coloring>>
 | |
| #$ win <Control-slash>
 | |
| #$ unix <Control-slash>
 | |
| 
 | |
| DEBUG = 0
 | |
| 
 | |
| 
 | |
| def any(name, list):
 | |
|     return "(?P<%s>" % name + "|".join(list) + ")"
 | |
| 
 | |
| def make_pat():
 | |
|     kw = r"\b" + any("KEYWORD", keyword.kwlist) + r"\b"
 | |
|     comment = any("COMMENT", [r"#[^\n]*"])
 | |
|     sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
 | |
|     dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
 | |
|     sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
 | |
|     dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
 | |
|     string = any("STRING", [sq3string, dq3string, sqstring, dqstring])
 | |
|     return kw + "|" + comment + "|" + string + "|" + any("SYNC", [r"\n"])
 | |
| 
 | |
| prog = re.compile(make_pat(), re.S)
 | |
| idprog = re.compile(r"\s+(\w+)", re.S)
 | |
| asprog = re.compile(r".*?\b(as)\b", re.S)
 | |
| 
 | |
| class ColorDelegator(Delegator):
 | |
| 
 | |
|     def __init__(self):
 | |
|         Delegator.__init__(self)
 | |
|         self.prog = prog
 | |
|         self.idprog = idprog
 | |
|         self.asprog = asprog
 | |
|         self.LoadTagDefs()
 | |
| 
 | |
|     def setdelegate(self, delegate):
 | |
|         if self.delegate is not None:
 | |
|             self.unbind("<<toggle-auto-coloring>>")
 | |
|         Delegator.setdelegate(self, delegate)
 | |
|         if delegate is not None:
 | |
|             self.config_colors()
 | |
|             self.bind("<<toggle-auto-coloring>>", self.toggle_colorize_event)
 | |
|             self.notify_range("1.0", "end")
 | |
| 
 | |
|     def config_colors(self):
 | |
|         for tag, cnf in self.tagdefs.items():
 | |
|             if cnf:
 | |
|                 apply(self.tag_configure, (tag,), cnf)
 | |
|         self.tag_raise('sel')
 | |
| 
 | |
|     def LoadTagDefs(self):
 | |
|         theme = idleConf.GetOption('main','Theme','name')
 | |
|         self.tagdefs = {
 | |
|             "COMMENT": idleConf.GetHighlight(theme, "comment"),
 | |
|             "KEYWORD": idleConf.GetHighlight(theme, "keyword"),
 | |
|             "STRING": idleConf.GetHighlight(theme, "string"),
 | |
|             "DEFINITION": idleConf.GetHighlight(theme, "definition"),
 | |
|             "SYNC": {'background':None,'foreground':None},
 | |
|             "TODO": {'background':None,'foreground':None},
 | |
|             "BREAK": idleConf.GetHighlight(theme, "break"),
 | |
|             "ERROR": idleConf.GetHighlight(theme, "error"),
 | |
|             # The following is used by ReplaceDialog:
 | |
|             "hit": idleConf.GetHighlight(theme, "hit"),
 | |
|             }
 | |
| 
 | |
|     if DEBUG: print 'tagdefs',tagdefs
 | |
| 
 | |
|     def insert(self, index, chars, tags=None):
 | |
|         index = self.index(index)
 | |
|         self.delegate.insert(index, chars, tags)
 | |
|         self.notify_range(index, index + "+%dc" % len(chars))
 | |
| 
 | |
|     def delete(self, index1, index2=None):
 | |
|         index1 = self.index(index1)
 | |
|         self.delegate.delete(index1, index2)
 | |
|         self.notify_range(index1)
 | |
| 
 | |
|     after_id = None
 | |
|     allow_colorizing = 1
 | |
|     colorizing = 0
 | |
| 
 | |
|     def notify_range(self, index1, index2=None):
 | |
|         self.tag_add("TODO", index1, index2)
 | |
|         if self.after_id:
 | |
|             if DEBUG: print "colorizing already scheduled"
 | |
|             return
 | |
|         if self.colorizing:
 | |
|             self.stop_colorizing = 1
 | |
|             if DEBUG: print "stop colorizing"
 | |
|         if self.allow_colorizing:
 | |
|             if DEBUG: print "schedule colorizing"
 | |
|             self.after_id = self.after(1, self.recolorize)
 | |
| 
 | |
|     close_when_done = None # Window to be closed when done colorizing
 | |
| 
 | |
|     def close(self, close_when_done=None):
 | |
|         if self.after_id:
 | |
|             after_id = self.after_id
 | |
|             self.after_id = None
 | |
|             if DEBUG: print "cancel scheduled recolorizer"
 | |
|             self.after_cancel(after_id)
 | |
|         self.allow_colorizing = 0
 | |
|         self.stop_colorizing = 1
 | |
|         if close_when_done:
 | |
|             if not self.colorizing:
 | |
|                 close_when_done.destroy()
 | |
|             else:
 | |
|                 self.close_when_done = close_when_done
 | |
| 
 | |
|     def toggle_colorize_event(self, event):
 | |
|         if self.after_id:
 | |
|             after_id = self.after_id
 | |
|             self.after_id = None
 | |
|             if DEBUG: print "cancel scheduled recolorizer"
 | |
|             self.after_cancel(after_id)
 | |
|         if self.allow_colorizing and self.colorizing:
 | |
|             if DEBUG: print "stop colorizing"
 | |
|             self.stop_colorizing = 1
 | |
|         self.allow_colorizing = not self.allow_colorizing
 | |
|         if self.allow_colorizing and not self.colorizing:
 | |
|             self.after_id = self.after(1, self.recolorize)
 | |
|         if DEBUG:
 | |
|             print "auto colorizing turned", self.allow_colorizing and "on" or "off"
 | |
|         return "break"
 | |
| 
 | |
|     def recolorize(self):
 | |
|         self.after_id = None
 | |
|         if not self.delegate:
 | |
|             if DEBUG: print "no delegate"
 | |
|             return
 | |
|         if not self.allow_colorizing:
 | |
|             if DEBUG: print "auto colorizing is off"
 | |
|             return
 | |
|         if self.colorizing:
 | |
|             if DEBUG: print "already colorizing"
 | |
|             return
 | |
|         try:
 | |
|             self.stop_colorizing = 0
 | |
|             self.colorizing = 1
 | |
|             if DEBUG: print "colorizing..."
 | |
|             t0 = time.clock()
 | |
|             self.recolorize_main()
 | |
|             t1 = time.clock()
 | |
|             if DEBUG: print "%.3f seconds" % (t1-t0)
 | |
|         finally:
 | |
|             self.colorizing = 0
 | |
|         if self.allow_colorizing and self.tag_nextrange("TODO", "1.0"):
 | |
|             if DEBUG: print "reschedule colorizing"
 | |
|             self.after_id = self.after(1, self.recolorize)
 | |
|         if self.close_when_done:
 | |
|             top = self.close_when_done
 | |
|             self.close_when_done = None
 | |
|             top.destroy()
 | |
| 
 | |
|     def recolorize_main(self):
 | |
|         next = "1.0"
 | |
|         while 1:
 | |
|             item = self.tag_nextrange("TODO", next)
 | |
|             if not item:
 | |
|                 break
 | |
|             head, tail = item
 | |
|             self.tag_remove("SYNC", head, tail)
 | |
|             item = self.tag_prevrange("SYNC", head)
 | |
|             if item:
 | |
|                 head = item[1]
 | |
|             else:
 | |
|                 head = "1.0"
 | |
| 
 | |
|             chars = ""
 | |
|             next = head
 | |
|             lines_to_get = 1
 | |
|             ok = 0
 | |
|             while not ok:
 | |
|                 mark = next
 | |
|                 next = self.index(mark + "+%d lines linestart" %
 | |
|                                          lines_to_get)
 | |
|                 lines_to_get = min(lines_to_get * 2, 100)
 | |
|                 ok = "SYNC" in self.tag_names(next + "-1c")
 | |
|                 line = self.get(mark, next)
 | |
|                 ##print head, "get", mark, next, "->", `line`
 | |
|                 if not line:
 | |
|                     return
 | |
|                 for tag in self.tagdefs.keys():
 | |
|                     self.tag_remove(tag, mark, next)
 | |
|                 chars = chars + line
 | |
|                 m = self.prog.search(chars)
 | |
|                 while m:
 | |
|                     for key, value in m.groupdict().items():
 | |
|                         if value:
 | |
|                             a, b = m.span(key)
 | |
|                             self.tag_add(key,
 | |
|                                          head + "+%dc" % a,
 | |
|                                          head + "+%dc" % b)
 | |
|                             if value in ("def", "class"):
 | |
|                                 m1 = self.idprog.match(chars, b)
 | |
|                                 if m1:
 | |
|                                     a, b = m1.span(1)
 | |
|                                     self.tag_add("DEFINITION",
 | |
|                                                  head + "+%dc" % a,
 | |
|                                                  head + "+%dc" % b)
 | |
|                             elif value == "import":
 | |
|                                 # color all the "as" words on same line;
 | |
|                                 # cheap approximation to the truth
 | |
|                                 while 1:
 | |
|                                     m1 = self.asprog.match(chars, b)
 | |
|                                     if not m1:
 | |
|                                         break
 | |
|                                     a, b = m1.span(1)
 | |
|                                     self.tag_add("KEYWORD",
 | |
|                                                  head + "+%dc" % a,
 | |
|                                                  head + "+%dc" % b)
 | |
|                     m = self.prog.search(chars, m.end())
 | |
|                 if "SYNC" in self.tag_names(next + "-1c"):
 | |
|                     head = next
 | |
|                     chars = ""
 | |
|                 else:
 | |
|                     ok = 0
 | |
|                 if not ok:
 | |
|                     # We're in an inconsistent state, and the call to
 | |
|                     # update may tell us to stop.  It may also change
 | |
|                     # the correct value for "next" (since this is a
 | |
|                     # line.col string, not a true mark).  So leave a
 | |
|                     # crumb telling the next invocation to resume here
 | |
|                     # in case update tells us to leave.
 | |
|                     self.tag_add("TODO", next)
 | |
|                 self.update()
 | |
|                 if self.stop_colorizing:
 | |
|                     if DEBUG: print "colorizing stopped"
 | |
|                     return
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     from Percolator import Percolator
 | |
|     root = Tk()
 | |
|     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
 | |
|     text = Text(background="white")
 | |
|     text.pack(expand=1, fill="both")
 | |
|     text.focus_set()
 | |
|     p = Percolator(text)
 | |
|     d = ColorDelegator()
 | |
|     p.insertfilter(d)
 | |
|     root.mainloop()
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 | 
