ยปCore Development>Code coverage>Lib/idlelib/ColorDelegator.py

Python code coverage for Lib/idlelib/ColorDelegator.py

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