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

``

`-

`

31

``

`-

`

32

``

`-

`

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

`+

`

``

30

`+

`

``

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

`