Add Tootip to notebook.

When user press '(' and nothing for 1200ms, 'object_info_request' sent to
	the kernel. Then tooltip with 'definition' and beggining of 'docstring'
	 shown to the user if non empty response.

	Unlike Completion <select> , <div> is not focusable so event handled in main
	cell event handeling function

	Add some CSS3 that most browser with websocket should support.
This commit is contained in:
Matthias BUSSONNIER 2011-11-07 13:00:15 +01:00
parent 38cd955d8d
commit 3053ec9f9b
4 changed files with 119 additions and 4 deletions

View File

@ -321,7 +321,7 @@ div.text_cell_render {
.ansigrey {color: grey;} .ansigrey {color: grey;}
.ansibold {font-weight: bold;} .ansibold {font-weight: bold;}
.completions { .completions , .tooltip{
position: absolute; position: absolute;
z-index: 10; z-index: 10;
overflow: auto; overflow: auto;
@ -337,6 +337,36 @@ div.text_cell_render {
font-family: monospace; font-family: monospace;
} }
@-moz-keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
@-webkit-keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
@keyframes fadeIn {
from {opacity:0;}
to {opacity:1;}
}
.tooltip{
border-radius: 0px 10px 10px 10px;
box-shadow: 3px 3px 5px #999;
-webkit-animation: fadeIn 200ms;
-moz-animation: fadeIn 200ms;
animation: fadeIn 200ms;
vertical-align: middle;
background: #FDFDD8;
outline: none;
padding: 3px;
margin: 0px;
font-family: monospace;
}
@media print { @media print {
body { overflow: visible !important; } body { overflow: visible !important; }
.ui-widget-content { border: 0px; } .ui-widget-content { border: 0px; }

View File

@ -53,9 +53,29 @@ var IPython = (function (IPython) {
// handlers and is used to provide custom key handling. Its return // handlers and is used to provide custom key handling. Its return
// value is used to determine if CodeMirror should ignore the event: // value is used to determine if CodeMirror should ignore the event:
// true = ignore, false = don't ignore. // true = ignore, false = don't ignore.
// whatever key is pressed, first, cancel the tooltip request before
// they are sent, and remove tooltip if any
if(event.type === 'keydown' && this.tooltip_timeout != null){
CodeCell.prototype.remove_and_cancell_tooltip(this.tooltip_timeout);
this.tooltip_timeout=null;
}
if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
// Always ignore shift-enter in CodeMirror as we handle it. // Always ignore shift-enter in CodeMirror as we handle it.
return true; return true;
}else if (event.keyCode === 53 && event.type === 'keydown') {
var cursor = editor.getCursor();
var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim();
if (pre_cursor === "") {
// don't do anything if line beggin with '('
} else {
var that = this;
// Will set a timer to request tooltip in 1200ms
this.tooltip_timeout = setTimeout(function(){
IPython.notebook.request_tool_tip(that, pre_cursor)
},1200);
}
} else if (event.keyCode === 9 && event.type == 'keydown') { } else if (event.keyCode === 9 && event.type == 'keydown') {
// Tab completion. // Tab completion.
var cur = editor.getCursor(); var cur = editor.getCursor();
@ -108,6 +128,39 @@ var IPython = (function (IPython) {
}; };
}; };
CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
{
// note that we don't handle closing directly inside the calltip
// as in the completer, because it is not focusable, so won't
// get the event.
clearTimeout(timeout);
$('#tooltip').remove();
}
CodeCell.prototype.finish_tooltip = function (defstring,docstring) {
shortened = function(string){
if(string.length > 200){
return string.trim().substring(0,197)+'...';
} else { return string.trim() }
}
var that = this;
var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
if(defstring){
defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
tooltip.append(defstring_html);
}
tooltip.append($('<pre/>').html(utils.fixConsole(shortened(docstring))));
var pos = this.code_mirror.cursorCoords();
tooltip.css('left',pos.x+'px');
tooltip.css('top',pos.yBot+'px');
$('body').append(tooltip);
// issues with cross-closing if multiple tooltip in less than 5sec
// keep it comented for now
// setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
};
CodeCell.prototype.finish_completing = function (matched_text, matches) { CodeCell.prototype.finish_completing = function (matched_text, matches) {
// console.log("Got matches", matched_text, matches); // console.log("Got matches", matched_text, matches);

View File

@ -170,6 +170,15 @@ var IPython = (function (IPython) {
}; };
}; };
Kernel.prototype.object_info_request = function (objname) {
var content = {
oname : objname.toString(),
};
var msg = this.get_msg("object_info_request", content);
this.shell_channel.send(JSON.stringify(msg));
return msg.header.msg_id;
}
Kernel.prototype.execute = function (code) { Kernel.prototype.execute = function (code) {
var content = { var content = {
code : code, code : code,

View File

@ -700,14 +700,29 @@ var IPython = (function (IPython) {
this.dirty = true; this.dirty = true;
} else if (msg_type === "complete_reply") { } else if (msg_type === "complete_reply") {
cell.finish_completing(content.matched_text, content.matches); cell.finish_completing(content.matched_text, content.matches);
}; } else if (msg_type === "object_info_reply"){
var payload = content.payload || []; //console.log('back from object_info_request : ')
this.handle_payload(cell, payload); rep = reply.content;
if(rep.found)
{
console.log("object as been found");
cell.finish_tooltip(rep.definition,rep.docstring);
}
} else {
//console.log("unknown reply:"+msg_type);
}
// when having a rely from object_info_reply,
// no payload so no nned to handle it
if(typeof(content.payload)!='undefined') {
var payload = content.payload || [];
this.handle_payload(cell, payload);
}
}; };
Notebook.prototype.handle_payload = function (cell, payload) { Notebook.prototype.handle_payload = function (cell, payload) {
var l = payload.length; var l = payload.length;
console.log(payload);
for (var i=0; i<l; i++) { for (var i=0; i<l; i++) {
if (payload[i].source === 'IPython.zmq.page.page') { if (payload[i].source === 'IPython.zmq.page.page') {
if (payload[i].text.trim() !== '') { if (payload[i].text.trim() !== '') {
@ -868,6 +883,14 @@ var IPython = (function (IPython) {
}; };
Notebook.prototype.request_tool_tip = function (cell,func) {
// select last part of expression
var re = /[a-zA-Z._]+$/g;
var lastpart=re.exec(func);
var msg_id = this.kernel.object_info_request(lastpart);
this.msg_cell_map[msg_id] = cell.cell_id;
};
Notebook.prototype.complete_cell = function (cell, line, cursor_pos) { Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
var msg_id = this.kernel.complete(line, cursor_pos); var msg_id = this.kernel.complete(line, cursor_pos);
this.msg_cell_map[msg_id] = cell.cell_id; this.msg_cell_map[msg_id] = cell.cell_id;