Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v9 / lib / python2.4 / idlelib / ParenMatch.py
CommitLineData
920dae64
AT
1"""ParenMatch -- An IDLE extension for parenthesis matching.
2
3When you hit a right paren, the cursor should move briefly to the left
4paren. Paren here is used generically; the matching applies to
5parentheses, square brackets, and curly braces.
6
7WARNING: This extension will fight with the CallTips extension,
8because they both are interested in the KeyRelease-parenright event.
9We'll have to fix IDLE to do something reasonable when two or more
10extensions what to capture the same event.
11"""
12
13import PyParse
14from EditorWindow import EditorWindow, index2line
15from configHandler import idleConf
16
17class ParenMatch:
18 """Highlight matching parentheses
19
20 There are three supported style of paren matching, based loosely
21 on the Emacs options. The style is select based on the
22 HILITE_STYLE attribute; it can be changed used the set_style
23 method.
24
25 The supported styles are:
26
27 default -- When a right paren is typed, highlight the matching
28 left paren for 1/2 sec.
29
30 expression -- When a right paren is typed, highlight the entire
31 expression from the left paren to the right paren.
32
33 TODO:
34 - fix interaction with CallTips
35 - extend IDLE with configuration dialog to change options
36 - implement rest of Emacs highlight styles (see below)
37 - print mismatch warning in IDLE status window
38
39 Note: In Emacs, there are several styles of highlight where the
40 matching paren is highlighted whenever the cursor is immediately
41 to the right of a right paren. I don't know how to do that in Tk,
42 so I haven't bothered.
43 """
44 menudefs = []
45 STYLE = idleConf.GetOption('extensions','ParenMatch','style',
46 default='expression')
47 FLASH_DELAY = idleConf.GetOption('extensions','ParenMatch','flash-delay',
48 type='int',default=500)
49 HILITE_CONFIG = idleConf.GetHighlight(idleConf.CurrentTheme(),'hilite')
50 BELL = idleConf.GetOption('extensions','ParenMatch','bell',
51 type='bool',default=1)
52
53 def __init__(self, editwin):
54 self.editwin = editwin
55 self.text = editwin.text
56 self.finder = LastOpenBracketFinder(editwin)
57 self.counter = 0
58 self._restore = None
59 self.set_style(self.STYLE)
60
61 def set_style(self, style):
62 self.STYLE = style
63 if style == "default":
64 self.create_tag = self.create_tag_default
65 self.set_timeout = self.set_timeout_last
66 elif style == "expression":
67 self.create_tag = self.create_tag_expression
68 self.set_timeout = self.set_timeout_none
69
70 def flash_open_paren_event(self, event):
71 index = self.finder.find(keysym_type(event.keysym))
72 if index is None:
73 self.warn_mismatched()
74 return
75 self._restore = 1
76 self.create_tag(index)
77 self.set_timeout()
78
79 def check_restore_event(self, event=None):
80 if self._restore:
81 self.text.tag_delete("paren")
82 self._restore = None
83
84 def handle_restore_timer(self, timer_count):
85 if timer_count + 1 == self.counter:
86 self.check_restore_event()
87
88 def warn_mismatched(self):
89 if self.BELL:
90 self.text.bell()
91
92 # any one of the create_tag_XXX methods can be used depending on
93 # the style
94
95 def create_tag_default(self, index):
96 """Highlight the single paren that matches"""
97 self.text.tag_add("paren", index)
98 self.text.tag_config("paren", self.HILITE_CONFIG)
99
100 def create_tag_expression(self, index):
101 """Highlight the entire expression"""
102 self.text.tag_add("paren", index, "insert")
103 self.text.tag_config("paren", self.HILITE_CONFIG)
104
105 # any one of the set_timeout_XXX methods can be used depending on
106 # the style
107
108 def set_timeout_none(self):
109 """Highlight will remain until user input turns it off"""
110 pass
111
112 def set_timeout_last(self):
113 """The last highlight created will be removed after .5 sec"""
114 # associate a counter with an event; only disable the "paren"
115 # tag if the event is for the most recent timer.
116 self.editwin.text_frame.after(self.FLASH_DELAY,
117 lambda self=self, c=self.counter: \
118 self.handle_restore_timer(c))
119 self.counter = self.counter + 1
120
121def keysym_type(ks):
122 # Not all possible chars or keysyms are checked because of the
123 # limited context in which the function is used.
124 if ks == "parenright" or ks == "(":
125 return "paren"
126 if ks == "bracketright" or ks == "[":
127 return "bracket"
128 if ks == "braceright" or ks == "{":
129 return "brace"
130
131class LastOpenBracketFinder:
132 num_context_lines = EditorWindow.num_context_lines
133 indentwidth = EditorWindow.indentwidth
134 tabwidth = EditorWindow.tabwidth
135 context_use_ps1 = EditorWindow.context_use_ps1
136
137 def __init__(self, editwin):
138 self.editwin = editwin
139 self.text = editwin.text
140
141 def _find_offset_in_buf(self, lno):
142 y = PyParse.Parser(self.indentwidth, self.tabwidth)
143 for context in self.num_context_lines:
144 startat = max(lno - context, 1)
145 startatindex = repr(startat) + ".0"
146 # rawtext needs to contain everything up to the last
147 # character, which was the close paren. the parser also
148 # requires that the last line ends with "\n"
149 rawtext = self.text.get(startatindex, "insert")[:-1] + "\n"
150 y.set_str(rawtext)
151 bod = y.find_good_parse_start(
152 self.context_use_ps1,
153 self._build_char_in_string_func(startatindex))
154 if bod is not None or startat == 1:
155 break
156 y.set_lo(bod or 0)
157 i = y.get_last_open_bracket_pos()
158 return i, y.str
159
160 def find(self, right_keysym_type):
161 """Return the location of the last open paren"""
162 lno = index2line(self.text.index("insert"))
163 i, buf = self._find_offset_in_buf(lno)
164 if i is None \
165 or keysym_type(buf[i]) != right_keysym_type:
166 return None
167 lines_back = buf[i:].count("\n") - 1
168 # subtract one for the "\n" added to please the parser
169 upto_open = buf[:i]
170 j = upto_open.rfind("\n") + 1 # offset of column 0 of line
171 offset = i - j
172 return "%d.%d" % (lno - lines_back, offset)
173
174 def _build_char_in_string_func(self, startindex):
175 def inner(offset, startindex=startindex,
176 icis=self.editwin.is_char_in_string):
177 return icis(startindex + "%dc" % offset)
178 return inner