bpo-30723: IDLE -- Enhance parenmatch; add style, flash, and help (#2… · python/cpython@fae2c35 (original) (raw)
`@@ -11,43 +11,37 @@
`
11
11
`CHECK_DELAY = 100 # milliseconds
`
12
12
``
13
13
`class ParenMatch:
`
14
``
`-
"""Highlight matching parentheses
`
``
14
`+
"""Highlight matching openers and closers, (), [], and {}.
`
15
15
``
16
``
`-
There are three supported style of paren matching, based loosely
`
17
``
`-
on the Emacs options. The style is select based on the
`
18
``
`-
HILITE_STYLE attribute; it can be changed used the set_style
`
19
``
`-
method.
`
``
16
`+
There are three supported styles of paren matching. When a right
`
``
17
`+
paren (opener) is typed:
`
20
18
``
21
``
`-
The supported styles are:
`
``
19
`+
opener -- highlight the matching left paren (closer);
`
``
20
`+
parens -- highlight the left and right parens (opener and closer);
`
``
21
`+
expression -- highlight the entire expression from opener to closer.
`
``
22
`+
(For back compatibility, 'default' is a synonym for 'opener').
`
22
23
``
23
``
`-
default -- When a right paren is typed, highlight the matching
`
24
``
`-
left paren for 1/2 sec.
`
25
``
-
26
``
`-
expression -- When a right paren is typed, highlight the entire
`
27
``
`-
expression from the left paren to the right paren.
`
``
24
`+
Flash-delay is the maximum milliseconds the highlighting remains.
`
``
25
`+
Any cursor movement (key press or click) before that removes the
`
``
26
`+
highlight. If flash-delay is 0, there is no maximum.
`
28
27
``
29
28
` TODO:
`
30
``
`-
- extend IDLE with configuration dialog to change options
`
31
``
`-
- implement rest of Emacs highlight styles (see below)
`
32
``
`-
- print mismatch warning in IDLE status window
`
33
``
-
34
``
`-
Note: In Emacs, there are several styles of highlight where the
`
35
``
`-
matching paren is highlighted whenever the cursor is immediately
`
36
``
`-
to the right of a right paren. I don't know how to do that in Tk,
`
37
``
`-
so I haven't bothered.
`
``
29
`+
- Augment bell() with mismatch warning in status window.
`
``
30
`+
- Highlight when cursor is moved to the right of a closer.
`
``
31
`+
This might be too expensive to check.
`
38
32
` """
`
39
33
`menudefs = [
`
40
34
` ('edit', [
`
41
35
` ("Show surrounding parens", "<>"),
`
42
36
` ])
`
43
37
` ]
`
44
``
`-
STYLE = idleConf.GetOption('extensions','ParenMatch','style',
`
45
``
`-
default='expression')
`
46
``
`-
FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
`
47
``
`-
type='int',default=500)
`
``
38
`+
STYLE = idleConf.GetOption(
`
``
39
`+
'extensions','ParenMatch','style', default='expression')
`
``
40
`+
FLASH_DELAY = idleConf.GetOption(
`
``
41
`+
'extensions','ParenMatch','flash-delay', type='int',default=500)
`
``
42
`+
BELL = idleConf.GetOption(
`
``
43
`+
'extensions','ParenMatch','bell', type='bool',default=1)
`
48
44
`HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
`
49
``
`-
BELL = idleConf.GetOption('extensions','ParenMatch','bell',
`
50
``
`-
type='bool',default=1)
`
51
45
``
52
46
`RESTORE_VIRTUAL_EVENT_NAME = "<>"
`
53
47
`# We want the restore event be called before the usual return and
`
`@@ -69,39 +63,45 @@ def init(self, editwin):
`
69
63
`self.set_style(self.STYLE)
`
70
64
``
71
65
`def activate_restore(self):
`
``
66
`+
"Activate mechanism to restore text from highlighting."
`
72
67
`if not self.is_restore_active:
`
73
68
`for seq in self.RESTORE_SEQUENCES:
`
74
69
`self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
`
75
70
`self.is_restore_active = True
`
76
71
``
77
72
`def deactivate_restore(self):
`
``
73
`+
"Remove restore event bindings."
`
78
74
`if self.is_restore_active:
`
79
75
`for seq in self.RESTORE_SEQUENCES:
`
80
76
`self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq)
`
81
77
`self.is_restore_active = False
`
82
78
``
83
79
`def set_style(self, style):
`
``
80
`+
"Set tag and timeout functions."
`
84
81
`self.STYLE = style
`
85
``
`-
if style == "default":
`
86
``
`-
self.create_tag = self.create_tag_default
`
87
``
`-
self.set_timeout = self.set_timeout_last
`
88
``
`-
elif style == "expression":
`
89
``
`-
self.create_tag = self.create_tag_expression
`
90
``
`-
self.set_timeout = self.set_timeout_none
`
``
82
`+
self.create_tag = (
`
``
83
`+
self.create_tag_opener if style in {"opener", "default"} else
`
``
84
`+
self.create_tag_parens if style == "parens" else
`
``
85
`+
self.create_tag_expression) # "expression" or unknown
`
``
86
+
``
87
`+
self.set_timeout = (self.set_timeout_last if self.FLASH_DELAY else
`
``
88
`+
self.set_timeout_none)
`
91
89
``
92
90
`def flash_paren_event(self, event):
`
``
91
`+
"Handle editor 'show surrounding parens' event (menu or shortcut)."
`
93
92
`indices = (HyperParser(self.editwin, "insert")
`
94
93
` .get_surrounding_brackets())
`
95
94
`if indices is None:
`
96
95
`self.bell()
`
97
96
`return "break"
`
98
97
`self.activate_restore()
`
99
98
`self.create_tag(indices)
`
100
``
`-
self.set_timeout_last()
`
``
99
`+
self.set_timeout()
`
101
100
`return "break"
`
102
101
``
103
102
`def paren_closed_event(self, event):
`
104
``
`-
If it was a shortcut and not really a closing paren, quit.
`
``
103
`+
"Handle user input of closer."
`
``
104
`+
If user bound non-closer to <>, quit.
`
105
105
`closer = self.text.get("insert-1c")
`
106
106
`if closer not in _openers:
`
107
107
`return "break"
`
`@@ -118,6 +118,7 @@ def paren_closed_event(self, event):
`
118
118
`return "break"
`
119
119
``
120
120
`def restore_event(self, event=None):
`
``
121
`+
"Remove effect of doing match."
`
121
122
`self.text.tag_delete("paren")
`
122
123
`self.deactivate_restore()
`
123
124
`self.counter += 1 # disable the last timer, if there is one.
`
`@@ -129,11 +130,20 @@ def handle_restore_timer(self, timer_count):
`
129
130
`# any one of the create_tag_XXX methods can be used depending on
`
130
131
`# the style
`
131
132
``
132
``
`-
def create_tag_default(self, indices):
`
``
133
`+
def create_tag_opener(self, indices):
`
133
134
`"""Highlight the single paren that matches"""
`
134
135
`self.text.tag_add("paren", indices[0])
`
135
136
`self.text.tag_config("paren", self.HILITE_CONFIG)
`
136
137
``
``
138
`+
def create_tag_parens(self, indices):
`
``
139
`+
"""Highlight the left and right parens"""
`
``
140
`+
if self.text.get(indices[1]) in (')', ']', '}'):
`
``
141
`+
rightindex = indices[1]+"+1c"
`
``
142
`+
else:
`
``
143
`+
rightindex = indices[1]
`
``
144
`+
self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex)
`
``
145
`+
self.text.tag_config("paren", self.HILITE_CONFIG)
`
``
146
+
137
147
`def create_tag_expression(self, indices):
`
138
148
`"""Highlight the entire expression"""
`
139
149
`if self.text.get(indices[1]) in (')', ']', '}'):
`
`@@ -162,7 +172,7 @@ def callme(callme, self=self, c=self.counter,
`
162
172
`self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
`
163
173
``
164
174
`def set_timeout_last(self):
`
165
``
`-
"""The last highlight created will be removed after .5 sec"""
`
``
175
`+
"""The last highlight created will be removed after FLASH_DELAY millisecs"""
`
166
176
`# associate a counter with an event; only disable the "paren"
`
167
177
`# tag if the event is for the most recent timer.
`
168
178
`self.counter += 1
`