diff --git a/IPython/html/services/kernels/handlers.py b/IPython/html/services/kernels/handlers.py
index b68fc52..a4361c9 100644
--- a/IPython/html/services/kernels/handlers.py
+++ b/IPython/html/services/kernels/handlers.py
@@ -81,16 +81,23 @@ class ZMQChannelHandler(AuthenticatedZMQStreamHandler):
km = self.kernel_manager
meth = getattr(km, 'connect_%s' % self.channel)
self.zmq_stream = meth(self.kernel_id, identity=self.session.bsession)
+ # Create a kernel_info channel to query the kernel protocol version.
+ # This channel will be closed after the kernel_info reply is received.
self.kernel_info_channel = None
self.kernel_info_channel = km.connect_shell(self.kernel_id)
- self.kernel_info_channel.on_recv(self._handle_kernel_info)
+ self.kernel_info_channel.on_recv(self._handle_kernel_info_reply)
self._request_kernel_info()
def _request_kernel_info(self):
+ """send a request for kernel_info"""
self.log.debug("requesting kernel info")
self.session.send(self.kernel_info_channel, "kernel_info_request")
- def _handle_kernel_info(self, msg):
+ def _handle_kernel_info_reply(self, msg):
+ """process the kernel_info_reply
+
+ enabling msg spec adaptation, if necessary
+ """
idents,msg = self.session.feed_identities(msg)
try:
msg = self.session.unserialize(msg)
diff --git a/IPython/html/static/notebook/js/completer.js b/IPython/html/static/notebook/js/completer.js
index bddf732..ecac3bd 100644
--- a/IPython/html/static/notebook/js/completer.js
+++ b/IPython/html/static/notebook/js/completer.js
@@ -171,6 +171,15 @@ var IPython = (function (IPython) {
var matches = content.matches;
var cur = this.editor.getCursor();
+ if (end === null) {
+ // adapted message spec replies don't have cursor position info,
+ // interpret end=null as current position,
+ // and negative start relative to that
+ end = utils.to_absolute_cursor_pos(this.editor, cur);
+ if (start < 0) {
+ start = end + start;
+ }
+ }
var results = CodeMirror.contextHint(this.editor);
var filtered_results = [];
//remove results from context completion
diff --git a/IPython/kernel/adapter.py b/IPython/kernel/adapter.py
index 5d33e97..f15c75f 100644
--- a/IPython/kernel/adapter.py
+++ b/IPython/kernel/adapter.py
@@ -5,7 +5,7 @@
import json
-from IPython.core.release import kernel_protocol_version, kernel_protocol_version_info
+from IPython.core.release import kernel_protocol_version_info
from IPython.utils.tokenutil import token_at_cursor
@@ -45,6 +45,13 @@ class Adapter(object):
msg['msg_type'] = header['msg_type'] = self.msg_type_map[msg_type]
return msg
+ def handle_reply_status_error(msg):
+ """This will be called *instead of* the regular handler
+
+ on any reply with status != ok
+ """
+ return msg
+
def __call__(self, msg):
msg = self.update_header(msg)
msg = self.update_metadata(msg)
@@ -54,9 +61,10 @@ class Adapter(object):
handler = getattr(self, header['msg_type'], None)
if handler is None:
return msg
- # no transform for status=error
+
+ # handle status=error replies separately (no change, at present)
if msg['content'].get('status', None) in {'error', 'aborted'}:
- return msg
+ return self.handle_reply_status_error(msg)
return handler(msg)
def _version_str_to_list(version):
@@ -143,7 +151,7 @@ class V5toV4(Adapter):
cursor_pos = content['cursor_pos']
line, _ = code_to_line(code, cursor_pos)
- new_content = msg['content'] = {'status' : 'ok'}
+ new_content = msg['content'] = {}
new_content['oname'] = token_at_cursor(code, cursor_pos)
new_content['detail_level'] = content['detail_level']
return msg
@@ -173,12 +181,10 @@ class V5toV4(Adapter):
msg['content'].pop('password', None)
return msg
-def _tuple_to_str(version):
- return ".".join(map(str, version))
class V4toV5(Adapter):
"""Convert msg spec V4 to V5"""
- version = kernel_protocol_version
+ version = '5.0'
# invert message renames above
msg_type_map = {v:k for k,v in V5toV4.msg_type_map.items()}
@@ -227,13 +233,14 @@ class V4toV5(Adapter):
return msg
def complete_reply(self, msg):
- # TODO: complete_reply needs more context than we have
- # Maybe strip common prefix and give magic cursor_start = cursor_end = 0?
+ # complete_reply needs more context than we have to get cursor_start and end.
+ # use special value of `-1` to indicate to frontend that it should be at
+ # the current cursor position.
content = msg['content']
new_content = msg['content'] = {'status' : 'ok'}
- n = len(content['matched_text'])
- new_content['matches'] = [ m[n:] for m in content['matches'] ]
- new_content['cursor_start'] = new_content['cursor_end'] = 0
+ new_content['matches'] = content['matches']
+ new_content['cursor_start'] = -len(content['matched_text'])
+ new_content['cursor_end'] = None
new_content['metadata'] = {}
return msg
diff --git a/IPython/kernel/tests/test_adapter.py b/IPython/kernel/tests/test_adapter.py
index 46873c7..8590fd2 100644
--- a/IPython/kernel/tests/test_adapter.py
+++ b/IPython/kernel/tests/test_adapter.py
@@ -114,13 +114,11 @@ class V4toV5TestCase(AdapterTest):
v4, v5 = self.adapt(msg)
v4c = v4['content']
v5c = v5['content']
- n = len(v4c['matched_text'])
- expected_matches = [ m[n:] for m in v4c['matches'] ]
- self.assertEqual(v5c['matches'], expected_matches)
+ self.assertEqual(v5c['matches'], v4c['matches'])
self.assertEqual(v5c['metadata'], {})
- self.assertEqual(v5c['cursor_start'], 0)
- self.assertEqual(v5c['cursor_end'], 0)
+ self.assertEqual(v5c['cursor_start'], -4)
+ self.assertEqual(v5c['cursor_end'], None)
def test_object_info_request(self):
msg = self.msg("object_info_request", {