PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/kbe/res/scripts/common/Lib/idlelib/ColorDelegator.py

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