diff --git a/IPython/html/static/base/js/utils.js b/IPython/html/static/base/js/utils.js
index e2b532e..f12f9d4 100644
--- a/IPython/html/static/base/js/utils.js
+++ b/IPython/html/static/base/js/utils.js
@@ -437,7 +437,7 @@ IPython.utils = (function (IPython) {
return decodeURIComponent($('body').data(key));
};
- var absolute_cursor_pos = function (cm, cursor) {
+ var to_absolute_cursor_pos = function (cm, cursor) {
// get the absolute cursor position from CodeMirror's col, ch
if (!cursor) {
cursor = cm.getCursor();
@@ -449,6 +449,27 @@ IPython.utils = (function (IPython) {
return cursor_pos;
};
+ var from_absolute_cursor_pos = function (cm, cursor_pos) {
+ // turn absolute cursor postion into CodeMirror col, ch cursor
+ var i, line;
+ var offset = 0;
+ for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
+ if (offset + line.length < cursor_pos) {
+ offset += line.length + 1;
+ } else {
+ return {
+ line : i,
+ ch : cursor_pos - offset,
+ };
+ }
+ }
+ // reached end, return endpoint
+ return {
+ ch : line.length - 1,
+ line : i - 1,
+ };
+ };
+
// http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
var browser = (function() {
if (typeof navigator === 'undefined') {
@@ -457,7 +478,7 @@ IPython.utils = (function (IPython) {
}
var N= navigator.appName, ua= navigator.userAgent, tem;
var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
- if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
+ if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
return M;
})();
@@ -523,7 +544,8 @@ IPython.utils = (function (IPython) {
splitext : splitext,
escape_html : escape_html,
always_new : always_new,
- absolute_cursor_pos : absolute_cursor_pos,
+ to_absolute_cursor_pos : to_absolute_cursor_pos,
+ from_absolute_cursor_pos : from_absolute_cursor_pos,
browser : browser,
platform: platform,
is_or_has : is_or_has,
diff --git a/IPython/html/static/notebook/js/completer.js b/IPython/html/static/notebook/js/completer.js
index ac6d5f1..bddf732 100644
--- a/IPython/html/static/notebook/js/completer.js
+++ b/IPython/html/static/notebook/js/completer.js
@@ -11,15 +11,16 @@ var IPython = (function (IPython) {
// easier key mapping
var keycodes = IPython.keyboard.keycodes;
+ var utils = IPython.utils;
- function prepend_n_prc(str, n) {
+ var prepend_n_prc = function(str, n) {
for( var i =0 ; i< n ; i++){
str = '%'+str ;
}
return str;
};
- function _existing_completion(item, completion_array){
+ var _existing_completion = function(item, completion_array){
for( var i=0; i < completion_array.length; i++) {
if (completion_array[i].trim().substr(-item.length) == item) {
return true;
@@ -147,13 +148,14 @@ var IPython = (function (IPython) {
// one kernel completion came back, finish_completing will be called with the results
// we fork here and directly call finish completing if kernel is busy
+ var cursor_pos = utils.to_absolute_cursor_pos(this.editor, cur);
if (this.skip_kernel_completion) {
- this.finish_completing({
+ this.finish_completing({ content: {
matches: [],
- matched_text: ""
- });
+ cursor_start: cursor_pos,
+ cursor_end: cursor_pos,
+ }});
} else {
- var cursor_pos = IPython.utils.absolute_cursor_pos(this.editor, cur);
this.cell.kernel.complete(this.editor.getValue(), cursor_pos,
$.proxy(this.finish_completing, this)
);
@@ -164,7 +166,8 @@ var IPython = (function (IPython) {
// let's build a function that wrap all that stuff into what is needed
// for the new completer:
var content = msg.content;
- var matched_text = content.matched_text;
+ var start = content.cursor_start;
+ var end = content.cursor_end;
var matches = content.matches;
var cur = this.editor.getCursor();
@@ -172,7 +175,8 @@ var IPython = (function (IPython) {
var filtered_results = [];
//remove results from context completion
//that are already in kernel completion
- for (var i=0; i < results.length; i++) {
+ var i;
+ for (i=0; i < results.length; i++) {
if (!_existing_completion(results[i].str, matches)) {
filtered_results.push(results[i]);
}
@@ -181,18 +185,12 @@ var IPython = (function (IPython) {
// append the introspection result, in order, at at the beginning of
// the table and compute the replacement range from current cursor
// positon and matched_text length.
- for (var i = matches.length - 1; i >= 0; --i) {
+ for (i = matches.length - 1; i >= 0; --i) {
filtered_results.unshift({
str: matches[i],
type: "introspection",
- from: {
- line: cur.line,
- ch: cur.ch - matched_text.length
- },
- to: {
- line: cur.line,
- ch: cur.ch
- }
+ from: utils.from_absolute_cursor_pos(this.editor, start),
+ to: utils.from_absolute_cursor_pos(this.editor, end)
});
}
@@ -256,8 +254,9 @@ var IPython = (function (IPython) {
// After everything is on the page, compute the postion.
// We put it above the code if it is too close to the bottom of the page.
- cur.ch = cur.ch-matched_text.length;
- var pos = this.editor.cursorCoords(cur);
+ var pos = this.editor.cursorCoords(
+ utils.from_absolute_cursor_pos(this.editor, start)
+ );
var left = pos.left-3;
var top;
var cheight = this.complete.height();
diff --git a/IPython/html/static/notebook/js/tooltip.js b/IPython/html/static/notebook/js/tooltip.js
index f9d9b6a..066f709 100644
--- a/IPython/html/static/notebook/js/tooltip.js
+++ b/IPython/html/static/notebook/js/tooltip.js
@@ -229,7 +229,7 @@ var IPython = (function (IPython) {
this.cancel_pending();
var editor = cell.code_mirror;
var cursor = editor.getCursor();
- var cursor_pos = IPython.utils.absolute_cursor_pos(editor, cursor);
+ var cursor_pos = utils.to_absolute_cursor_pos(editor, cursor);
var text = cell.get_text();
this._hide_if_no_docstring = hide_if_no_docstring;
diff --git a/IPython/kernel/tests/test_message_spec.py b/IPython/kernel/tests/test_message_spec.py
index 36a0857..9c9da71 100644
--- a/IPython/kernel/tests/test_message_spec.py
+++ b/IPython/kernel/tests/test_message_spec.py
@@ -138,6 +138,9 @@ class Status(Reference):
class CompleteReply(Reference):
matches = List(Unicode)
+ cursor_start = Integer()
+ cursor_end = Integer()
+ status = Unicode()
class KernelInfoReply(Reference):
diff --git a/IPython/kernel/zmq/ipkernel.py b/IPython/kernel/zmq/ipkernel.py
index 27a3557..5f20b23 100755
--- a/IPython/kernel/zmq/ipkernel.py
+++ b/IPython/kernel/zmq/ipkernel.py
@@ -496,7 +496,9 @@ class Kernel(Configurable):
txt, matches = self.shell.complete('', code, cursor_pos)
matches = {'matches' : matches,
- 'matched_text' : txt,
+ 'cursor_end' : cursor_pos,
+ 'cursor_start' : cursor_pos - len(txt),
+ 'metadata' : {},
'status' : 'ok'}
matches = json_clean(matches)
completion_msg = self.session.send(stream, 'complete_reply',
diff --git a/IPython/qt/console/ipython_widget.py b/IPython/qt/console/ipython_widget.py
index 1f51dbc..1a0a757 100644
--- a/IPython/qt/console/ipython_widget.py
+++ b/IPython/qt/console/ipython_widget.py
@@ -146,22 +146,12 @@ class IPythonWidget(FrontendWidget):
info = self._request_info.get('complete')
if info and info.id == rep['parent_header']['msg_id'] and \
info.pos == cursor.position():
- matches = rep['content']['matches']
- text = rep['content']['matched_text']
- offset = len(text)
-
- # Clean up matches with period and path separators if the matched
- # text has not been transformed. This is done by truncating all
- # but the last component and then suitably decreasing the offset
- # between the current cursor position and the start of completion.
- if len(matches) > 1 and matches[0][:offset] == text:
- parts = re.split(r'[./\\]', text)
- sep_count = len(parts) - 1
- if sep_count:
- chop_length = sum(map(len, parts[:sep_count])) + sep_count
- matches = [ match[chop_length:] for match in matches ]
- offset -= chop_length
-
+ content = rep['content']
+ matches = content['matches']
+ start = content['cursor_start']
+ end = content['cursor_end']
+
+ offset = end - start
# Move the cursor to the start of the match and complete.
cursor.movePosition(QtGui.QTextCursor.Left, n=offset)
self._complete_with_items(cursor, matches)