PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib-python/2.7/idlelib/ColorDelegator.py

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