PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

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

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