Repository: ipython/ipython

Issue:
Inspect requests inside a function call should be smarter about what they inspect.
Previously, `func(a, b, <shift-tab>` would give information on `func`, now it gives information on `b`, which is not especially helpful.

This is because we removed logic from the frontend to make it more language agnostic, and we have not yet reimplemented that on the frontend. For 3.1, we should make it at least as smart as 2.x was. The quicky and dirty approach would be a regex; the proper way is tokenising the code.

Ping @mwaskom who brought this up on the mailing list.

Discussion:

Patch:
diff --git a/IPython/html/static/notebook/js/actions.js b/IPython/html/static/notebook/js/actions.js
index 828b724e7..40816144e 100644
--- a/IPython/html/static/notebook/js/actions.js
+++ b/IPython/html/static/notebook/js/actions.js
@@ -79,7 +79,6 @@ define(function(require){
             }
         },
         'select-previous-cell' : {
-            help: 'select cell above',
             help_index : 'da',
             handler : function (env) {
                 var index = env.notebook.get_selected_index();
@@ -90,7 +89,6 @@ define(function(require){
             }
         },
         'select-next-cell' : {
-            help: 'select cell below',
             help_index : 'db',
             handler : function (env) {
                 var index = env.notebook.get_selected_index();
@@ -119,14 +117,12 @@ define(function(require){
             }
         },
         'paste-cell-before' : {
-            help: 'paste cell above',
             help_index : 'eg',
             handler : function (env) {
                 env.notebook.paste_cell_above();
             }
         },
         'paste-cell-after' : {
-            help: 'paste cell below',
             icon: 'fa-paste',
             help_index : 'eh',
             handler : function (env) {
@@ -134,7 +130,6 @@ define(function(require){
             }
         },
         'insert-cell-before' : {
-            help: 'insert cell above',
             help_index : 'ec',
             handler : function (env) {
                 env.notebook.insert_cell_above();
@@ -143,7 +138,6 @@ define(function(require){
             }
         },
         'insert-cell-after' : {
-            help: 'insert cell below',
             icon : 'fa-plus',
             help_index : 'ed',
             handler : function (env) {
@@ -257,7 +251,6 @@ define(function(require){
             }
         },
         'delete-cell': {
-            help: 'delete selected cell',
             help_index : 'ej',
             handler : function (env) {
                 env.notebook.delete_cell();
diff --git a/IPython/html/static/notebook/js/quickhelp.js b/IPython/html/static/notebook/js/quickhelp.js
index 36402139f..bceaa2db1 100644
--- a/IPython/html/static/notebook/js/quickhelp.js
+++ b/IPython/html/static/notebook/js/quickhelp.js
@@ -38,8 +38,8 @@ define([
             { shortcut: "Cmd-Down",   help:"go to cell end"  },
             { shortcut: "Alt-Left",   help:"go one word left"  },
             { shortcut: "Alt-Right",  help:"go one word right"  },
-            { shortcut: "Alt-Backspace",      help:"delete word before"  },
-            { shortcut: "Alt-Delete",         help:"delete word after"  },
+            { shortcut: "Alt-Backspace",      help:"del word before"  },
+            { shortcut: "Alt-Delete",         help:"del word after"  },
         ];
     } else {
         // PC specific
@@ -50,8 +50,8 @@ define([
             { shortcut: "Ctrl-Down",  help:"go to cell end"  },
             { shortcut: "Ctrl-Left",  help:"go one word left"  },
             { shortcut: "Ctrl-Right", help:"go one word right"  },
-            { shortcut: "Ctrl-Backspace", help:"delete word before"  },
-            { shortcut: "Ctrl-Delete",    help:"delete word after"  },
+            { shortcut: "Ctrl-Backspace", help:"del word before"  },
+            { shortcut: "Ctrl-Delete",    help:"del word after"  },
         ];
     }
 
diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js
index e5f758dd3..073186716 100644
--- a/IPython/html/static/widgets/js/widget.js
+++ b/IPython/html/static/widgets/js/widget.js
@@ -155,9 +155,8 @@ define(["widgets/js/manager",
                     this.trigger('msg:custom', msg.content.data.content);
                     break;
                 case 'display':
-                    this.state_change = this.state_change.then(function() {
-                        that.widget_manager.display_view(msg, that);
-                    }).catch(utils.reject('Could not process display view msg', true));
+                    this.widget_manager.display_view(msg, this)
+                        .catch(utils.reject('Could not process display view msg', true));
                     break;
             }
         },
diff --git a/IPython/utils/tokenutil.py b/IPython/utils/tokenutil.py
index 48cc82c2e..f0040bfd8 100644
--- a/IPython/utils/tokenutil.py
+++ b/IPython/utils/tokenutil.py
@@ -58,6 +58,9 @@ def token_at_cursor(cell, cursor_pos=0):
     
     Used for introspection.
     
+    Function calls are prioritized, so the token for the callable will be returned
+    if the cursor is anywhere inside the call.
+    
     Parameters
     ----------
     
@@ -70,6 +73,7 @@ def token_at_cursor(cell, cursor_pos=0):
     names = []
     tokens = []
     offset = 0
+    call_names = []
     for tup in generate_tokens(StringIO(cell).readline):
         
         tok = Token(*tup)
@@ -93,6 +97,11 @@ def token_at_cursor(cell, cursor_pos=0):
             if tok.text == '=' and names:
                 # don't inspect the lhs of an assignment
                 names.pop(-1)
+            if tok.text == '(' and names:
+                # if we are inside a function call, inspect the function
+                call_names.append(names[-1])
+            elif tok.text == ')' and call_names:
+                call_names.pop(-1)
         
         if offset + end_col > cursor_pos:
             # we found the cursor, stop reading
@@ -102,7 +111,9 @@ def token_at_cursor(cell, cursor_pos=0):
         if tok.token == tokenize2.NEWLINE:
             offset += len(tok.line)
     
-    if names:
+    if call_names:
+        return call_names[-1]
+    elif names:
         return names[-1]
     else:
         return ''
diff --git a/examples/Notebook/Notebook Basics.ipynb b/examples/Notebook/Notebook Basics.ipynb
index ae67fab91..ce5c43338 100644
--- a/examples/Notebook/Notebook Basics.ipynb
+++ b/examples/Notebook/Notebook Basics.ipynb
@@ -226,7 +226,7 @@
     "1. Basic navigation: `enter`, `shift-enter`, `up/k`, `down/j`\n",
     "2. Saving the notebook: `s`\n",
     "2. Cell types: `y`, `m`, `1-6`, `t`\n",
-    "3. Cell creation: `a`, `b`\n",
+    "3. Cell creation and movement: `a`, `b`, `ctrl+k`, `ctrl+j`\n",
     "4. Cell editing: `x`, `c`, `v`, `d`, `z`, `shift+=`\n",
     "5. Kernel operations: `i`, `.`"
    ]


Test patch:
diff --git a/IPython/utils/tests/test_tokenutil.py b/IPython/utils/tests/test_tokenutil.py
index be9cb578a..ff3efc75c 100644
--- a/IPython/utils/tests/test_tokenutil.py
+++ b/IPython/utils/tests/test_tokenutil.py
@@ -48,13 +48,28 @@ def test_multiline():
     start = cell.index(expected) + 1
     for i in range(start, start + len(expected)):
         expect_token(expected, cell, i)
-    expected = 'there'
+    expected = 'hello'
     start = cell.index(expected) + 1
     for i in range(start, start + len(expected)):
         expect_token(expected, cell, i)
 
+def test_nested_call():
+    cell = "foo(bar(a=5), b=10)"
+    expected = 'foo'
+    start = cell.index('bar') + 1
+    for i in range(start, start + 3):
+        expect_token(expected, cell, i)
+    expected = 'bar'
+    start = cell.index('a=')
+    for i in range(start, start + 3):
+        expect_token(expected, cell, i)
+    expected = 'foo'
+    start = cell.index(')') + 1
+    for i in range(start, len(cell)-1):
+        expect_token(expected, cell, i)
+
 def test_attrs():
-    cell = "foo(a=obj.attr.subattr)"
+    cell = "a = obj.attr.subattr"
     expected = 'obj'
     idx = cell.find('obj') + 1
     for i in range(idx, idx + 3):