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