Repository: ipython/ipython

Issue:
QTConsole tooltip logic chooses wrong object to print infomation about
Moved from #6726 as requested

The tooltip shifts to display info about the argument rather then the function (^ denotes the cursor) once an argument is provided:

``` python
In [14]: s='foo'

In [15]: s.split(^  # shows info about str.split
In [15]: s.split(^s  # shows info about string 's'
In [15]: s.split(^x  # no tooltip because x does not exist
```

In older version you always saw the function signature and specifically the names of the keyword arguments.

and again:

The tooltip updates depending on where you are within the parens (maybe intentional, but different than it used to be) and the tooltip looks at what's right in front of the cursor rather than just behind it when just to the right of the parens.

@takluyver suggests looking at:
https://github.com/ipython/ipython/blob/master/IPython/qt/console/frontend_widget.py#L718
but any language-specific parts should go in the kernel and not the frontend if possible.

continues QTConsole regressions from #6726



Discussion:
The change was introduced in de473a1cef96068c6ebd2f5f52e6c3152fbfd799.

The tooltop behavior previously displayed the function signature and after this it shows the object under the cursor as you type them in. was this change intentional? I've come to depend on the former...

cc @minrk 


Resolving patch:
diff --git a/IPython/utils/tokenutil.py b/IPython/utils/tokenutil.py
--- a/IPython/utils/tokenutil.py
+++ b/IPython/utils/tokenutil.py
@@ -47,7 +47,9 @@ def token_at_cursor(cell, cursor_pos=0):
         # token, text, start, end, line = tup
         start_col = tok.start[1]
         end_col = tok.end[1]
-        if offset + start_col > cursor_pos:
+        # allow '|foo' to find 'foo' at the beginning of a line
+        boundary = cursor_pos + 1 if start_col == 0 else cursor_pos
+        if offset + start_col >= boundary:
             # current token starts after the cursor,
             # don't consume it
             break


Test patch:
diff --git a/IPython/utils/tests/test_tokenutil.py b/IPython/utils/tests/test_tokenutil.py
--- a/IPython/utils/tests/test_tokenutil.py
+++ b/IPython/utils/tests/test_tokenutil.py
@@ -18,8 +18,8 @@ def expect_token(expected, cell, cursor_pos):
     line_with_cursor = '%s|%s' % (line[:column], line[column:])
     line
     nt.assert_equal(token, expected,
-        "Excpected %r, got %r in: %s" % (
-        expected, token, line_with_cursor)
+        "Expected %r, got %r in: %r (pos %i)" % (
+        expected, token, line_with_cursor, cursor_pos)
     )
 
 def test_simple(): 
@@ -30,7 +30,14 @@ def test_simple():
 def test_function():
     cell = "foo(a=5, b='10')"
     expected = 'foo'
-    for i in (6,7,8,10,11,12):
+    # up to `foo(|a=`
+    for i in range(cell.find('a=') + 1):
+        expect_token("foo", cell, i)
+    # find foo after `=`
+    for i in [cell.find('=') + 1, cell.rfind('=') + 1]:
+        expect_token("foo", cell, i)
+    # in between `5,|` and `|b=`
+    for i in range(cell.find(','), cell.find('b=')):
         expect_token("foo", cell, i)
 
 def test_multiline():
@@ -39,25 +46,25 @@ def test_multiline():
         'b = hello("string", there)'
     ])
     expected = 'hello'
-    start = cell.index(expected)
+    start = cell.index(expected) + 1
     for i in range(start, start + len(expected)):
         expect_token(expected, cell, i)
     expected = 'there'
-    start = cell.index(expected)
+    start = cell.index(expected) + 1
     for i in range(start, start + len(expected)):
         expect_token(expected, cell, i)
 
 def test_attrs():
     cell = "foo(a=obj.attr.subattr)"
     expected = 'obj'
-    idx = cell.find('obj')
+    idx = cell.find('obj') + 1
     for i in range(idx, idx + 3):
         expect_token(expected, cell, i)
-    idx = idx + 4
+    idx = cell.find('.attr') + 2
     expected = 'obj.attr'
     for i in range(idx, idx + 4):
         expect_token(expected, cell, i)
-    idx = idx + 5
+    idx = cell.find('.subattr') + 2
     expected = 'obj.attr.subattr'
     for i in range(idx, len(cell)):
         expect_token(expected, cell, i)