##// END OF EJS Templates
Fix some uses of map() in Qt console completion code.
Thomas Kluyver -
Show More
@@ -1,371 +1,371 b''
1 """a navigable completer for the qtconsole"""
1 """a navigable completer for the qtconsole"""
2 # coding : utf-8
2 # coding : utf-8
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (c) 2012, IPython Development Team.$
4 # Copyright (c) 2012, IPython Development Team.$
5 #
5 #
6 # Distributed under the terms of the Modified BSD License.$
6 # Distributed under the terms of the Modified BSD License.$
7 #
7 #
8 # The full license is in the file COPYING.txt, distributed with this software.
8 # The full license is in the file COPYING.txt, distributed with this software.
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10
10
11 # System library imports
11 # System library imports
12 import IPython.utils.text as text
12 import IPython.utils.text as text
13
13
14 from IPython.external.qt import QtCore, QtGui
14 from IPython.external.qt import QtCore, QtGui
15
15
16 #--------------------------------------------------------------------------
16 #--------------------------------------------------------------------------
17 # Return an HTML table with selected item in a special class
17 # Return an HTML table with selected item in a special class
18 #--------------------------------------------------------------------------
18 #--------------------------------------------------------------------------
19 def html_tableify(item_matrix, select=None, header=None , footer=None) :
19 def html_tableify(item_matrix, select=None, header=None , footer=None) :
20 """ returnr a string for an html table"""
20 """ returnr a string for an html table"""
21 if not item_matrix :
21 if not item_matrix :
22 return ''
22 return ''
23 html_cols = []
23 html_cols = []
24 tds = lambda text : u'<td>'+text+u' </td>'
24 tds = lambda text : u'<td>'+text+u' </td>'
25 trs = lambda text : u'<tr>'+text+u'</tr>'
25 trs = lambda text : u'<tr>'+text+u'</tr>'
26 tds_items = [map(tds, row) for row in item_matrix]
26 tds_items = [list(map(tds, row)) for row in item_matrix]
27 if select :
27 if select :
28 row, col = select
28 row, col = select
29 tds_items[row][col] = u'<td class="inverted">'\
29 tds_items[row][col] = u'<td class="inverted">'\
30 +item_matrix[row][col]\
30 +item_matrix[row][col]\
31 +u' </td>'
31 +u' </td>'
32 #select the right item
32 #select the right item
33 html_cols = map(trs, (u''.join(row) for row in tds_items))
33 html_cols = map(trs, (u''.join(row) for row in tds_items))
34 head = ''
34 head = ''
35 foot = ''
35 foot = ''
36 if header :
36 if header :
37 head = (u'<tr>'\
37 head = (u'<tr>'\
38 +''.join((u'<td>'+header+u'</td>')*len(item_matrix[0]))\
38 +''.join((u'<td>'+header+u'</td>')*len(item_matrix[0]))\
39 +'</tr>')
39 +'</tr>')
40
40
41 if footer :
41 if footer :
42 foot = (u'<tr>'\
42 foot = (u'<tr>'\
43 +''.join((u'<td>'+footer+u'</td>')*len(item_matrix[0]))\
43 +''.join((u'<td>'+footer+u'</td>')*len(item_matrix[0]))\
44 +'</tr>')
44 +'</tr>')
45 html = (u'<table class="completion" style="white-space:pre">'+head+(u''.join(html_cols))+foot+u'</table>')
45 html = (u'<table class="completion" style="white-space:pre">'+head+(u''.join(html_cols))+foot+u'</table>')
46 return html
46 return html
47
47
48 class SlidingInterval(object):
48 class SlidingInterval(object):
49 """a bound interval that follows a cursor
49 """a bound interval that follows a cursor
50
50
51 internally used to scoll the completion view when the cursor
51 internally used to scoll the completion view when the cursor
52 try to go beyond the edges, and show '...' when rows are hidden
52 try to go beyond the edges, and show '...' when rows are hidden
53 """
53 """
54
54
55 _min = 0
55 _min = 0
56 _max = 1
56 _max = 1
57 _current = 0
57 _current = 0
58 def __init__(self, maximum=1, width=6, minimum=0, sticky_lenght=1):
58 def __init__(self, maximum=1, width=6, minimum=0, sticky_lenght=1):
59 """Create a new bounded interval
59 """Create a new bounded interval
60
60
61 any value return by this will be bound between maximum and
61 any value return by this will be bound between maximum and
62 minimum. usual width will be 'width', and sticky_length
62 minimum. usual width will be 'width', and sticky_length
63 set when the return interval should expand to max and min
63 set when the return interval should expand to max and min
64 """
64 """
65 self._min = minimum
65 self._min = minimum
66 self._max = maximum
66 self._max = maximum
67 self._start = 0
67 self._start = 0
68 self._width = width
68 self._width = width
69 self._stop = self._start+self._width+1
69 self._stop = self._start+self._width+1
70 self._sticky_lenght = sticky_lenght
70 self._sticky_lenght = sticky_lenght
71
71
72 @property
72 @property
73 def current(self):
73 def current(self):
74 """current cursor position"""
74 """current cursor position"""
75 return self._current
75 return self._current
76
76
77 @current.setter
77 @current.setter
78 def current(self, value):
78 def current(self, value):
79 """set current cursor position"""
79 """set current cursor position"""
80 current = min(max(self._min, value), self._max)
80 current = min(max(self._min, value), self._max)
81
81
82 self._current = current
82 self._current = current
83
83
84 if current > self._stop :
84 if current > self._stop :
85 self._stop = current
85 self._stop = current
86 self._start = current-self._width
86 self._start = current-self._width
87 elif current < self._start :
87 elif current < self._start :
88 self._start = current
88 self._start = current
89 self._stop = current + self._width
89 self._stop = current + self._width
90
90
91 if abs(self._start - self._min) <= self._sticky_lenght :
91 if abs(self._start - self._min) <= self._sticky_lenght :
92 self._start = self._min
92 self._start = self._min
93
93
94 if abs(self._stop - self._max) <= self._sticky_lenght :
94 if abs(self._stop - self._max) <= self._sticky_lenght :
95 self._stop = self._max
95 self._stop = self._max
96
96
97 @property
97 @property
98 def start(self):
98 def start(self):
99 """begiiing of interval to show"""
99 """begiiing of interval to show"""
100 return self._start
100 return self._start
101
101
102 @property
102 @property
103 def stop(self):
103 def stop(self):
104 """end of interval to show"""
104 """end of interval to show"""
105 return self._stop
105 return self._stop
106
106
107 @property
107 @property
108 def width(self):
108 def width(self):
109 return self._stop - self._start
109 return self._stop - self._start
110
110
111 @property
111 @property
112 def nth(self):
112 def nth(self):
113 return self.current - self.start
113 return self.current - self.start
114
114
115 class CompletionHtml(QtGui.QWidget):
115 class CompletionHtml(QtGui.QWidget):
116 """ A widget for tab completion, navigable by arrow keys """
116 """ A widget for tab completion, navigable by arrow keys """
117
117
118 #--------------------------------------------------------------------------
118 #--------------------------------------------------------------------------
119 # 'QObject' interface
119 # 'QObject' interface
120 #--------------------------------------------------------------------------
120 #--------------------------------------------------------------------------
121
121
122 _items = ()
122 _items = ()
123 _index = (0, 0)
123 _index = (0, 0)
124 _consecutive_tab = 0
124 _consecutive_tab = 0
125 _size = (1, 1)
125 _size = (1, 1)
126 _old_cursor = None
126 _old_cursor = None
127 _start_position = 0
127 _start_position = 0
128 _slice_start = 0
128 _slice_start = 0
129 _slice_len = 4
129 _slice_len = 4
130
130
131 def __init__(self, console_widget):
131 def __init__(self, console_widget):
132 """ Create a completion widget that is attached to the specified Qt
132 """ Create a completion widget that is attached to the specified Qt
133 text edit widget.
133 text edit widget.
134 """
134 """
135 assert isinstance(console_widget._control, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
135 assert isinstance(console_widget._control, (QtGui.QTextEdit, QtGui.QPlainTextEdit))
136 super(CompletionHtml, self).__init__()
136 super(CompletionHtml, self).__init__()
137
137
138 self._text_edit = console_widget._control
138 self._text_edit = console_widget._control
139 self._console_widget = console_widget
139 self._console_widget = console_widget
140 self._text_edit.installEventFilter(self)
140 self._text_edit.installEventFilter(self)
141 self._sliding_interval = None
141 self._sliding_interval = None
142 self._justified_items = None
142 self._justified_items = None
143
143
144 # Ensure that the text edit keeps focus when widget is displayed.
144 # Ensure that the text edit keeps focus when widget is displayed.
145 self.setFocusProxy(self._text_edit)
145 self.setFocusProxy(self._text_edit)
146
146
147
147
148 def eventFilter(self, obj, event):
148 def eventFilter(self, obj, event):
149 """ Reimplemented to handle keyboard input and to auto-hide when the
149 """ Reimplemented to handle keyboard input and to auto-hide when the
150 text edit loses focus.
150 text edit loses focus.
151 """
151 """
152 if obj == self._text_edit:
152 if obj == self._text_edit:
153 etype = event.type()
153 etype = event.type()
154 if etype == QtCore.QEvent.KeyPress:
154 if etype == QtCore.QEvent.KeyPress:
155 key = event.key()
155 key = event.key()
156 if self._consecutive_tab == 0 and key in (QtCore.Qt.Key_Tab,):
156 if self._consecutive_tab == 0 and key in (QtCore.Qt.Key_Tab,):
157 return False
157 return False
158 elif self._consecutive_tab == 1 and key in (QtCore.Qt.Key_Tab,):
158 elif self._consecutive_tab == 1 and key in (QtCore.Qt.Key_Tab,):
159 # ok , called twice, we grab focus, and show the cursor
159 # ok , called twice, we grab focus, and show the cursor
160 self._consecutive_tab = self._consecutive_tab+1
160 self._consecutive_tab = self._consecutive_tab+1
161 self._update_list()
161 self._update_list()
162 return True
162 return True
163 elif self._consecutive_tab == 2:
163 elif self._consecutive_tab == 2:
164 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
164 if key in (QtCore.Qt.Key_Return, QtCore.Qt.Key_Enter):
165 self._complete_current()
165 self._complete_current()
166 return True
166 return True
167 if key in (QtCore.Qt.Key_Tab,):
167 if key in (QtCore.Qt.Key_Tab,):
168 self.select_right()
168 self.select_right()
169 self._update_list()
169 self._update_list()
170 return True
170 return True
171 elif key in ( QtCore.Qt.Key_Down,):
171 elif key in ( QtCore.Qt.Key_Down,):
172 self.select_down()
172 self.select_down()
173 self._update_list()
173 self._update_list()
174 return True
174 return True
175 elif key in (QtCore.Qt.Key_Right,):
175 elif key in (QtCore.Qt.Key_Right,):
176 self.select_right()
176 self.select_right()
177 self._update_list()
177 self._update_list()
178 return True
178 return True
179 elif key in ( QtCore.Qt.Key_Up,):
179 elif key in ( QtCore.Qt.Key_Up,):
180 self.select_up()
180 self.select_up()
181 self._update_list()
181 self._update_list()
182 return True
182 return True
183 elif key in ( QtCore.Qt.Key_Left,):
183 elif key in ( QtCore.Qt.Key_Left,):
184 self.select_left()
184 self.select_left()
185 self._update_list()
185 self._update_list()
186 return True
186 return True
187 elif key in ( QtCore.Qt.Key_Escape,):
187 elif key in ( QtCore.Qt.Key_Escape,):
188 self.cancel_completion()
188 self.cancel_completion()
189 return True
189 return True
190 else :
190 else :
191 self.cancel_completion()
191 self.cancel_completion()
192 else:
192 else:
193 self.cancel_completion()
193 self.cancel_completion()
194
194
195 elif etype == QtCore.QEvent.FocusOut:
195 elif etype == QtCore.QEvent.FocusOut:
196 self.cancel_completion()
196 self.cancel_completion()
197
197
198 return super(CompletionHtml, self).eventFilter(obj, event)
198 return super(CompletionHtml, self).eventFilter(obj, event)
199
199
200 #--------------------------------------------------------------------------
200 #--------------------------------------------------------------------------
201 # 'CompletionHtml' interface
201 # 'CompletionHtml' interface
202 #--------------------------------------------------------------------------
202 #--------------------------------------------------------------------------
203 def cancel_completion(self):
203 def cancel_completion(self):
204 """Cancel the completion
204 """Cancel the completion
205
205
206 should be called when the completer have to be dismissed
206 should be called when the completer have to be dismissed
207
207
208 This reset internal variable, clearing the temporary buffer
208 This reset internal variable, clearing the temporary buffer
209 of the console where the completion are shown.
209 of the console where the completion are shown.
210 """
210 """
211 self._consecutive_tab = 0
211 self._consecutive_tab = 0
212 self._slice_start = 0
212 self._slice_start = 0
213 self._console_widget._clear_temporary_buffer()
213 self._console_widget._clear_temporary_buffer()
214 self._index = (0, 0)
214 self._index = (0, 0)
215 if(self._sliding_interval):
215 if(self._sliding_interval):
216 self._sliding_interval = None
216 self._sliding_interval = None
217
217
218 #
218 #
219 # ... 2 4 4 4 4 4 4 4 4 4 4 4 4
219 # ... 2 4 4 4 4 4 4 4 4 4 4 4 4
220 # 2 2 4 4 4 4 4 4 4 4 4 4 4 4
220 # 2 2 4 4 4 4 4 4 4 4 4 4 4 4
221 #
221 #
222 #2 2 x x x x x x x x x x x 5 5
222 #2 2 x x x x x x x x x x x 5 5
223 #6 6 x x x x x x x x x x x 5 5
223 #6 6 x x x x x x x x x x x 5 5
224 #6 6 x x x x x x x x x x ? 5 5
224 #6 6 x x x x x x x x x x ? 5 5
225 #6 6 x x x x x x x x x x ? 1 1
225 #6 6 x x x x x x x x x x ? 1 1
226 #
226 #
227 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
227 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
228 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
228 #3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 ...
229 def _select_index(self, row, col):
229 def _select_index(self, row, col):
230 """Change the selection index, and make sure it stays in the right range
230 """Change the selection index, and make sure it stays in the right range
231
231
232 A little more complicated than just dooing modulo the number of row columns
232 A little more complicated than just dooing modulo the number of row columns
233 to be sure to cycle through all element.
233 to be sure to cycle through all element.
234
234
235 horizontaly, the element are maped like this :
235 horizontaly, the element are maped like this :
236 to r <-- a b c d e f --> to g
236 to r <-- a b c d e f --> to g
237 to f <-- g h i j k l --> to m
237 to f <-- g h i j k l --> to m
238 to l <-- m n o p q r --> to a
238 to l <-- m n o p q r --> to a
239
239
240 and vertically
240 and vertically
241 a d g j m p
241 a d g j m p
242 b e h k n q
242 b e h k n q
243 c f i l o r
243 c f i l o r
244 """
244 """
245
245
246 nr, nc = self._size
246 nr, nc = self._size
247 nr = nr-1
247 nr = nr-1
248 nc = nc-1
248 nc = nc-1
249
249
250 # case 1
250 # case 1
251 if (row > nr and col >= nc) or (row >= nr and col > nc):
251 if (row > nr and col >= nc) or (row >= nr and col > nc):
252 self._select_index(0, 0)
252 self._select_index(0, 0)
253 # case 2
253 # case 2
254 elif (row <= 0 and col < 0) or (row < 0 and col <= 0):
254 elif (row <= 0 and col < 0) or (row < 0 and col <= 0):
255 self._select_index(nr, nc)
255 self._select_index(nr, nc)
256 # case 3
256 # case 3
257 elif row > nr :
257 elif row > nr :
258 self._select_index(0, col+1)
258 self._select_index(0, col+1)
259 # case 4
259 # case 4
260 elif row < 0 :
260 elif row < 0 :
261 self._select_index(nr, col-1)
261 self._select_index(nr, col-1)
262 # case 5
262 # case 5
263 elif col > nc :
263 elif col > nc :
264 self._select_index(row+1, 0)
264 self._select_index(row+1, 0)
265 # case 6
265 # case 6
266 elif col < 0 :
266 elif col < 0 :
267 self._select_index(row-1, nc)
267 self._select_index(row-1, nc)
268 elif 0 <= row and row <= nr and 0 <= col and col <= nc :
268 elif 0 <= row and row <= nr and 0 <= col and col <= nc :
269 self._index = (row, col)
269 self._index = (row, col)
270 else :
270 else :
271 raise NotImplementedError("you'r trying to go where no completion\
271 raise NotImplementedError("you'r trying to go where no completion\
272 have gone before : %d:%d (%d:%d)"%(row, col, nr, nc) )
272 have gone before : %d:%d (%d:%d)"%(row, col, nr, nc) )
273
273
274
274
275 @property
275 @property
276 def _slice_end(self):
276 def _slice_end(self):
277 end = self._slice_start+self._slice_len
277 end = self._slice_start+self._slice_len
278 if end > len(self._items) :
278 if end > len(self._items) :
279 return None
279 return None
280 return end
280 return end
281
281
282 def select_up(self):
282 def select_up(self):
283 """move cursor up"""
283 """move cursor up"""
284 r, c = self._index
284 r, c = self._index
285 self._select_index(r-1, c)
285 self._select_index(r-1, c)
286
286
287 def select_down(self):
287 def select_down(self):
288 """move cursor down"""
288 """move cursor down"""
289 r, c = self._index
289 r, c = self._index
290 self._select_index(r+1, c)
290 self._select_index(r+1, c)
291
291
292 def select_left(self):
292 def select_left(self):
293 """move cursor left"""
293 """move cursor left"""
294 r, c = self._index
294 r, c = self._index
295 self._select_index(r, c-1)
295 self._select_index(r, c-1)
296
296
297 def select_right(self):
297 def select_right(self):
298 """move cursor right"""
298 """move cursor right"""
299 r, c = self._index
299 r, c = self._index
300 self._select_index(r, c+1)
300 self._select_index(r, c+1)
301
301
302 def show_items(self, cursor, items):
302 def show_items(self, cursor, items):
303 """ Shows the completion widget with 'items' at the position specified
303 """ Shows the completion widget with 'items' at the position specified
304 by 'cursor'.
304 by 'cursor'.
305 """
305 """
306 if not items :
306 if not items :
307 return
307 return
308 self._start_position = cursor.position()
308 self._start_position = cursor.position()
309 self._consecutive_tab = 1
309 self._consecutive_tab = 1
310 items_m, ci = text.compute_item_matrix(items, empty=' ')
310 items_m, ci = text.compute_item_matrix(items, empty=' ')
311 self._sliding_interval = SlidingInterval(len(items_m)-1)
311 self._sliding_interval = SlidingInterval(len(items_m)-1)
312
312
313 self._items = items_m
313 self._items = items_m
314 self._size = (ci['rows_numbers'], ci['columns_numbers'])
314 self._size = (ci['rows_numbers'], ci['columns_numbers'])
315 self._old_cursor = cursor
315 self._old_cursor = cursor
316 self._index = (0, 0)
316 self._index = (0, 0)
317 sjoin = lambda x : [ y.ljust(w, ' ') for y, w in zip(x, ci['columns_width'])]
317 sjoin = lambda x : [ y.ljust(w, ' ') for y, w in zip(x, ci['columns_width'])]
318 self._justified_items = map(sjoin, items_m)
318 self._justified_items = list(map(sjoin, items_m))
319 self._update_list(hilight=False)
319 self._update_list(hilight=False)
320
320
321
321
322
322
323
323
324 def _update_list(self, hilight=True):
324 def _update_list(self, hilight=True):
325 """ update the list of completion and hilight the currently selected completion """
325 """ update the list of completion and hilight the currently selected completion """
326 self._sliding_interval.current = self._index[0]
326 self._sliding_interval.current = self._index[0]
327 head = None
327 head = None
328 foot = None
328 foot = None
329 if self._sliding_interval.start > 0 :
329 if self._sliding_interval.start > 0 :
330 head = '...'
330 head = '...'
331
331
332 if self._sliding_interval.stop < self._sliding_interval._max:
332 if self._sliding_interval.stop < self._sliding_interval._max:
333 foot = '...'
333 foot = '...'
334 items_m = self._justified_items[\
334 items_m = self._justified_items[\
335 self._sliding_interval.start:\
335 self._sliding_interval.start:\
336 self._sliding_interval.stop+1\
336 self._sliding_interval.stop+1\
337 ]
337 ]
338
338
339 self._console_widget._clear_temporary_buffer()
339 self._console_widget._clear_temporary_buffer()
340 if(hilight):
340 if(hilight):
341 sel = (self._sliding_interval.nth, self._index[1])
341 sel = (self._sliding_interval.nth, self._index[1])
342 else :
342 else :
343 sel = None
343 sel = None
344
344
345 strng = html_tableify(items_m, select=sel, header=head, footer=foot)
345 strng = html_tableify(items_m, select=sel, header=head, footer=foot)
346 self._console_widget._fill_temporary_buffer(self._old_cursor, strng, html=True)
346 self._console_widget._fill_temporary_buffer(self._old_cursor, strng, html=True)
347
347
348 #--------------------------------------------------------------------------
348 #--------------------------------------------------------------------------
349 # Protected interface
349 # Protected interface
350 #--------------------------------------------------------------------------
350 #--------------------------------------------------------------------------
351
351
352 def _complete_current(self):
352 def _complete_current(self):
353 """ Perform the completion with the currently selected item.
353 """ Perform the completion with the currently selected item.
354 """
354 """
355 i = self._index
355 i = self._index
356 item = self._items[i[0]][i[1]]
356 item = self._items[i[0]][i[1]]
357 item = item.strip()
357 item = item.strip()
358 if item :
358 if item :
359 self._current_text_cursor().insertText(item)
359 self._current_text_cursor().insertText(item)
360 self.cancel_completion()
360 self.cancel_completion()
361
361
362 def _current_text_cursor(self):
362 def _current_text_cursor(self):
363 """ Returns a cursor with text between the start position and the
363 """ Returns a cursor with text between the start position and the
364 current position selected.
364 current position selected.
365 """
365 """
366 cursor = self._text_edit.textCursor()
366 cursor = self._text_edit.textCursor()
367 if cursor.position() >= self._start_position:
367 if cursor.position() >= self._start_position:
368 cursor.setPosition(self._start_position,
368 cursor.setPosition(self._start_position,
369 QtGui.QTextCursor.KeepAnchor)
369 QtGui.QTextCursor.KeepAnchor)
370 return cursor
370 return cursor
371
371
General Comments 0
You need to be logged in to leave comments. Login now