PyXR

c:\python24\lib \ idlelib \ ParenMatch.py



0001 """ParenMatch -- An IDLE extension for parenthesis matching.
0002 
0003 When you hit a right paren, the cursor should move briefly to the left
0004 paren.  Paren here is used generically; the matching applies to
0005 parentheses, square brackets, and curly braces.
0006 
0007 WARNING: This extension will fight with the CallTips extension,
0008 because they both are interested in the KeyRelease-parenright event.
0009 We'll have to fix IDLE to do something reasonable when two or more
0010 extensions what to capture the same event.
0011 """
0012 
0013 import PyParse
0014 from EditorWindow import EditorWindow, index2line
0015 from configHandler import idleConf
0016 
0017 class ParenMatch:
0018     """Highlight matching parentheses
0019 
0020     There are three supported style of paren matching, based loosely
0021     on the Emacs options.  The style is select based on the
0022     HILITE_STYLE attribute; it can be changed used the set_style
0023     method.
0024 
0025     The supported styles are:
0026 
0027     default -- When a right paren is typed, highlight the matching
0028         left paren for 1/2 sec.
0029 
0030     expression -- When a right paren is typed, highlight the entire
0031         expression from the left paren to the right paren.
0032 
0033     TODO:
0034         - fix interaction with CallTips
0035         - extend IDLE with configuration dialog to change options
0036         - implement rest of Emacs highlight styles (see below)
0037         - print mismatch warning in IDLE status window
0038 
0039     Note: In Emacs, there are several styles of highlight where the
0040     matching paren is highlighted whenever the cursor is immediately
0041     to the right of a right paren.  I don't know how to do that in Tk,
0042     so I haven't bothered.
0043     """
0044     menudefs = []
0045     STYLE = idleConf.GetOption('extensions','ParenMatch','style',
0046             default='expression')
0047     FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
0048             type='int',default=500)
0049     HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
0050     BELL = idleConf.GetOption('extensions','ParenMatch','bell',
0051             type='bool',default=1)
0052 
0053     def __init__(self, editwin):
0054         self.editwin = editwin
0055         self.text = editwin.text
0056         self.finder = LastOpenBracketFinder(editwin)
0057         self.counter = 0
0058         self._restore = None
0059         self.set_style(self.STYLE)
0060 
0061     def set_style(self, style):
0062         self.STYLE = style
0063         if style == "default":
0064             self.create_tag = self.create_tag_default
0065             self.set_timeout = self.set_timeout_last
0066         elif style == "expression":
0067             self.create_tag = self.create_tag_expression
0068             self.set_timeout = self.set_timeout_none
0069 
0070     def flash_open_paren_event(self, event):
0071         index = self.finder.find(keysym_type(event.keysym))
0072         if index is None:
0073             self.warn_mismatched()
0074             return
0075         self._restore = 1
0076         self.create_tag(index)
0077         self.set_timeout()
0078 
0079     def check_restore_event(self, event=None):
0080         if self._restore:
0081             self.text.tag_delete("paren")
0082             self._restore = None
0083 
0084     def handle_restore_timer(self, timer_count):
0085         if timer_count + 1 == self.counter:
0086             self.check_restore_event()
0087 
0088     def warn_mismatched(self):
0089         if self.BELL:
0090             self.text.bell()
0091 
0092     # any one of the create_tag_XXX methods can be used depending on
0093     # the style
0094 
0095     def create_tag_default(self, index):
0096         """Highlight the single paren that matches"""
0097         self.text.tag_add("paren", index)
0098         self.text.tag_config("paren", self.HILITE_CONFIG)
0099 
0100     def create_tag_expression(self, index):
0101         """Highlight the entire expression"""
0102         self.text.tag_add("paren", index, "insert")
0103         self.text.tag_config("paren", self.HILITE_CONFIG)
0104 
0105     # any one of the set_timeout_XXX methods can be used depending on
0106     # the style
0107 
0108     def set_timeout_none(self):
0109         """Highlight will remain until user input turns it off"""
0110         pass
0111 
0112     def set_timeout_last(self):
0113         """The last highlight created will be removed after .5 sec"""
0114         # associate a counter with an event; only disable the "paren"
0115         # tag if the event is for the most recent timer.
0116         self.editwin.text_frame.after(self.FLASH_DELAY,
0117                                       lambda self=self, c=self.counter: \
0118                                       self.handle_restore_timer(c))
0119         self.counter = self.counter + 1
0120 
0121 def keysym_type(ks):
0122     # Not all possible chars or keysyms are checked because of the
0123     # limited context in which the function is used.
0124     if ks == "parenright" or ks == "(":
0125         return "paren"
0126     if ks == "bracketright" or ks == "[":
0127         return "bracket"
0128     if ks == "braceright" or ks == "{":
0129         return "brace"
0130 
0131 class LastOpenBracketFinder:
0132     num_context_lines = EditorWindow.num_context_lines
0133     indentwidth = EditorWindow.indentwidth
0134     tabwidth = EditorWindow.tabwidth
0135     context_use_ps1 = EditorWindow.context_use_ps1
0136 
0137     def __init__(self, editwin):
0138         self.editwin = editwin
0139         self.text = editwin.text
0140 
0141     def _find_offset_in_buf(self, lno):
0142         y = PyParse.Parser(self.indentwidth, self.tabwidth)
0143         for context in self.num_context_lines:
0144             startat = max(lno - context, 1)
0145             startatindex = repr(startat) + ".0"
0146             # rawtext needs to contain everything up to the last
0147             # character, which was the close paren.  the parser also
0148             # requires that the last line ends with "\n"
0149             rawtext = self.text.get(startatindex, "insert")[:-1] + "\n"
0150             y.set_str(rawtext)
0151             bod = y.find_good_parse_start(
0152                         self.context_use_ps1,
0153                         self._build_char_in_string_func(startatindex))
0154             if bod is not None or startat == 1:
0155                 break
0156         y.set_lo(bod or 0)
0157         i = y.get_last_open_bracket_pos()
0158         return i, y.str
0159 
0160     def find(self, right_keysym_type):
0161         """Return the location of the last open paren"""
0162         lno = index2line(self.text.index("insert"))
0163         i, buf = self._find_offset_in_buf(lno)
0164         if i is None \
0165            or keysym_type(buf[i]) != right_keysym_type:
0166             return None
0167         lines_back = buf[i:].count("\n") - 1
0168         # subtract one for the "\n" added to please the parser
0169         upto_open = buf[:i]
0170         j = upto_open.rfind("\n") + 1 # offset of column 0 of line
0171         offset = i - j
0172         return "%d.%d" % (lno - lines_back, offset)
0173 
0174     def _build_char_in_string_func(self, startindex):
0175         def inner(offset, startindex=startindex,
0176                   icis=self.editwin.is_char_in_string):
0177             return icis(startindex + "%dc" % offset)
0178         return inner
0179 

Generated by PyXR 0.9.4
SourceForge.net Logo