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

Python code coverage for Lib/idlelib/parenmatch.py

#countcontent
1n/a"""ParenMatch -- An IDLE extension for parenthesis matching.
2n/a
3n/aWhen you hit a right paren, the cursor should move briefly to the left
4n/aparen. Paren here is used generically; the matching applies to
5n/aparentheses, square brackets, and curly braces.
6n/a"""
7n/afrom idlelib.hyperparser import HyperParser
8n/afrom idlelib.config import idleConf
9n/a
10n/a_openers = {')':'(',']':'[','}':'{'}
11n/aCHECK_DELAY = 100 # milliseconds
12n/a
13n/aclass ParenMatch:
14n/a """Highlight matching parentheses
15n/a
16n/a There are three supported style of paren matching, based loosely
17n/a on the Emacs options. The style is select based on the
18n/a HILITE_STYLE attribute; it can be changed used the set_style
19n/a method.
20n/a
21n/a The supported styles are:
22n/a
23n/a default -- When a right paren is typed, highlight the matching
24n/a left paren for 1/2 sec.
25n/a
26n/a expression -- When a right paren is typed, highlight the entire
27n/a expression from the left paren to the right paren.
28n/a
29n/a TODO:
30n/a - extend IDLE with configuration dialog to change options
31n/a - implement rest of Emacs highlight styles (see below)
32n/a - print mismatch warning in IDLE status window
33n/a
34n/a Note: In Emacs, there are several styles of highlight where the
35n/a matching paren is highlighted whenever the cursor is immediately
36n/a to the right of a right paren. I don't know how to do that in Tk,
37n/a so I haven't bothered.
38n/a """
39n/a menudefs = [
40n/a ('edit', [
41n/a ("Show surrounding parens", "<<flash-paren>>"),
42n/a ])
43n/a ]
44n/a STYLE = idleConf.GetOption('extensions','ParenMatch','style',
45n/a default='expression')
46n/a FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
47n/a type='int',default=500)
48n/a HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
49n/a BELL = idleConf.GetOption('extensions','ParenMatch','bell',
50n/a type='bool',default=1)
51n/a
52n/a RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>"
53n/a # We want the restore event be called before the usual return and
54n/a # backspace events.
55n/a RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
56n/a "<Key-Return>", "<Key-BackSpace>")
57n/a
58n/a def __init__(self, editwin):
59n/a self.editwin = editwin
60n/a self.text = editwin.text
61n/a # Bind the check-restore event to the function restore_event,
62n/a # so that we can then use activate_restore (which calls event_add)
63n/a # and deactivate_restore (which calls event_delete).
64n/a editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
65n/a self.restore_event)
66n/a self.bell = self.text.bell if self.BELL else lambda: None
67n/a self.counter = 0
68n/a self.is_restore_active = 0
69n/a self.set_style(self.STYLE)
70n/a
71n/a def activate_restore(self):
72n/a if not self.is_restore_active:
73n/a for seq in self.RESTORE_SEQUENCES:
74n/a self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
75n/a self.is_restore_active = True
76n/a
77n/a def deactivate_restore(self):
78n/a if self.is_restore_active:
79n/a for seq in self.RESTORE_SEQUENCES:
80n/a self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
81n/a self.is_restore_active = False
82n/a
83n/a def set_style(self, style):
84n/a self.STYLE = style
85n/a if style == "default":
86n/a self.create_tag = self.create_tag_default
87n/a self.set_timeout = self.set_timeout_last
88n/a elif style == "expression":
89n/a self.create_tag = self.create_tag_expression
90n/a self.set_timeout = self.set_timeout_none
91n/a
92n/a def flash_paren_event(self, event):
93n/a indices = (HyperParser(self.editwin, "insert")
94n/a .get_surrounding_brackets())
95n/a if indices is None:
96n/a self.bell()
97n/a return
98n/a self.activate_restore()
99n/a self.create_tag(indices)
100n/a self.set_timeout_last()
101n/a
102n/a def paren_closed_event(self, event):
103n/a # If it was a shortcut and not really a closing paren, quit.
104n/a closer = self.text.get("insert-1c")
105n/a if closer not in _openers:
106n/a return
107n/a hp = HyperParser(self.editwin, "insert-1c")
108n/a if not hp.is_in_code():
109n/a return
110n/a indices = hp.get_surrounding_brackets(_openers[closer], True)
111n/a if indices is None:
112n/a self.bell()
113n/a return
114n/a self.activate_restore()
115n/a self.create_tag(indices)
116n/a self.set_timeout()
117n/a
118n/a def restore_event(self, event=None):
119n/a self.text.tag_delete("paren")
120n/a self.deactivate_restore()
121n/a self.counter += 1 # disable the last timer, if there is one.
122n/a
123n/a def handle_restore_timer(self, timer_count):
124n/a if timer_count == self.counter:
125n/a self.restore_event()
126n/a
127n/a # any one of the create_tag_XXX methods can be used depending on
128n/a # the style
129n/a
130n/a def create_tag_default(self, indices):
131n/a """Highlight the single paren that matches"""
132n/a self.text.tag_add("paren", indices[0])
133n/a self.text.tag_config("paren", self.HILITE_CONFIG)
134n/a
135n/a def create_tag_expression(self, indices):
136n/a """Highlight the entire expression"""
137n/a if self.text.get(indices[1]) in (')', ']', '}'):
138n/a rightindex = indices[1]+"+1c"
139n/a else:
140n/a rightindex = indices[1]
141n/a self.text.tag_add("paren", indices[0], rightindex)
142n/a self.text.tag_config("paren", self.HILITE_CONFIG)
143n/a
144n/a # any one of the set_timeout_XXX methods can be used depending on
145n/a # the style
146n/a
147n/a def set_timeout_none(self):
148n/a """Highlight will remain until user input turns it off
149n/a or the insert has moved"""
150n/a # After CHECK_DELAY, call a function which disables the "paren" tag
151n/a # if the event is for the most recent timer and the insert has changed,
152n/a # or schedules another call for itself.
153n/a self.counter += 1
154n/a def callme(callme, self=self, c=self.counter,
155n/a index=self.text.index("insert")):
156n/a if index != self.text.index("insert"):
157n/a self.handle_restore_timer(c)
158n/a else:
159n/a self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
160n/a self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
161n/a
162n/a def set_timeout_last(self):
163n/a """The last highlight created will be removed after .5 sec"""
164n/a # associate a counter with an event; only disable the "paren"
165n/a # tag if the event is for the most recent timer.
166n/a self.counter += 1
167n/a self.editwin.text_frame.after(
168n/a self.FLASH_DELAY,
169n/a lambda self=self, c=self.counter: self.handle_restore_timer(c))
170n/a
171n/a
172n/aif __name__ == '__main__':
173n/a import unittest
174n/a unittest.main('idlelib.idle_test.test_parenmatch', verbosity=2)