diff --git a/IPython/frontend/qt/console/completion_html.py b/IPython/frontend/qt/console/completion_html.py
index 0875627..852fcf3 100644
--- a/IPython/frontend/qt/console/completion_html.py
+++ b/IPython/frontend/qt/console/completion_html.py
@@ -1,7 +1,116 @@
+"""a navigable completer for the qtconsole"""
+# coding : utf-8
+#-----------------------------------------------------------------------------
+# Copyright (c) 2012, IPython Development Team.$
+#
+# Distributed under the terms of the Modified BSD License.$
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
# System library imports
+import IPython.utils.text as text
+
from IPython.external.qt import QtCore, QtGui
-import IPython.utils.html_utils as html_utils
+#--------------------------------------------------------------------------
+# Return an HTML table with selected item in a special class
+#--------------------------------------------------------------------------
+def html_tableify(item_matrix, select=None, header=None , footer=None) :
+ """ returnr a string for an html table"""
+ if not item_matrix :
+ return ''
+ html_cols = []
+ tds = lambda text : u'
'+text+u' | '
+ trs = lambda text : u''+text+u'
'
+ tds_items = [map(tds, row) for row in item_matrix]
+ if select :
+ row, col = select
+ tds_items[row][col] = u''\
+ +item_matrix[row][col]\
+ +u' | '
+ #select the right item
+ html_cols = map(trs, (u''.join(row) for row in tds_items))
+ head = ''
+ foot = ''
+ if header :
+ head = (u''\
+ +''.join((u''+header+u' | ')*len(item_matrix[0]))\
+ +'
')
+
+ if footer :
+ foot = (u''\
+ +''.join((u''+footer+u' | ')*len(item_matrix[0]))\
+ +'
')
+ html = (u''+head+(u''.join(html_cols))+foot+u'
')
+ return html
+
+class SlidingInterval(object):
+ """a bound interval that follows a cursor
+
+ internally used to scoll the completion view when the cursor
+ try to go beyond the edges, and show '...' when rows are hidden
+ """
+
+ _min = 0
+ _max = 1
+ _current = 0
+ def __init__(self, maximum=1, width=6, minimum=0, sticky_lenght=1):
+ """Create a new bounded interval
+
+ any value return by this will be bound between maximum and
+ minimum. usual width will be 'width', and sticky_length
+ set when the return interval should expand to max and min
+ """
+ self._min = minimum
+ self._max = maximum
+ self._start = 0
+ self._width = width
+ self._stop = self._start+self._width+1
+ self._sticky_lenght = sticky_lenght
+
+ @property
+ def current(self):
+ """current cursor position"""
+ return self._current
+
+ @current.setter
+ def current(self, value):
+ """set current cursor position"""
+ current = min(max(self._min, value), self._max)
+
+ self._current = current
+
+ if current > self._stop :
+ self._stop = current
+ self._start = current-self._width
+ elif current < self._start :
+ self._start = current
+ self._stop = current + self._width
+
+ if abs(self._start - self._min) <= self._sticky_lenght :
+ self._start = self._min
+
+ if abs(self._stop - self._max) <= self._sticky_lenght :
+ self._stop = self._max
+
+ @property
+ def start(self):
+ """begiiing of interval to show"""
+ return self._start
+
+ @property
+ def stop(self):
+ """end of interval to show"""
+ return self._stop
+
+ @property
+ def width(self):
+ return self._stop - self._start
+
+ @property
+ def nth(self):
+ return self.current - self.start
class CompletionHtml(QtGui.QWidget):
""" A widget for tab completion, navigable by arrow keys """
@@ -16,6 +125,8 @@ class CompletionHtml(QtGui.QWidget):
_size = (1, 1)
_old_cursor = None
_start_position = 0
+ _slice_start = 0
+ _slice_len = 4
def __init__(self, console_widget):
""" Create a completion widget that is attached to the specified Qt
@@ -27,6 +138,8 @@ class CompletionHtml(QtGui.QWidget):
self._text_edit = console_widget._control
self._console_widget = console_widget
self._text_edit.installEventFilter(self)
+ self._sliding_interval = None
+ self._justified_items = None
# Ensure that the text edit keeps focus when widget is displayed.
self.setFocusProxy(self._text_edit)
@@ -71,24 +184,36 @@ class CompletionHtml(QtGui.QWidget):
self.select_left()
self._update_list()
return True
+ elif key in ( QtCore.Qt.Key_Escape,):
+ self.cancel_completion()
+ return True
else :
- self._cancel_completion()
+ self.cancel_completion()
else:
- self._cancel_completion()
+ self.cancel_completion()
elif etype == QtCore.QEvent.FocusOut:
- self._cancel_completion()
+ self.cancel_completion()
return super(CompletionHtml, self).eventFilter(obj, event)
#--------------------------------------------------------------------------
# 'CompletionHtml' interface
#--------------------------------------------------------------------------
- def _cancel_completion(self):
- """Cancel the completion, reseting internal variable, clearing buffer """
+ def cancel_completion(self):
+ """Cancel the completion
+
+ should be called when the completer have to be dismissed
+
+ This reset internal variable, clearing the temporary buffer
+ of the console where the completion are shown.
+ """
self._consecutive_tab = 0
+ self._slice_start = 0
self._console_widget._clear_temporary_buffer()
self._index = (0, 0)
+ if(self._sliding_interval):
+ self._sliding_interval = None
#
# ... 2 4 4 4 4 4 4 4 4 4 4 4 4
@@ -147,6 +272,12 @@ class CompletionHtml(QtGui.QWidget):
have gone before : %d:%d (%d:%d)"%(row, col, nr, nc) )
+ @property
+ def _slice_end(self):
+ end = self._slice_start+self._slice_len
+ if end > len(self._items) :
+ return None
+ return end
def select_up(self):
"""move cursor up"""
@@ -176,27 +307,42 @@ class CompletionHtml(QtGui.QWidget):
return
self._start_position = cursor.position()
self._consecutive_tab = 1
- ci = html_utils.columnize_info(items, empty=' ')
- self._items = ci['item_matrix']
- self._size = (ci['rows_number'], ci['columns_number'])
+ items_m, ci = text.compute_item_matrix(items, empty=' ')
+ self._sliding_interval = SlidingInterval(len(items_m)-1)
+
+ self._items = items_m
+ self._size = (ci['rows_numbers'], ci['columns_numbers'])
self._old_cursor = cursor
self._index = (0, 0)
+ sjoin = lambda x : [ y.ljust(w, ' ') for y, w in zip(x, ci['columns_width'])]
+ self._justified_items = map(sjoin, items_m)
self._update_list(hilight=False)
+
+
def _update_list(self, hilight=True):
""" update the list of completion and hilight the currently selected completion """
- if len(self._items) > 100:
- items = self._items[:100]
- else :
- items = self._items
- items_m = items
+ self._sliding_interval.current = self._index[0]
+ head = None
+ foot = None
+ if self._sliding_interval.start > 0 :
+ head = '...'
+
+ if self._sliding_interval.stop < self._sliding_interval._max:
+ foot = '...'
+ items_m = self._justified_items[\
+ self._sliding_interval.start:\
+ self._sliding_interval.stop+1\
+ ]
self._console_widget._clear_temporary_buffer()
if(hilight):
- strng = html_utils.html_tableify(items_m, select=self._index)
- else:
- strng = html_utils.html_tableify(items_m, select=None)
+ sel = (self._sliding_interval.nth, self._index[1])
+ else :
+ sel = None
+
+ strng = html_tableify(items_m, select=sel, header=head, footer=foot)
self._console_widget._fill_temporary_buffer(self._old_cursor, strng, html=True)
#--------------------------------------------------------------------------
@@ -211,7 +357,7 @@ class CompletionHtml(QtGui.QWidget):
item = item.strip()
if item :
self._current_text_cursor().insertText(item)
- self._cancel_completion()
+ self.cancel_completion()
def _current_text_cursor(self):
""" Returns a cursor with text between the start position and the
@@ -223,4 +369,3 @@ class CompletionHtml(QtGui.QWidget):
QtGui.QTextCursor.KeepAnchor)
return cursor
-
diff --git a/IPython/frontend/qt/console/completion_plain.py b/IPython/frontend/qt/console/completion_plain.py
index 8db0069..d6b4066 100644
--- a/IPython/frontend/qt/console/completion_plain.py
+++ b/IPython/frontend/qt/console/completion_plain.py
@@ -1,6 +1,15 @@
+"""a simple completer for the qtconsole"""
+#-----------------------------------------------------------------------------
+# Copyright (c) 2012, IPython Development Team.$
+#
+# Distributed under the terms of the Modified BSD License.$
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-------------------------------------------------------------------
+
# System library imports
from IPython.external.qt import QtCore, QtGui
-import IPython.utils.html_utils as html_utils
+import IPython.utils.text as text
class CompletionPlain(QtGui.QWidget):
@@ -10,10 +19,6 @@ class CompletionPlain(QtGui.QWidget):
# 'QObject' interface
#--------------------------------------------------------------------------
- _items = ()
- _index = (0, 0)
- _old_cursor = None
-
def __init__(self, console_widget):
""" Create a completion widget that is attached to the specified Qt
text edit widget.
@@ -33,18 +38,17 @@ class CompletionPlain(QtGui.QWidget):
if obj == self._text_edit:
etype = event.type()
- if etype == QtCore.QEvent.KeyPress:
- self._cancel_completion()
+ if etype in( QtCore.QEvent.KeyPress, QtCore.QEvent.FocusOut ):
+ self.cancel_completion()
return super(CompletionPlain, self).eventFilter(obj, event)
#--------------------------------------------------------------------------
# 'CompletionPlain' interface
#--------------------------------------------------------------------------
- def _cancel_completion(self):
+ def cancel_completion(self):
"""Cancel the completion, reseting internal variable, clearing buffer """
self._console_widget._clear_temporary_buffer()
- self._index = (0, 0)
def show_items(self, cursor, items):
@@ -53,21 +57,6 @@ class CompletionPlain(QtGui.QWidget):
"""
if not items :
return
-
- ci = html_utils.columnize_info(items, empty=' ')
- self._items = ci['item_matrix']
- self._old_cursor = cursor
- self._update_list()
-
-
- def _update_list(self):
- """ update the list of completion and hilight the currently selected completion """
- if len(self._items) > 100:
- items = self._items[:100]
- else :
- items = self._items
- items_m = items
-
- self._console_widget._clear_temporary_buffer()
- strng = html_utils.html_tableify(items_m, select=None)
- self._console_widget._fill_temporary_buffer(self._old_cursor, strng, html=True)
+ self.cancel_completion()
+ strng = text.columnize(items)
+ self._console_widget._fill_temporary_buffer(cursor, strng, html=False)
diff --git a/IPython/frontend/qt/console/completion_widget.py b/IPython/frontend/qt/console/completion_widget.py
index 8103f05..b258775 100644
--- a/IPython/frontend/qt/console/completion_widget.py
+++ b/IPython/frontend/qt/console/completion_widget.py
@@ -134,5 +134,5 @@ class CompletionWidget(QtGui.QListWidget):
else:
self.hide()
- def _cancel_completion(self):
+ def cancel_completion(self):
self.hide()
diff --git a/IPython/frontend/qt/console/console_widget.py b/IPython/frontend/qt/console/console_widget.py
index 75bb12e..cd2a93c 100644
--- a/IPython/frontend/qt/console/console_widget.py
+++ b/IPython/frontend/qt/console/console_widget.py
@@ -843,7 +843,7 @@ class ConsoleWidget(LoggingConfigurable, QtGui.QWidget):
def _cancel_completion(self):
""" If text completion is progress, cancel it.
"""
- self._completion_widget._cancel_completion()
+ self._completion_widget.cancel_completion()
def _clear_temporary_buffer(self):
""" Clears the "temporary text" buffer, i.e. all the text following
diff --git a/IPython/frontend/qt/console/qtconsoleapp.py b/IPython/frontend/qt/console/qtconsoleapp.py
index e1eac4c..1735d9b 100644
--- a/IPython/frontend/qt/console/qtconsoleapp.py
+++ b/IPython/frontend/qt/console/qtconsoleapp.py
@@ -105,13 +105,6 @@ qt_flags = {
"Disable rich text support."),
}
-# not quite sure on how this works
-#qt_flags.update(boolean_flag(
-# 'gui-completion', 'ConsoleWidget.gui_completion',
-# "use a GUI widget for tab completion",
-# "use plaintext output for completion"
-#))
-
# and app_flags from the Console Mixin
qt_flags.update(app_flags)
# add frontend flags to the full set
diff --git a/IPython/utils/html_utils.py b/IPython/utils/html_utils.py
deleted file mode 100644
index aaf2f2b..0000000
--- a/IPython/utils/html_utils.py
+++ /dev/null
@@ -1,124 +0,0 @@
-"""some html utilis"""
-from IPython.core.display import HTML
-
-
-def columnize_info(items, separator_width=1, displaywidth=80, empty=None):
- """ Get info on a list of string to display it as a multicolumns list
-
- returns :
- ---------
-
- a dict containing several parameters:
-
- 'item_matrix' : list of list with the innermost list representing a row
- 'columns_number': number of columns
- 'rows_number' : number of rown
- 'columns_width' : a list indicating the maximum length of the element in each columns
-
- Parameters :
- ------------
- separator_width : when trying to ajust the number of column, consider a separator size of this much caracters
- displaywidth : try to fit the columns in this width
- empty : if the number of items is different from nrows * ncols, fill with empty
-
- """
- # Note: this code is adapted from columnize 0.3.2.
- # See http://code.google.com/p/pycolumnize/
-
- # Some degenerate cases.
- size = len(items)
- if size == 0:
- return {'item_matrix' :[[empty]],
- 'columns_number':1,
- 'rows_number':1,
- 'columns_width':[0]}
- elif size == 1:
- return {'item_matrix' :[[items[0]]],
- 'columns_number':1,
- 'rows_number':1,
- 'columns_width':[len(items[0])]}
-
- # Special case: if any item is longer than the maximum width, there's no
- # point in triggering the logic below...
- item_len = map(len, items) # save these, we can reuse them below
- #longest = max(item_len)
- #if longest >= displaywidth:
- # return (items, [longest])
-
- # Try every row count from 1 upwards
- array_index = lambda nrows, row, col: nrows*col + row
- nrows = 1
- for nrows in range(1, size):
- ncols = (size + nrows - 1) // nrows
- colwidths = []
- totwidth = -separator_width
- for col in range(ncols):
- # Get max column width for this column
- colwidth = 0
- for row in range(nrows):
- i = array_index(nrows, row, col)
- if i >= size:
- break
- len_x = item_len[i]
- colwidth = max(colwidth, len_x)
- colwidths.append(colwidth)
- totwidth += colwidth + separator_width
- if totwidth > displaywidth:
- break
- if totwidth <= displaywidth:
- break
-
- # The smallest number of rows computed and the max widths for each
- # column has been obtained. Now we just have to format each of the rows.
- reorderd_items = []
- for row in range(nrows):
- texts = []
- for col in range(ncols):
- i = row + nrows*col
- if i >= size:
- texts.append(empty)
- else:
- texts.append(items[i])
- #while texts and not texts[-1]:
- # del texts[-1]
- #for col in range(len(texts)):
- # texts[col] = texts[col].ljust(colwidths[col])
- reorderd_items.append(texts)
-
- return {'item_matrix' :reorderd_items,
- 'columns_number':ncols,
- 'rows_number':nrows,
- 'columns_width':colwidths}
-
-
-def column_table(items, select=None) :
- """ return a html table of the item with a select class on one"""
- items_m = columnize_info(items)['item_matrix']
- return HTML(html_tableify(items_m, select=select))
-
-def html_tableify(item_matrix, select=None) :
- """ returnr a string for an html table"""
- if not item_matrix :
- return ''
- html_cols = []
- tds = lambda text : u''+text+u' | '
- trs = lambda text : u''+text+u'
'
- tds_items = [map(tds, row) for row in item_matrix ]
- if select :
- row, col = select
- try :
- tds_items[row][col] = u''\
- +item_matrix[row][col]\
- +u' | '
- except IndexError :
- pass
- #select the right item
- html_cols = map(trs, (u''.join(row) for row in tds_items))
- html = (u''+(u''.join(html_cols))+u'
')
- css = u"""
-
- """
- return css+html
diff --git a/IPython/utils/text.py b/IPython/utils/text.py
index 85db442..0df0bec 100644
--- a/IPython/utils/text.py
+++ b/IPython/utils/text.py
@@ -690,7 +690,7 @@ def _get_or_default(mylist, i, default=None):
return mylist[i]
@skip_doctest
-def compute_item_matrix(items, *args, **kwargs) :
+def compute_item_matrix(items, empty=None, *args, **kwargs) :
"""Returns a nested list, and info to columnize items
Parameters :
@@ -698,6 +698,8 @@ def compute_item_matrix(items, *args, **kwargs) :
items :
list of strings to columize
+ empty : (default None)
+ default value to fill list if needed
separator_size : int (default=2)
How much caracters will be used as a separation between each columns.
displaywidth : int (default=80)