bpo-30723: Enhancement to IDLE parenmatch extension by wohlganger · Pull Request #2306 · python/cpython
class ParenMatch: """Highlight matching parentheses """Highlight matching openers and closers, (), [], and {}.
There are three supported style of paren matching, based loosely on the Emacs options. The style is select based on the HILITE_STYLE attribute; it can be changed used the set_style method. There are three supported styles of paren matching. When a right paren (opener) is typed:
The supported styles are: opener -- highlight the matching left paren (closer); parens -- highlight the left and right parens (opener and closer); expression -- highlight the entire expression from opener to closer. (For back compatibility, 'default' is a synonym for 'opener').
default -- When a right paren is typed, highlight the matching left paren for 1/2 sec.
expression -- When a right paren is typed, highlight the entire expression from the left paren to the right paren. Flash-delay is the maximum milliseconds the highlighting remains. Any cursor movement (key press or click) before that removes the highlight. If flash-delay is 0, there is no maximum.
TODO: - extend IDLE with configuration dialog to change options - implement rest of Emacs highlight styles (see below) - print mismatch warning in IDLE status window
Note: In Emacs, there are several styles of highlight where the matching paren is highlighted whenever the cursor is immediately to the right of a right paren. I don't know how to do that in Tk, so I haven't bothered. - Augment bell() with mismatch warning in status window. - Highlight when cursor is moved to the right of a closer. This might be too expensive to check. """ menudefs = [ ('edit', [ ("Show surrounding parens", "<<flash-paren>>"), ]) ] STYLE = idleConf.GetOption('extensions','ParenMatch','style', default='expression') FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay', type='int',default=500) STYLE = idleConf.GetOption( 'extensions','ParenMatch','style', default='expression') FLASH_DELAY = idleConf.GetOption( 'extensions','ParenMatch','flash-delay', type='int',default=500) BELL = idleConf.GetOption( 'extensions','ParenMatch','bell', type='bool',default=1) HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite') BELL = idleConf.GetOption('extensions','ParenMatch','bell', type='bool',default=1)
RESTORE_VIRTUAL_EVENT_NAME = "<<parenmatch-check-restore>>" # We want the restore event be called before the usual return and
def activate_restore(self): "Activate mechanism to restore text from highlighting." if not self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_add(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = True
def deactivate_restore(self): "Remove restore event bindings." if self.is_restore_active: for seq in self.RESTORE_SEQUENCES: self.text.event_delete(self.RESTORE_VIRTUAL_EVENT_NAME, seq) self.is_restore_active = False
def set_style(self, style): "Set tag and timeout functions." self.STYLE = style if style == "default": self.create_tag = self.create_tag_default self.set_timeout = self.set_timeout_last elif style == "expression": self.create_tag = self.create_tag_expression self.set_timeout = self.set_timeout_none self.create_tag = ( self.create_tag_opener if style in {"opener", "default"} else self.create_tag_parens if style == "parens" else self.create_tag_expression) # "expression" or unknown
self.set_timeout = (self.set_timeout_last if self.FLASH_DELAY else self.set_timeout_none)
def flash_paren_event(self, event): "Handle editor 'show surrounding parens' event (menu or shortcut)." indices = (HyperParser(self.editwin, "insert") .get_surrounding_brackets()) if indices is None: self.bell() return "break" self.activate_restore() self.create_tag(indices) self.set_timeout_last() self.set_timeout() return "break"
def paren_closed_event(self, event): # If it was a shortcut and not really a closing paren, quit. "Handle user input of closer." # If user bound non-closer to <<paren-closed>>, quit. closer = self.text.get("insert-1c") if closer not in _openers: return "break"
def restore_event(self, event=None): "Remove effect of doing match." self.text.tag_delete("paren") self.deactivate_restore() self.counter += 1 # disable the last timer, if there is one.
def create_tag_default(self, indices): def create_tag_opener(self, indices): """Highlight the single paren that matches""" self.text.tag_add("paren", indices[0]) self.text.tag_config("paren", self.HILITE_CONFIG)
def create_tag_parens(self, indices): """Highlight the left and right parens""" if self.text.get(indices[1]) in (')', ']', '}'): rightindex = indices[1]+"+1c" else: rightindex = indices[1] self.text.tag_add("paren", indices[0], indices[0]+"+1c", rightindex+"-1c", rightindex) self.text.tag_config("paren", self.HILITE_CONFIG)
def create_tag_expression(self, indices): """Highlight the entire expression""" if self.text.get(indices[1]) in (')', ']', '}'):
def set_timeout_last(self): """The last highlight created will be removed after .5 sec""" """The last highlight created will be removed after FLASH_DELAY millisecs""" # associate a counter with an event; only disable the "paren" # tag if the event is for the most recent timer. self.counter += 1