Show More
@@ -1,13 +1,10 b'' | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008-2012 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
|||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | //============================================================================ |
|
4 | //============================================================================ | |
9 | // Utilities |
|
5 | // Utilities | |
10 | //============================================================================ |
|
6 | //============================================================================ | |
|
7 | ||||
11 | IPython.namespace('IPython.utils'); |
|
8 | IPython.namespace('IPython.utils'); | |
12 |
|
9 | |||
13 | IPython.utils = (function (IPython) { |
|
10 | IPython.utils = (function (IPython) { | |
@@ -430,7 +427,7 b' IPython.utils = (function (IPython) {' | |||||
430 | var escape_html = function (text) { |
|
427 | var escape_html = function (text) { | |
431 | // escape text to HTML |
|
428 | // escape text to HTML | |
432 | return $("<div/>").text(text).html(); |
|
429 | return $("<div/>").text(text).html(); | |
433 | } |
|
430 | }; | |
434 |
|
431 | |||
435 |
|
432 | |||
436 | var get_body_data = function(key) { |
|
433 | var get_body_data = function(key) { | |
@@ -440,6 +437,17 b' IPython.utils = (function (IPython) {' | |||||
440 | return decodeURIComponent($('body').data(key)); |
|
437 | return decodeURIComponent($('body').data(key)); | |
441 | }; |
|
438 | }; | |
442 |
|
439 | |||
|
440 | var absolute_cursor_pos = function (cm, cursor) { | |||
|
441 | // get the absolute cursor position from CodeMirror's col, ch | |||
|
442 | if (!cursor) { | |||
|
443 | cursor = cm.getCursor(); | |||
|
444 | } | |||
|
445 | var cursor_pos = cursor.ch; | |||
|
446 | for (var i = 0; i < cursor.line; i++) { | |||
|
447 | cursor_pos += cm.getLine(i).length + 1; | |||
|
448 | } | |||
|
449 | return cursor_pos; | |||
|
450 | }; | |||
443 |
|
451 | |||
444 | // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript |
|
452 | // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript | |
445 | var browser = (function() { |
|
453 | var browser = (function() { | |
@@ -465,13 +473,13 b' IPython.utils = (function (IPython) {' | |||||
465 | if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS"; |
|
473 | if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS"; | |
466 | if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX"; |
|
474 | if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX"; | |
467 | if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux"; |
|
475 | if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux"; | |
468 | return OSName |
|
476 | return OSName; | |
469 | })(); |
|
477 | })(); | |
470 |
|
478 | |||
471 | var is_or_has = function (a, b) { |
|
479 | var is_or_has = function (a, b) { | |
472 | // Is b a child of a or a itself? |
|
480 | // Is b a child of a or a itself? | |
473 | return a.has(b).length !==0 || a.is(b); |
|
481 | return a.has(b).length !==0 || a.is(b); | |
474 | } |
|
482 | }; | |
475 |
|
483 | |||
476 | var is_focused = function (e) { |
|
484 | var is_focused = function (e) { | |
477 | // Is element e, or one of its children focused? |
|
485 | // Is element e, or one of its children focused? | |
@@ -486,7 +494,7 b' IPython.utils = (function (IPython) {' | |||||
486 | } else { |
|
494 | } else { | |
487 | return false; |
|
495 | return false; | |
488 | } |
|
496 | } | |
489 | } |
|
497 | }; | |
490 |
|
498 | |||
491 | var log_ajax_error = function (jqXHR, status, error) { |
|
499 | var log_ajax_error = function (jqXHR, status, error) { | |
492 | // log ajax failures with informative messages |
|
500 | // log ajax failures with informative messages | |
@@ -515,6 +523,7 b' IPython.utils = (function (IPython) {' | |||||
515 | splitext : splitext, |
|
523 | splitext : splitext, | |
516 | escape_html : escape_html, |
|
524 | escape_html : escape_html, | |
517 | always_new : always_new, |
|
525 | always_new : always_new, | |
|
526 | absolute_cursor_pos : absolute_cursor_pos, | |||
518 | browser : browser, |
|
527 | browser : browser, | |
519 | platform: platform, |
|
528 | platform: platform, | |
520 | is_or_has : is_or_has, |
|
529 | is_or_has : is_or_has, |
@@ -1,6 +1,10 b'' | |||||
1 | // function completer. |
|
1 | // Copyright (c) IPython Development Team. | |
|
2 | // Distributed under the terms of the Modified BSD License. | |||
|
3 | ||||
|
4 | // Completer | |||
2 | // |
|
5 | // | |
3 |
// |
|
6 | // Completer is be a class that takes a cell instance. | |
|
7 | ||||
4 | var IPython = (function (IPython) { |
|
8 | var IPython = (function (IPython) { | |
5 | // that will prevent us from misspelling |
|
9 | // that will prevent us from misspelling | |
6 | "use strict"; |
|
10 | "use strict"; | |
@@ -145,11 +149,14 b' var IPython = (function (IPython) {' | |||||
145 | // we fork here and directly call finish completing if kernel is busy |
|
149 | // we fork here and directly call finish completing if kernel is busy | |
146 | if (this.skip_kernel_completion) { |
|
150 | if (this.skip_kernel_completion) { | |
147 | this.finish_completing({ |
|
151 | this.finish_completing({ | |
148 |
|
|
152 | matches: [], | |
149 | matched_text: "" |
|
153 | matched_text: "" | |
150 | }); |
|
154 | }); | |
151 | } else { |
|
155 | } else { | |
152 | this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this)); |
|
156 | var cursor_pos = IPython.utils.absolute_cursor_pos(this.editor, cur); | |
|
157 | this.cell.kernel.complete(this.editor.getValue(), cursor_pos, | |||
|
158 | $.proxy(this.finish_completing, this) | |||
|
159 | ); | |||
153 | } |
|
160 | } | |
154 | }; |
|
161 | }; | |
155 |
|
162 |
@@ -1,9 +1,6 b'' | |||||
1 | //---------------------------------------------------------------------------- |
|
1 | // Copyright (c) IPython Development Team. | |
2 | // Copyright (C) 2008-2011 The IPython Development Team |
|
2 | // Distributed under the terms of the Modified BSD License. | |
3 | // |
|
3 | ||
4 | // Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | // the file COPYING, distributed as part of this software. |
|
|||
6 | //---------------------------------------------------------------------------- |
|
|||
7 | //============================================================================ |
|
4 | //============================================================================ | |
8 | // Tooltip |
|
5 | // Tooltip | |
9 | //============================================================================ |
|
6 | //============================================================================ | |
@@ -105,8 +102,8 b' var IPython = (function (IPython) {' | |||||
105 | this.tooltip.append(this.text); |
|
102 | this.tooltip.append(this.text); | |
106 |
|
103 | |||
107 | // function that will be called if you press tab 1, 2, 3... times in a row |
|
104 | // function that will be called if you press tab 1, 2, 3... times in a row | |
108 | this.tabs_functions = [function (cell, text) { |
|
105 | this.tabs_functions = [function (cell, text, cursor) { | |
109 | that._request_tooltip(cell, text); |
|
106 | that._request_tooltip(cell, text, cursor); | |
110 | }, function () { |
|
107 | }, function () { | |
111 | that.expand(); |
|
108 | that.expand(); | |
112 | }, function () { |
|
109 | }, function () { | |
@@ -131,13 +128,10 b' var IPython = (function (IPython) {' | |||||
131 | Tooltip.prototype.showInPager = function (cell) { |
|
128 | Tooltip.prototype.showInPager = function (cell) { | |
132 | // reexecute last call in pager by appending ? to show back in pager |
|
129 | // reexecute last call in pager by appending ? to show back in pager | |
133 | var that = this; |
|
130 | var that = this; | |
134 |
var |
|
131 | var payload = {}; | |
135 | 'payload' : { |
|
132 | payload.text = that._reply.content.data['text/plain']; | |
136 | 'page' : $.proxy(cell._open_with_pager, cell) |
|
133 | ||
137 | } |
|
134 | $([IPython.events]).trigger('open_with_text.Pager', payload); | |
138 | } |
|
|||
139 | }; |
|
|||
140 | cell.kernel.execute(that.name + '?', callbacks, {'silent': false, 'store_history': true}); |
|
|||
141 | this.remove_and_cancel_tooltip(); |
|
135 | this.remove_and_cancel_tooltip(); | |
142 | }; |
|
136 | }; | |
143 |
|
137 | |||
@@ -222,10 +216,9 b' var IPython = (function (IPython) {' | |||||
222 | return Tooltip.last_token_re.exec(line); |
|
216 | return Tooltip.last_token_re.exec(line); | |
223 | }; |
|
217 | }; | |
224 |
|
218 | |||
225 |
Tooltip.prototype._request_tooltip = function (cell, |
|
219 | Tooltip.prototype._request_tooltip = function (cell, text, cursor_pos) { | |
226 | var callbacks = $.proxy(this._show, this); |
|
220 | var callbacks = $.proxy(this._show, this); | |
227 | var oir_token = this.extract_oir_token(line); |
|
221 | var msg_id = cell.kernel.object_info(text, cursor_pos, callbacks); | |
228 | var msg_id = cell.kernel.object_info(oir_token, callbacks); |
|
|||
229 | }; |
|
222 | }; | |
230 |
|
223 | |||
231 | // make an imediate completion request |
|
224 | // make an imediate completion request | |
@@ -236,10 +229,8 b' var IPython = (function (IPython) {' | |||||
236 | this.cancel_pending(); |
|
229 | this.cancel_pending(); | |
237 | var editor = cell.code_mirror; |
|
230 | var editor = cell.code_mirror; | |
238 | var cursor = editor.getCursor(); |
|
231 | var cursor = editor.getCursor(); | |
239 | var text = editor.getRange({ |
|
232 | var cursor_pos = IPython.utils.absolute_cursor_pos(editor, cursor); | |
240 | line: cursor.line, |
|
233 | var text = cell.get_text(); | |
241 | ch: 0 |
|
|||
242 | }, cursor).trim(); |
|
|||
243 |
|
234 | |||
244 | this._hide_if_no_docstring = hide_if_no_docstring; |
|
235 | this._hide_if_no_docstring = hide_if_no_docstring; | |
245 |
|
236 | |||
@@ -260,16 +251,16 b' var IPython = (function (IPython) {' | |||||
260 | this.reset_tabs_function (cell, text); |
|
251 | this.reset_tabs_function (cell, text); | |
261 | } |
|
252 | } | |
262 |
|
253 | |||
263 |
// don't do anything if line beg |
|
254 | // don't do anything if line begins with '(' or is empty | |
264 | if (text === "" || text === "(") { |
|
255 | // if (text === "" || text === "(") { | |
265 | return; |
|
256 | // return; | |
266 | } |
|
257 | // } | |
267 |
|
258 | |||
268 | this.tabs_functions[this._consecutive_counter](cell, text); |
|
259 | this.tabs_functions[this._consecutive_counter](cell, text, cursor_pos); | |
269 |
|
260 | |||
270 | // then if we are at the end of list function, reset |
|
261 | // then if we are at the end of list function, reset | |
271 | if (this._consecutive_counter == this.tabs_functions.length) { |
|
262 | if (this._consecutive_counter == this.tabs_functions.length) { | |
272 | this.reset_tabs_function (cell, text); |
|
263 | this.reset_tabs_function (cell, text, cursor); | |
273 | } |
|
264 | } | |
274 |
|
265 | |||
275 | return; |
|
266 | return; | |
@@ -302,6 +293,7 b' var IPython = (function (IPython) {' | |||||
302 | Tooltip.prototype._show = function (reply) { |
|
293 | Tooltip.prototype._show = function (reply) { | |
303 | // move the bubble if it is not hidden |
|
294 | // move the bubble if it is not hidden | |
304 | // otherwise fade it |
|
295 | // otherwise fade it | |
|
296 | this._reply = reply; | |||
305 | var content = reply.content; |
|
297 | var content = reply.content; | |
306 | if (!content.found) { |
|
298 | if (!content.found) { | |
307 | // object not found, nothing to show |
|
299 | // object not found, nothing to show | |
@@ -339,42 +331,13 b' var IPython = (function (IPython) {' | |||||
339 | 'left': posarrowleft + 'px' |
|
331 | 'left': posarrowleft + 'px' | |
340 | }); |
|
332 | }); | |
341 |
|
333 | |||
342 | // build docstring |
|
|||
343 | var defstring = content.call_def; |
|
|||
344 | if (!defstring) { |
|
|||
345 | defstring = content.init_definition; |
|
|||
346 | } |
|
|||
347 | if (!defstring) { |
|
|||
348 | defstring = content.definition; |
|
|||
349 | } |
|
|||
350 |
|
||||
351 | var docstring = content.call_docstring; |
|
|||
352 | if (!docstring) { |
|
|||
353 | docstring = content.init_docstring; |
|
|||
354 | } |
|
|||
355 | if (!docstring) { |
|
|||
356 | docstring = content.docstring; |
|
|||
357 | } |
|
|||
358 |
|
||||
359 | if (!docstring) { |
|
|||
360 | // For reals this time, no docstring |
|
|||
361 | if (this._hide_if_no_docstring) { |
|
|||
362 | return; |
|
|||
363 | } else { |
|
|||
364 | docstring = "<empty docstring>"; |
|
|||
365 | } |
|
|||
366 | } |
|
|||
367 |
|
||||
368 | this._hidden = false; |
|
334 | this._hidden = false; | |
369 | this.tooltip.fadeIn('fast'); |
|
335 | this.tooltip.fadeIn('fast'); | |
370 | this.text.children().remove(); |
|
336 | this.text.children().remove(); | |
371 |
|
337 | |||
|
338 | // This should support rich data types, but only text/plain for now | |||
372 | // Any HTML within the docstring is escaped by the fixConsole() method. |
|
339 | // Any HTML within the docstring is escaped by the fixConsole() method. | |
373 |
var pre = $('<pre/>').html(utils.fixConsole( |
|
340 | var pre = $('<pre/>').html(utils.fixConsole(content.data['text/plain'])); | |
374 | if (defstring) { |
|
|||
375 | var defstring_html = $('<pre/>').html(utils.fixConsole(defstring)); |
|
|||
376 | this.text.append(defstring_html); |
|
|||
377 | } |
|
|||
378 | this.text.append(pre); |
|
341 | this.text.append(pre); | |
379 | // keep scroll top to be sure to always see the first line |
|
342 | // keep scroll top to be sure to always see the first line | |
380 | this.text.scrollTop(0); |
|
343 | this.text.scrollTop(0); |
@@ -263,7 +263,8 b' var IPython = (function (IPython) {' | |||||
263 | /** |
|
263 | /** | |
264 | * Get info on an object |
|
264 | * Get info on an object | |
265 | * |
|
265 | * | |
266 |
* @param |
|
266 | * @param code {string} | |
|
267 | * @param cursor_pos {integer} | |||
267 | * @param callback {function} |
|
268 | * @param callback {function} | |
268 | * @method object_info |
|
269 | * @method object_info | |
269 | * |
|
270 | * | |
@@ -271,20 +272,18 b' var IPython = (function (IPython) {' | |||||
271 | * The callback will be passed the complete `object_info_reply` message documented |
|
272 | * The callback will be passed the complete `object_info_reply` message documented | |
272 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) |
|
273 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#object-information) | |
273 | */ |
|
274 | */ | |
274 |
Kernel.prototype.object_info = function ( |
|
275 | Kernel.prototype.object_info = function (code, cursor_pos, callback) { | |
275 | var callbacks; |
|
276 | var callbacks; | |
276 | if (callback) { |
|
277 | if (callback) { | |
277 | callbacks = { shell : { reply : callback } }; |
|
278 | callbacks = { shell : { reply : callback } }; | |
278 | } |
|
279 | } | |
279 |
|
280 | |||
280 | if (typeof(objname) !== null && objname !== null) { |
|
|||
281 |
|
|
281 | var content = { | |
282 | oname : objname.toString(), |
|
282 | code : code, | |
|
283 | cursor_pos : cursor_pos, | |||
283 |
|
|
284 | detail_level : 0, | |
284 |
|
|
285 | }; | |
285 |
|
|
286 | return this.send_shell_message("object_info_request", content, callbacks); | |
286 | } |
|
|||
287 | return; |
|
|||
288 | }; |
|
287 | }; | |
289 |
|
288 | |||
290 | /** |
|
289 | /** | |
@@ -360,21 +359,19 b' var IPython = (function (IPython) {' | |||||
360 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete) |
|
359 | * [here](http://ipython.org/ipython-doc/dev/development/messaging.html#complete) | |
361 | * |
|
360 | * | |
362 | * @method complete |
|
361 | * @method complete | |
363 |
* @param |
|
362 | * @param code {string} | |
364 | * @param cursor_pos {integer} |
|
363 | * @param cursor_pos {integer} | |
365 | * @param callback {function} |
|
364 | * @param callback {function} | |
366 | * |
|
365 | * | |
367 | */ |
|
366 | */ | |
368 |
Kernel.prototype.complete = function ( |
|
367 | Kernel.prototype.complete = function (code, cursor_pos, callback) { | |
369 | var callbacks; |
|
368 | var callbacks; | |
370 | if (callback) { |
|
369 | if (callback) { | |
371 | callbacks = { shell : { reply : callback } }; |
|
370 | callbacks = { shell : { reply : callback } }; | |
372 | } |
|
371 | } | |
373 | var content = { |
|
372 | var content = { | |
374 |
|
|
373 | code : code, | |
375 | line : line, |
|
374 | cursor_pos : cursor_pos, | |
376 | block : null, |
|
|||
377 | cursor_pos : cursor_pos |
|
|||
378 | }; |
|
375 | }; | |
379 | return this.send_shell_message("complete_request", content, callbacks); |
|
376 | return this.send_shell_message("complete_request", content, callbacks); | |
380 | }; |
|
377 | }; |
@@ -270,38 +270,38 b' class ShellChannel(ZMQSocketChannel):' | |||||
270 | self._queue_send(msg) |
|
270 | self._queue_send(msg) | |
271 | return msg['header']['msg_id'] |
|
271 | return msg['header']['msg_id'] | |
272 |
|
272 | |||
273 |
def complete(self, |
|
273 | def complete(self, code, cursor_pos=0, block=None): | |
274 | """Tab complete text in the kernel's namespace. |
|
274 | """Tab complete text in the kernel's namespace. | |
275 |
|
275 | |||
276 | Parameters |
|
276 | Parameters | |
277 | ---------- |
|
277 | ---------- | |
278 |
|
|
278 | code : str | |
279 |
The |
|
279 | The context in which completion is requested. | |
280 | line : str |
|
280 | Can be anything between a variable name and an entire cell. | |
281 | The full line of text that is the surrounding context for the |
|
281 | cursor_pos : int, optional | |
282 | text to complete. |
|
282 | The position of the cursor in the block of code where the completion was requested. | |
283 | cursor_pos : int |
|
|||
284 | The position of the cursor in the line where the completion was |
|
|||
285 | requested. |
|
|||
286 | block : str, optional |
|
|||
287 | The full block of code in which the completion is being requested. |
|
|||
288 |
|
283 | |||
289 | Returns |
|
284 | Returns | |
290 | ------- |
|
285 | ------- | |
291 | The msg_id of the message sent. |
|
286 | The msg_id of the message sent. | |
292 | """ |
|
287 | """ | |
293 |
content = dict( |
|
288 | content = dict(code=code, cursor_pos=cursor_pos) | |
294 | msg = self.session.msg('complete_request', content) |
|
289 | msg = self.session.msg('complete_request', content) | |
295 | self._queue_send(msg) |
|
290 | self._queue_send(msg) | |
296 | return msg['header']['msg_id'] |
|
291 | return msg['header']['msg_id'] | |
297 |
|
292 | |||
298 |
def object_info(self, |
|
293 | def object_info(self, code, cursor_pos=0, detail_level=0): | |
299 | """Get metadata information about an object in the kernel's namespace. |
|
294 | """Get metadata information about an object in the kernel's namespace. | |
300 |
|
295 | |||
|
296 | It is up to the kernel to determine the appropriate object to inspect. | |||
|
297 | ||||
301 | Parameters |
|
298 | Parameters | |
302 | ---------- |
|
299 | ---------- | |
303 |
|
|
300 | code : str | |
304 | A string specifying the object name. |
|
301 | The context in which info is requested. | |
|
302 | Can be anything between a variable name and an entire cell. | |||
|
303 | cursor_pos : int, optional | |||
|
304 | The position of the cursor in the block of code where the info was requested. | |||
305 | detail_level : int, optional |
|
305 | detail_level : int, optional | |
306 | The level of detail for the introspection (0-2) |
|
306 | The level of detail for the introspection (0-2) | |
307 |
|
307 | |||
@@ -309,7 +309,9 b' class ShellChannel(ZMQSocketChannel):' | |||||
309 | ------- |
|
309 | ------- | |
310 | The msg_id of the message sent. |
|
310 | The msg_id of the message sent. | |
311 | """ |
|
311 | """ | |
312 | content = dict(oname=oname, detail_level=detail_level) |
|
312 | content = dict(code=code, cursor_pos=cursor_pos, | |
|
313 | detail_level=detail_level, | |||
|
314 | ) | |||
313 | msg = self.session.msg('object_info_request', content) |
|
315 | msg = self.session.msg('object_info_request', content) | |
314 | self._queue_send(msg) |
|
316 | self._queue_send(msg) | |
315 | return msg['header']['msg_id'] |
|
317 | return msg['header']['msg_id'] |
@@ -94,14 +94,16 b' class InProcessShellChannel(InProcessChannel):' | |||||
94 | self._dispatch_to_kernel(msg) |
|
94 | self._dispatch_to_kernel(msg) | |
95 | return msg['header']['msg_id'] |
|
95 | return msg['header']['msg_id'] | |
96 |
|
96 | |||
97 |
def complete(self, |
|
97 | def complete(self, code, cursor_pos=0): | |
98 |
content = dict( |
|
98 | content = dict(code=code, cursor_pos=cursor_pos) | |
99 | msg = self.client.session.msg('complete_request', content) |
|
99 | msg = self.client.session.msg('complete_request', content) | |
100 | self._dispatch_to_kernel(msg) |
|
100 | self._dispatch_to_kernel(msg) | |
101 | return msg['header']['msg_id'] |
|
101 | return msg['header']['msg_id'] | |
102 |
|
102 | |||
103 |
def object_info(self, |
|
103 | def object_info(self, code, cursor_pos=0, detail_level=0): | |
104 | content = dict(oname=oname, detail_level=detail_level) |
|
104 | content = dict(code=code, cursor_pos=cursor_pos, | |
|
105 | detail_level=detail_level, | |||
|
106 | ) | |||
105 | msg = self.client.session.msg('object_info_request', content) |
|
107 | msg = self.client.session.msg('object_info_request', content) | |
106 | self._dispatch_to_kernel(msg) |
|
108 | self._dispatch_to_kernel(msg) | |
107 | return msg['header']['msg_id'] |
|
109 | return msg['header']['msg_id'] |
@@ -1,19 +1,10 b'' | |||||
1 | #------------------------------------------------------------------------------- |
|
1 | # Copyright (c) IPython Development Team. | |
2 | # Copyright (C) 2012 The IPython Development Team |
|
2 | # Distributed under the terms of the Modified BSD License. | |
3 | # |
|
|||
4 | # Distributed under the terms of the BSD License. The full license is in |
|
|||
5 | # the file COPYING, distributed as part of this software. |
|
|||
6 | #------------------------------------------------------------------------------- |
|
|||
7 |
|
3 | |||
8 | #----------------------------------------------------------------------------- |
|
|||
9 | # Imports |
|
|||
10 | #----------------------------------------------------------------------------- |
|
|||
11 | from __future__ import print_function |
|
4 | from __future__ import print_function | |
12 |
|
5 | |||
13 | # Standard library imports |
|
|||
14 | import unittest |
|
6 | import unittest | |
15 |
|
7 | |||
16 | # Local imports |
|
|||
17 | from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient |
|
8 | from IPython.kernel.inprocess.blocking import BlockingInProcessKernelClient | |
18 | from IPython.kernel.inprocess.manager import InProcessKernelManager |
|
9 | from IPython.kernel.inprocess.manager import InProcessKernelManager | |
19 |
|
10 | |||
@@ -71,7 +62,7 b' class InProcessKernelManagerTestCase(unittest.TestCase):' | |||||
71 | kc = BlockingInProcessKernelClient(kernel=km.kernel) |
|
62 | kc = BlockingInProcessKernelClient(kernel=km.kernel) | |
72 | kc.start_channels() |
|
63 | kc.start_channels() | |
73 | km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) |
|
64 | km.kernel.shell.push({'my_bar': 0, 'my_baz': 1}) | |
74 |
kc.complete('my_ba', |
|
65 | kc.complete('my_ba', 5) | |
75 | msg = kc.get_shell_msg() |
|
66 | msg = kc.get_shell_msg() | |
76 | self.assertEqual(msg['header']['msg_type'], 'complete_reply') |
|
67 | self.assertEqual(msg['header']['msg_type'], 'complete_reply') | |
77 | self.assertEqual(sorted(msg['content']['matches']), |
|
68 | self.assertEqual(sorted(msg['content']['matches']), | |
@@ -87,9 +78,12 b' class InProcessKernelManagerTestCase(unittest.TestCase):' | |||||
87 | km.kernel.shell.user_ns['foo'] = 1 |
|
78 | km.kernel.shell.user_ns['foo'] = 1 | |
88 | kc.object_info('foo') |
|
79 | kc.object_info('foo') | |
89 | msg = kc.get_shell_msg() |
|
80 | msg = kc.get_shell_msg() | |
90 |
self.assertEqual |
|
81 | self.assertEqual(msg['header']['msg_type'], 'object_info_reply') | |
91 | self.assertEquals(msg['content']['name'], 'foo') |
|
82 | content = msg['content'] | |
92 | self.assertEquals(msg['content']['type_name'], 'int') |
|
83 | assert content['found'] | |
|
84 | self.assertEqual(content['name'], 'foo') | |||
|
85 | text = content['data']['text/plain'] | |||
|
86 | self.assertIn('int', text) | |||
93 |
|
87 | |||
94 | def test_history(self): |
|
88 | def test_history(self): | |
95 | """ Does requesting history from an in-process kernel work? |
|
89 | """ Does requesting history from an in-process kernel work? |
@@ -85,6 +85,17 b' class RHeader(Reference):' | |||||
85 | username = Unicode() |
|
85 | username = Unicode() | |
86 | version = Version('5.0') |
|
86 | version = Version('5.0') | |
87 |
|
87 | |||
|
88 | mime_pat = re.compile(r'\w+/\w+') | |||
|
89 | ||||
|
90 | class MimeBundle(Reference): | |||
|
91 | metadata = Dict() | |||
|
92 | data = Dict() | |||
|
93 | def _data_changed(self, name, old, new): | |||
|
94 | for k,v in iteritems(new): | |||
|
95 | assert mime_pat.match(k) | |||
|
96 | nt.assert_is_instance(v, string_types) | |||
|
97 | ||||
|
98 | # shell replies | |||
88 |
|
99 | |||
89 | class ExecuteReply(Reference): |
|
100 | class ExecuteReply(Reference): | |
90 | execution_count = Integer() |
|
101 | execution_count = Integer() | |
@@ -109,31 +120,9 b' class ExecuteReplyError(Reference):' | |||||
109 | traceback = List(Unicode) |
|
120 | traceback = List(Unicode) | |
110 |
|
121 | |||
111 |
|
122 | |||
112 |
class OInfoReply( |
|
123 | class OInfoReply(MimeBundle): | |
113 | name = Unicode() |
|
124 | name = Unicode() | |
114 | found = Bool() |
|
125 | found = Bool() | |
115 | ismagic = Bool() |
|
|||
116 | isalias = Bool() |
|
|||
117 | namespace = Enum((u'builtin', u'magics', u'alias', u'Interactive')) |
|
|||
118 | type_name = Unicode() |
|
|||
119 | string_form = Unicode() |
|
|||
120 | base_class = Unicode() |
|
|||
121 | length = Integer() |
|
|||
122 | file = Unicode() |
|
|||
123 | definition = Unicode() |
|
|||
124 | argspec = Dict() |
|
|||
125 | init_definition = Unicode() |
|
|||
126 | docstring = Unicode() |
|
|||
127 | init_docstring = Unicode() |
|
|||
128 | class_docstring = Unicode() |
|
|||
129 | call_def = Unicode() |
|
|||
130 | call_docstring = Unicode() |
|
|||
131 | source = Unicode() |
|
|||
132 |
|
||||
133 | def check(self, d): |
|
|||
134 | super(OInfoReply, self).check(d) |
|
|||
135 | if d['argspec'] is not None: |
|
|||
136 | ArgSpec().check(d['argspec']) |
|
|||
137 |
|
126 | |||
138 |
|
127 | |||
139 | class ArgSpec(Reference): |
|
128 | class ArgSpec(Reference): | |
@@ -173,25 +162,12 b' class Stream(Reference):' | |||||
173 | data = Unicode() |
|
162 | data = Unicode() | |
174 |
|
163 | |||
175 |
|
164 | |||
176 | mime_pat = re.compile(r'\w+/\w+') |
|
165 | class DisplayData(MimeBundle): | |
177 |
|
||||
178 | class DisplayData(Reference): |
|
|||
179 | source = Unicode() |
|
166 | source = Unicode() | |
180 | metadata = Dict() |
|
|||
181 | data = Dict() |
|
|||
182 | def _data_changed(self, name, old, new): |
|
|||
183 | for k,v in iteritems(new): |
|
|||
184 | assert mime_pat.match(k) |
|
|||
185 | nt.assert_is_instance(v, string_types) |
|
|||
186 |
|
167 | |||
187 |
|
168 | |||
188 |
class ExecuteResult( |
|
169 | class ExecuteResult(MimeBundle): | |
189 | execution_count = Integer() |
|
170 | execution_count = Integer() | |
190 | data = Dict() |
|
|||
191 | def _data_changed(self, name, old, new): |
|
|||
192 | for k,v in iteritems(new): |
|
|||
193 | assert mime_pat.match(k) |
|
|||
194 | nt.assert_is_instance(v, string_types) |
|
|||
195 |
|
171 | |||
196 |
|
172 | |||
197 | references = { |
|
173 | references = { | |
@@ -334,8 +310,10 b' def test_oinfo_found():' | |||||
334 | validate_message(reply, 'object_info_reply', msg_id) |
|
310 | validate_message(reply, 'object_info_reply', msg_id) | |
335 | content = reply['content'] |
|
311 | content = reply['content'] | |
336 | assert content['found'] |
|
312 | assert content['found'] | |
337 | argspec = content['argspec'] |
|
313 | nt.assert_equal(content['name'], 'a') | |
338 | nt.assert_is(argspec, None) |
|
314 | text = content['data']['text/plain'] | |
|
315 | nt.assert_in('Type:', text) | |||
|
316 | nt.assert_in('Docstring:', text) | |||
339 |
|
317 | |||
340 |
|
318 | |||
341 | def test_oinfo_detail(): |
|
319 | def test_oinfo_detail(): | |
@@ -343,14 +321,15 b' def test_oinfo_detail():' | |||||
343 |
|
321 | |||
344 | msg_id, reply = execute(code='ip=get_ipython()') |
|
322 | msg_id, reply = execute(code='ip=get_ipython()') | |
345 |
|
323 | |||
346 |
msg_id = KC.object_info('ip.object_inspect', detail_level= |
|
324 | msg_id = KC.object_info('ip.object_inspect', cursor_pos=10, detail_level=1) | |
347 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
325 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
348 | validate_message(reply, 'object_info_reply', msg_id) |
|
326 | validate_message(reply, 'object_info_reply', msg_id) | |
349 | content = reply['content'] |
|
327 | content = reply['content'] | |
350 | assert content['found'] |
|
328 | assert content['found'] | |
351 | argspec = content['argspec'] |
|
329 | nt.assert_equal(content['name'], 'ip.object_inspect') | |
352 | nt.assert_is_instance(argspec, dict, "expected non-empty argspec dict, got %r" % argspec) |
|
330 | text = content['data']['text/plain'] | |
353 | nt.assert_equal(argspec['defaults'], [0]) |
|
331 | nt.assert_in('Definition:', text) | |
|
332 | nt.assert_in('Source:', text) | |||
354 |
|
333 | |||
355 |
|
334 | |||
356 | def test_oinfo_not_found(): |
|
335 | def test_oinfo_not_found(): | |
@@ -368,7 +347,7 b' def test_complete():' | |||||
368 |
|
347 | |||
369 | msg_id, reply = execute(code="alpha = albert = 5") |
|
348 | msg_id, reply = execute(code="alpha = albert = 5") | |
370 |
|
349 | |||
371 |
msg_id = KC.complete('al', |
|
350 | msg_id = KC.complete('al', 2) | |
372 | reply = KC.get_shell_msg(timeout=TIMEOUT) |
|
351 | reply = KC.get_shell_msg(timeout=TIMEOUT) | |
373 | validate_message(reply, 'complete_reply', msg_id) |
|
352 | validate_message(reply, 'complete_reply', msg_id) | |
374 | matches = reply['content']['matches'] |
|
353 | matches = reply['content']['matches'] |
@@ -1,4 +1,3 b'' | |||||
1 | #!/usr/bin/env python |
|
|||
2 |
|
|
1 | """An interactive kernel that talks to frontends over 0MQ.""" | |
3 |
|
2 | |||
4 | # Copyright (c) IPython Development Team. |
|
3 | # Copyright (c) IPython Development Team. | |
@@ -28,6 +27,7 b' from IPython.core import release' | |||||
28 | from IPython.utils import py3compat |
|
27 | from IPython.utils import py3compat | |
29 | from IPython.utils.py3compat import builtin_mod, unicode_type, string_types |
|
28 | from IPython.utils.py3compat import builtin_mod, unicode_type, string_types | |
30 | from IPython.utils.jsonutil import json_clean |
|
29 | from IPython.utils.jsonutil import json_clean | |
|
30 | from IPython.utils.tokenutil import token_at_cursor | |||
31 | from IPython.utils.traitlets import ( |
|
31 | from IPython.utils.traitlets import ( | |
32 | Any, Instance, Float, Dict, List, Set, Integer, Unicode, |
|
32 | Any, Instance, Float, Dict, List, Set, Integer, Unicode, | |
33 | Type, Bool, |
|
33 | Type, Bool, | |
@@ -243,6 +243,7 b' class Kernel(Configurable):' | |||||
243 | else: |
|
243 | else: | |
244 | # ensure default_int_handler during handler call |
|
244 | # ensure default_int_handler during handler call | |
245 | sig = signal(SIGINT, default_int_handler) |
|
245 | sig = signal(SIGINT, default_int_handler) | |
|
246 | self.log.debug("%s: %s", msg_type, msg) | |||
246 | try: |
|
247 | try: | |
247 | handler(stream, idents, msg) |
|
248 | handler(stream, idents, msg) | |
248 | except Exception: |
|
249 | except Exception: | |
@@ -489,7 +490,11 b' class Kernel(Configurable):' | |||||
489 | self._publish_status(u'idle', parent) |
|
490 | self._publish_status(u'idle', parent) | |
490 |
|
491 | |||
491 | def complete_request(self, stream, ident, parent): |
|
492 | def complete_request(self, stream, ident, parent): | |
492 | txt, matches = self._complete(parent) |
|
493 | content = parent['content'] | |
|
494 | code = content['code'] | |||
|
495 | cursor_pos = content['cursor_pos'] | |||
|
496 | ||||
|
497 | txt, matches = self.shell.complete('', code, cursor_pos) | |||
493 | matches = {'matches' : matches, |
|
498 | matches = {'matches' : matches, | |
494 | 'matched_text' : txt, |
|
499 | 'matched_text' : txt, | |
495 | 'status' : 'ok'} |
|
500 | 'status' : 'ok'} | |
@@ -500,13 +505,25 b' class Kernel(Configurable):' | |||||
500 |
|
505 | |||
501 | def object_info_request(self, stream, ident, parent): |
|
506 | def object_info_request(self, stream, ident, parent): | |
502 | content = parent['content'] |
|
507 | content = parent['content'] | |
503 | object_info = self.shell.object_inspect(content['oname'], |
|
508 | ||
504 | detail_level = content.get('detail_level', 0) |
|
509 | name = token_at_cursor(content['code'], content['cursor_pos']) | |
|
510 | info = self.shell.object_inspect(name) | |||
|
511 | ||||
|
512 | reply_content = {'status' : 'ok'} | |||
|
513 | reply_content['data'] = data = {} | |||
|
514 | reply_content['metadata'] = {} | |||
|
515 | reply_content['name'] = name | |||
|
516 | reply_content['found'] = info['found'] | |||
|
517 | if info['found']: | |||
|
518 | info_text = self.shell.object_inspect_text( | |||
|
519 | name, | |||
|
520 | detail_level=content.get('detail_level', 0), | |||
505 | ) |
|
521 | ) | |
|
522 | reply_content['data']['text/plain'] = info_text | |||
506 | # Before we send this object over, we scrub it for JSON usage |
|
523 | # Before we send this object over, we scrub it for JSON usage | |
507 |
|
|
524 | reply_content = json_clean(reply_content) | |
508 | msg = self.session.send(stream, 'object_info_reply', |
|
525 | msg = self.session.send(stream, 'object_info_reply', | |
509 |
|
|
526 | reply_content, parent, ident) | |
510 | self.log.debug("%s", msg) |
|
527 | self.log.debug("%s", msg) | |
511 |
|
528 | |||
512 | def history_request(self, stream, ident, parent): |
|
529 | def history_request(self, stream, ident, parent): | |
@@ -822,19 +839,6 b' class Kernel(Configurable):' | |||||
822 | raise EOFError |
|
839 | raise EOFError | |
823 | return value |
|
840 | return value | |
824 |
|
841 | |||
825 | def _complete(self, msg): |
|
|||
826 | c = msg['content'] |
|
|||
827 | try: |
|
|||
828 | cpos = int(c['cursor_pos']) |
|
|||
829 | except: |
|
|||
830 | # If we don't get something that we can convert to an integer, at |
|
|||
831 | # least attempt the completion guessing the cursor is at the end of |
|
|||
832 | # the text, if there's any, and otherwise of the line |
|
|||
833 | cpos = len(c['text']) |
|
|||
834 | if cpos==0: |
|
|||
835 | cpos = len(c['line']) |
|
|||
836 | return self.shell.complete(c['text'], c['line'], cpos) |
|
|||
837 |
|
||||
838 | def _at_shutdown(self): |
|
842 | def _at_shutdown(self): | |
839 | """Actions taken at shutdown by the kernel, called by python's atexit. |
|
843 | """Actions taken at shutdown by the kernel, called by python's atexit. | |
840 | """ |
|
844 | """ |
@@ -128,7 +128,8 b' def test_embed_kernel_basic():' | |||||
128 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
128 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
129 | content = msg['content'] |
|
129 | content = msg['content'] | |
130 | nt.assert_true(content['found']) |
|
130 | nt.assert_true(content['found']) | |
131 | nt.assert_equal(content['string_form'], u'10') |
|
131 | text = content['data']['text/plain'] | |
|
132 | nt.assert_in('10', text) | |||
132 |
|
133 | |||
133 | def test_embed_kernel_namespace(): |
|
134 | def test_embed_kernel_namespace(): | |
134 | """IPython.embed_kernel() inherits calling namespace""" |
|
135 | """IPython.embed_kernel() inherits calling namespace""" | |
@@ -148,14 +149,16 b' def test_embed_kernel_namespace():' | |||||
148 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
149 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
149 | content = msg['content'] |
|
150 | content = msg['content'] | |
150 | nt.assert_true(content['found']) |
|
151 | nt.assert_true(content['found']) | |
151 | nt.assert_equal(content['string_form'], u'5') |
|
152 | text = content['data']['text/plain'] | |
|
153 | nt.assert_in(u'5', text) | |||
152 |
|
154 | |||
153 | # oinfo b (str) |
|
155 | # oinfo b (str) | |
154 | msg_id = client.object_info('b') |
|
156 | msg_id = client.object_info('b') | |
155 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
157 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
156 | content = msg['content'] |
|
158 | content = msg['content'] | |
157 | nt.assert_true(content['found']) |
|
159 | nt.assert_true(content['found']) | |
158 | nt.assert_equal(content['string_form'], u'hi there') |
|
160 | text = content['data']['text/plain'] | |
|
161 | nt.assert_in(u'hi there', text) | |||
159 |
|
162 | |||
160 | # oinfo c (undefined) |
|
163 | # oinfo c (undefined) | |
161 | msg_id = client.object_info('c') |
|
164 | msg_id = client.object_info('c') | |
@@ -184,7 +187,8 b' def test_embed_kernel_reentrant():' | |||||
184 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
187 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
185 | content = msg['content'] |
|
188 | content = msg['content'] | |
186 | nt.assert_true(content['found']) |
|
189 | nt.assert_true(content['found']) | |
187 | nt.assert_equal(content['string_form'], unicode_type(i)) |
|
190 | text = content['data']['text/plain'] | |
|
191 | nt.assert_in(unicode_type(i), text) | |||
188 |
|
192 | |||
189 | # exit from embed_kernel |
|
193 | # exit from embed_kernel | |
190 | client.execute("get_ipython().exit_now = True") |
|
194 | client.execute("get_ipython().exit_now = True") |
@@ -14,7 +14,8 b' def test_ipython_start_kernel_userns():' | |||||
14 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
14 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
15 | content = msg['content'] |
|
15 | content = msg['content'] | |
16 | assert content['found'] |
|
16 | assert content['found'] | |
17 | nt.assert_equal(content['string_form'], u'123') |
|
17 | text = content['data']['text/plain'] | |
|
18 | nt.assert_in(u'123', text) | |||
18 |
|
19 | |||
19 | # user_module should be an instance of DummyMod |
|
20 | # user_module should be an instance of DummyMod | |
20 | msg_id = client.execute("usermod = get_ipython().user_module") |
|
21 | msg_id = client.execute("usermod = get_ipython().user_module") | |
@@ -25,7 +26,8 b' def test_ipython_start_kernel_userns():' | |||||
25 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
26 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
26 | content = msg['content'] |
|
27 | content = msg['content'] | |
27 | assert content['found'] |
|
28 | assert content['found'] | |
28 | nt.assert_in('DummyMod', content['string_form']) |
|
29 | text = content['data']['text/plain'] | |
|
30 | nt.assert_in(u'DummyMod', text) | |||
29 |
|
31 | |||
30 | def test_ipython_start_kernel_no_userns(): |
|
32 | def test_ipython_start_kernel_no_userns(): | |
31 | # Issue #4188 - user_ns should be passed to shell as None, not {} |
|
33 | # Issue #4188 - user_ns should be passed to shell as None, not {} | |
@@ -42,4 +44,5 b' def test_ipython_start_kernel_no_userns():' | |||||
42 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) |
|
44 | msg = client.get_shell_msg(block=True, timeout=TIMEOUT) | |
43 | content = msg['content'] |
|
45 | content = msg['content'] | |
44 | assert content['found'] |
|
46 | assert content['found'] | |
45 | nt.assert_not_in('DummyMod', content['string_form']) |
|
47 | text = content['data']['text/plain'] | |
|
48 | nt.assert_not_in(u'DummyMod', text) |
@@ -1585,6 +1585,15 b" class ConsoleWidget(MetaQObjectHasTraits('NewBase', (LoggingConfigurable, QtGui." | |||||
1585 | text = self._get_block_plain_text(cursor.block()) |
|
1585 | text = self._get_block_plain_text(cursor.block()) | |
1586 | return text[len(prompt):] |
|
1586 | return text[len(prompt):] | |
1587 |
|
1587 | |||
|
1588 | def _get_input_buffer_cursor_pos(self): | |||
|
1589 | """Return the cursor position within the input buffer.""" | |||
|
1590 | cursor = self._control.textCursor() | |||
|
1591 | cursor.setPosition(self._prompt_pos, QtGui.QTextCursor.KeepAnchor) | |||
|
1592 | input_buffer = cursor.selection().toPlainText() | |||
|
1593 | ||||
|
1594 | # Don't count continuation prompts | |||
|
1595 | return len(input_buffer.replace('\n' + self._continuation_prompt, '\n')) | |||
|
1596 | ||||
1588 | def _get_input_buffer_cursor_prompt(self): |
|
1597 | def _get_input_buffer_cursor_prompt(self): | |
1589 | """ Returns the (plain text) prompt for line of the input buffer that |
|
1598 | """ Returns the (plain text) prompt for line of the input buffer that | |
1590 | contains the cursor, or None if there is no such line. |
|
1599 | contains the cursor, or None if there is no such line. |
@@ -727,17 +727,10 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
727 | # Decide if it makes sense to show a call tip |
|
727 | # Decide if it makes sense to show a call tip | |
728 | if not self.enable_calltips: |
|
728 | if not self.enable_calltips: | |
729 | return False |
|
729 | return False | |
730 | cursor = self._get_cursor() |
|
730 | cursor_pos = self._get_input_buffer_cursor_pos() | |
731 | cursor.movePosition(QtGui.QTextCursor.Left) |
|
731 | code = self.input_buffer | |
732 | if cursor.document().characterAt(cursor.position()) != '(': |
|
|||
733 | return False |
|
|||
734 | context = self._get_context(cursor) |
|
|||
735 | if not context: |
|
|||
736 | return False |
|
|||
737 |
|
||||
738 | # Send the metadata request to the kernel |
|
732 | # Send the metadata request to the kernel | |
739 | name = '.'.join(context) |
|
733 | msg_id = self.kernel_client.object_info(code, cursor_pos) | |
740 | msg_id = self.kernel_client.object_info(name) |
|
|||
741 | pos = self._get_cursor().position() |
|
734 | pos = self._get_cursor().position() | |
742 | self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos) |
|
735 | self._request_info['call_tip'] = self._CallTipRequest(msg_id, pos) | |
743 | return True |
|
736 | return True | |
@@ -749,10 +742,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
749 | if context: |
|
742 | if context: | |
750 | # Send the completion request to the kernel |
|
743 | # Send the completion request to the kernel | |
751 | msg_id = self.kernel_client.complete( |
|
744 | msg_id = self.kernel_client.complete( | |
752 | '.'.join(context), # text |
|
745 | code=self.input_buffer, | |
753 |
self._get_input_buffer_cursor_ |
|
746 | cursor_pos=self._get_input_buffer_cursor_pos(), | |
754 | self._get_input_buffer_cursor_column(), # cursor_pos |
|
747 | ) | |
755 | self.input_buffer) # block |
|
|||
756 | pos = self._get_cursor().position() |
|
748 | pos = self._get_cursor().position() | |
757 | info = self._CompletionRequest(msg_id, pos) |
|
749 | info = self._CompletionRequest(msg_id, pos) | |
758 | self._request_info['complete'] = info |
|
750 | self._request_info['complete'] = info |
@@ -327,24 +327,6 b' class IPythonWidget(FrontendWidget):' | |||||
327 | # 'FrontendWidget' protected interface |
|
327 | # 'FrontendWidget' protected interface | |
328 | #--------------------------------------------------------------------------- |
|
328 | #--------------------------------------------------------------------------- | |
329 |
|
329 | |||
330 | def _complete(self): |
|
|||
331 | """ Reimplemented to support IPython's improved completion machinery. |
|
|||
332 | """ |
|
|||
333 | # We let the kernel split the input line, so we *always* send an empty |
|
|||
334 | # text field. Readline-based frontends do get a real text field which |
|
|||
335 | # they can use. |
|
|||
336 | text = '' |
|
|||
337 |
|
||||
338 | # Send the completion request to the kernel |
|
|||
339 | msg_id = self.kernel_client.shell_channel.complete( |
|
|||
340 | text, # text |
|
|||
341 | self._get_input_buffer_cursor_line(), # line |
|
|||
342 | self._get_input_buffer_cursor_column(), # cursor_pos |
|
|||
343 | self.input_buffer) # block |
|
|||
344 | pos = self._get_cursor().position() |
|
|||
345 | info = self._CompletionRequest(msg_id, pos) |
|
|||
346 | self._request_info['complete'] = info |
|
|||
347 |
|
||||
348 | def _process_execute_error(self, msg): |
|
330 | def _process_execute_error(self, msg): | |
349 | """ Reimplemented for IPython-style traceback formatting. |
|
331 | """ Reimplemented for IPython-style traceback formatting. | |
350 | """ |
|
332 | """ |
@@ -1,6 +1,9 b'' | |||||
1 | """Adapt readline completer interface to make ZMQ request. |
|
|||
2 | """ |
|
|||
3 |
|
|
1 | # -*- coding: utf-8 -*- | |
|
2 | """Adapt readline completer interface to make ZMQ request.""" | |||
|
3 | ||||
|
4 | # Copyright (c) IPython Development Team. | |||
|
5 | # Distributed under the terms of the Modified BSD License. | |||
|
6 | ||||
4 | try: |
|
7 | try: | |
5 | from queue import Empty # Py 3 |
|
8 | from queue import Empty # Py 3 | |
6 | except ImportError: |
|
9 | except ImportError: | |
@@ -33,8 +36,10 b' class ZMQCompleter(IPCompleter):' | |||||
33 |
|
36 | |||
34 | # send completion request to kernel |
|
37 | # send completion request to kernel | |
35 | # Give the kernel up to 0.5s to respond |
|
38 | # Give the kernel up to 0.5s to respond | |
36 |
msg_id = self.client.shell_channel.complete( |
|
39 | msg_id = self.client.shell_channel.complete( | |
37 | cursor_pos=cursor_pos) |
|
40 | code=line, | |
|
41 | cursor_pos=cursor_pos, | |||
|
42 | ) | |||
38 |
|
43 | |||
39 | msg = self.client.shell_channel.get_msg(timeout=self.timeout) |
|
44 | msg = self.client.shell_channel.get_msg(timeout=self.timeout) | |
40 | if msg['parent_header']['msg_id'] == msg_id: |
|
45 | if msg['parent_header']['msg_id'] == msg_id: |
@@ -6,11 +6,16 b' import nose.tools as nt' | |||||
6 |
|
6 | |||
7 | from IPython.utils.tokenutil import token_at_cursor |
|
7 | from IPython.utils.tokenutil import token_at_cursor | |
8 |
|
8 | |||
9 |
def expect_token(expected, cell, c |
|
9 | def expect_token(expected, cell, cursor_pos): | |
10 |
token = token_at_cursor(cell, c |
|
10 | token = token_at_cursor(cell, cursor_pos) | |
11 |
|
11 | offset = 0 | ||
12 |
|
|
12 | for line in cell.splitlines(): | |
13 | line_with_cursor = '%s|%s' % (lines[line][:column], lines[line][column:]) |
|
13 | if offset + len(line) >= cursor_pos: | |
|
14 | break | |||
|
15 | else: | |||
|
16 | offset += len(line) | |||
|
17 | column = cursor_pos - offset | |||
|
18 | line_with_cursor = '%s|%s' % (line[:column], line[column:]) | |||
14 | line |
|
19 | line | |
15 | nt.assert_equal(token, expected, |
|
20 | nt.assert_equal(token, expected, | |
16 | "Excpected %r, got %r in: %s" % ( |
|
21 | "Excpected %r, got %r in: %s" % ( | |
@@ -34,11 +39,13 b' def test_multiline():' | |||||
34 | 'b = hello("string", there)' |
|
39 | 'b = hello("string", there)' | |
35 | ]) |
|
40 | ]) | |
36 | expected = 'hello' |
|
41 | expected = 'hello' | |
37 | for i in range(4,9): |
|
42 | start = cell.index(expected) | |
38 | expect_token(expected, cell, i, 1) |
|
43 | for i in range(start, start + len(expected)): | |
|
44 | expect_token(expected, cell, i) | |||
39 | expected = 'there' |
|
45 | expected = 'there' | |
40 | for i in range(21,27): |
|
46 | start = cell.index(expected) | |
41 | expect_token(expected, cell, i, 1) |
|
47 | for i in range(start, start + len(expected)): | |
|
48 | expect_token(expected, cell, i) | |||
42 |
|
49 | |||
43 | def test_attrs(): |
|
50 | def test_attrs(): | |
44 | cell = "foo(a=obj.attr.subattr)" |
|
51 | cell = "foo(a=obj.attr.subattr)" |
@@ -23,7 +23,7 b' def generate_tokens(readline):' | |||||
23 | # catch EOF error |
|
23 | # catch EOF error | |
24 | return |
|
24 | return | |
25 |
|
25 | |||
26 |
def token_at_cursor(cell, c |
|
26 | def token_at_cursor(cell, cursor_pos=0): | |
27 | """Get the token at a given cursor |
|
27 | """Get the token at a given cursor | |
28 |
|
28 | |||
29 | Used for introspection. |
|
29 | Used for introspection. | |
@@ -33,15 +33,13 b' def token_at_cursor(cell, column, line=0):' | |||||
33 |
|
33 | |||
34 | cell : unicode |
|
34 | cell : unicode | |
35 | A block of Python code |
|
35 | A block of Python code | |
36 |
c |
|
36 | cursor_pos : int | |
37 |
The co |
|
37 | The location of the cursor in the block where the token should be found | |
38 | line : int, optional |
|
|||
39 | The line where the token should be found (optional if cell is a single line) |
|
|||
40 | """ |
|
38 | """ | |
41 | cell = cast_unicode_py2(cell) |
|
39 | cell = cast_unicode_py2(cell) | |
42 | names = [] |
|
40 | names = [] | |
43 | tokens = [] |
|
41 | tokens = [] | |
44 | current_line = 0 |
|
42 | offset = 0 | |
45 | for tup in generate_tokens(StringIO(cell).readline): |
|
43 | for tup in generate_tokens(StringIO(cell).readline): | |
46 |
|
44 | |||
47 | tok = Token(*tup) |
|
45 | tok = Token(*tup) | |
@@ -49,7 +47,7 b' def token_at_cursor(cell, column, line=0):' | |||||
49 | # token, text, start, end, line = tup |
|
47 | # token, text, start, end, line = tup | |
50 | start_col = tok.start[1] |
|
48 | start_col = tok.start[1] | |
51 | end_col = tok.end[1] |
|
49 | end_col = tok.end[1] | |
52 | if line == current_line and start_col > column: |
|
50 | if offset + start_col > cursor_pos: | |
53 | # current token starts after the cursor, |
|
51 | # current token starts after the cursor, | |
54 | # don't consume it |
|
52 | # don't consume it | |
55 | break |
|
53 | break | |
@@ -64,13 +62,13 b' def token_at_cursor(cell, column, line=0):' | |||||
64 | # don't inspect the lhs of an assignment |
|
62 | # don't inspect the lhs of an assignment | |
65 | names.pop(-1) |
|
63 | names.pop(-1) | |
66 |
|
64 | |||
67 | if line == current_line and end_col > column: |
|
65 | if offset + end_col > cursor_pos: | |
68 | # we found the cursor, stop reading |
|
66 | # we found the cursor, stop reading | |
69 | break |
|
67 | break | |
70 |
|
68 | |||
71 | tokens.append(tok) |
|
69 | tokens.append(tok) | |
72 | if tok.token == tokenize2.NEWLINE: |
|
70 | if tok.token == tokenize2.NEWLINE: | |
73 | current_line += 1 |
|
71 | offset += len(tok.line) | |
74 |
|
72 | |||
75 | if names: |
|
73 | if names: | |
76 | return names[-1] |
|
74 | return names[-1] |
General Comments 0
You need to be logged in to leave comments.
Login now