##// END OF EJS Templates
Merge pull request #987 from Carreau/notebook-tooltip...
Fernando Perez -
r5415:a8ed50a1 merge
parent child Browse files
Show More
@@ -115,6 +115,18 b' span.section_row_buttons a {'
115 float: right;
115 float: right;
116 }
116 }
117
117
118 #timebeforetooltip_span {
119 float: right;
120 }
121
122 #tooltipontab_span {
123 float: right;
124 }
125
126 #smartcompleter_span {
127 float: right;
128 }
129
118 .checkbox_label {
130 .checkbox_label {
119 font-size: 85%;
131 font-size: 85%;
120 float: right;
132 float: right;
@@ -321,7 +333,7 b' div.text_cell_render {'
321 .ansigrey {color: grey;}
333 .ansigrey {color: grey;}
322 .ansibold {font-weight: bold;}
334 .ansibold {font-weight: bold;}
323
335
324 .completions {
336 .completions , .tooltip{
325 position: absolute;
337 position: absolute;
326 z-index: 10;
338 z-index: 10;
327 overflow: auto;
339 overflow: auto;
@@ -337,6 +349,63 b' div.text_cell_render {'
337 font-family: monospace;
349 font-family: monospace;
338 }
350 }
339
351
352 @-moz-keyframes fadeIn {
353 from {opacity:0;}
354 to {opacity:1;}
355 }
356
357 @-webkit-keyframes fadeIn {
358 from {opacity:0;}
359 to {opacity:1;}
360 }
361
362 @keyframes fadeIn {
363 from {opacity:0;}
364 to {opacity:1;}
365 }
366
367 /*"close" "expand" and "Open in pager button" of
368 /* the tooltip*/
369 .tooltip a{
370 float:right;
371 }
372
373 /*properties of tooltip after "expand"*/
374 .bigtooltip{
375 height:60%;
376 }
377
378 /*properties of tooltip before "expand"*/
379 .smalltooltip{
380 text-overflow: ellipsis;
381 overflow: hidden;
382 height:15%;
383 }
384
385 .tooltip{
386 /*transition when "expand"ing tooltip */
387 -webkit-transition-property: height;
388 -webkit-transition-duration: 1s;
389 -moz-transition-property: height;
390 -moz-transition-duration: 1s;
391 transition-property: height;
392 transition-duration: 1s;
393 max-width:700px;
394 border-radius: 0px 10px 10px 10px;
395 box-shadow: 3px 3px 5px #999;
396 /*fade-in animation when inserted*/
397 -webkit-animation: fadeIn 200ms;
398 -moz-animation: fadeIn 200ms;
399 animation: fadeIn 200ms;
400 vertical-align: middle;
401 background: #FDFDD8;
402 outline: none;
403 padding: 3px;
404 margin: 0px;
405 font-family: monospace;
406 min-height:50px;
407 }
408
340 @media print {
409 @media print {
341 body { overflow: visible !important; }
410 body { overflow: visible !important; }
342 .ui-widget-content { border: 0px; }
411 .ui-widget-content { border: 0px; }
@@ -85,7 +85,6 b' var IPython = (function (IPython) {'
85 }
85 }
86 };
86 };
87
87
88
89 // Subclasses must implement create_element.
88 // Subclasses must implement create_element.
90 Cell.prototype.create_element = function () {};
89 Cell.prototype.create_element = function () {};
91
90
@@ -47,24 +47,60 b' var IPython = (function (IPython) {'
47 this.collapse()
47 this.collapse()
48 };
48 };
49
49
50 //TODO, try to diminish the number of parameters.
51 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time,that){
52 if (pre_cursor === "" || pre_cursor === "(" ) {
53 // don't do anything if line beggin with '(' or is empty
54 } else {
55 // Will set a timer to request tooltip in `time`
56 that.tooltip_timeout = setTimeout(function(){
57 IPython.notebook.request_tool_tip(that, pre_cursor)
58 },time);
59 }
60 };
50
61
51 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
62 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
52 // This method gets called in CodeMirror's onKeyDown/onKeyPress
63 // This method gets called in CodeMirror's onKeyDown/onKeyPress
53 // handlers and is used to provide custom key handling. Its return
64 // handlers and is used to provide custom key handling. Its return
54 // value is used to determine if CodeMirror should ignore the event:
65 // value is used to determine if CodeMirror should ignore the event:
55 // true = ignore, false = don't ignore.
66 // true = ignore, false = don't ignore.
67
68 // note that we are comparing and setting the time to wait at each key press.
69 // a better wqy might be to generate a new function on each time change and
70 // assign it to CodeCell.prototype.request_tooltip_after_time
71 tooltip_wait_time = this.notebook.time_before_tooltip;
72 tooltip_on_tab = this.notebook.tooltip_on_tab;
73 var that = this;
74 // whatever key is pressed, first, cancel the tooltip request before
75 // they are sent, and remove tooltip if any
76 if(event.type === 'keydown' && this.tooltip_timeout != null){
77 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
78 that.tooltip_timeout=null;
79 }
80
56 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
81 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
57 // Always ignore shift-enter in CodeMirror as we handle it.
82 // Always ignore shift-enter in CodeMirror as we handle it.
58 return true;
83 return true;
84 }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
85 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
86 // browser and keyboard layout !
87 // Pressing '(' , request tooltip, don't forget to reappend it
88 var cursor = editor.getCursor();
89 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
90 CodeCell.prototype.request_tooltip_after_time(pre_cursor,tooltip_wait_time,that);
59 } else if (event.keyCode === 9 && event.type == 'keydown') {
91 } else if (event.keyCode === 9 && event.type == 'keydown') {
60 // Tab completion.
92 // Tab completion.
61 var cur = editor.getCursor();
93 var cur = editor.getCursor();
62 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur).trim();
94 //Do not trim here because of tooltip
63 if (pre_cursor === "") {
95 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
96 if (pre_cursor.trim() === "") {
64 // Don't autocomplete if the part of the line before the cursor
97 // Don't autocomplete if the part of the line before the cursor
65 // is empty. In this case, let CodeMirror handle indentation.
98 // is empty. In this case, let CodeMirror handle indentation.
66 return false;
99 return false;
100 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
101 CodeCell.prototype.request_tooltip_after_time(pre_cursor,0,that);
67 } else {
102 } else {
103 pre_cursor.trim();
68 // Autocomplete the current line.
104 // Autocomplete the current line.
69 event.stop();
105 event.stop();
70 var line = editor.getLine(cur.line);
106 var line = editor.getLine(cur.line);
@@ -108,11 +144,130 b' var IPython = (function (IPython) {'
108 };
144 };
109 };
145 };
110
146
147 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
148 {
149 // note that we don't handle closing directly inside the calltip
150 // as in the completer, because it is not focusable, so won't
151 // get the event.
152 clearTimeout(timeout);
153 $('#tooltip').remove();
154 }
155
156 CodeCell.prototype.finish_tooltip = function (reply) {
157 defstring=reply.definition;
158 docstring=reply.docstring;
159 if(docstring == null){docstring="<empty docstring>"};
160 name=reply.name;
161
162 var that = this;
163 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
164 // remove to have the tooltip not Limited in X and Y
165 tooltip.addClass('smalltooltip');
166 var pre=$('<pre/>').html(utils.fixConsole(docstring));
167 var expandlink=$('<a/>').attr('href',"#");
168 expandlink.addClass("ui-corner-all"); //rounded corner
169 expandlink.attr('role',"button");
170 //expandlink.addClass('ui-button');
171 //expandlink.addClass('ui-state-default');
172 var expandspan=$('<span/>').text('Expand');
173 expandspan.addClass('ui-icon');
174 expandspan.addClass('ui-icon-plus');
175 expandlink.append(expandspan);
176 expandlink.attr('id','expanbutton');
177 expandlink.click(function(){
178 tooltip.removeClass('smalltooltip');
179 tooltip.addClass('bigtooltip');
180 $('#expanbutton').remove();
181 setTimeout(function(){that.code_mirror.focus();}, 50);
182 });
183 var morelink=$('<a/>').attr('href',"#");
184 morelink.attr('role',"button");
185 morelink.addClass('ui-button');
186 //morelink.addClass("ui-corner-all"); //rounded corner
187 //morelink.addClass('ui-state-default');
188 var morespan=$('<span/>').text('Open in Pager');
189 morespan.addClass('ui-icon');
190 morespan.addClass('ui-icon-arrowstop-l-n');
191 morelink.append(morespan);
192 morelink.click(function(){
193 var msg_id = IPython.notebook.kernel.execute(name+"?");
194 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
195 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
196 setTimeout(function(){that.code_mirror.focus();}, 50);
197 });
198
199 var closelink=$('<a/>').attr('href',"#");
200 closelink.attr('role',"button");
201 closelink.addClass('ui-button');
202 //closelink.addClass("ui-corner-all"); //rounded corner
203 //closelink.adClass('ui-state-default'); // grey background and blue cross
204 var closespan=$('<span/>').text('Close');
205 closespan.addClass('ui-icon');
206 closespan.addClass('ui-icon-close');
207 closelink.append(closespan);
208 closelink.click(function(){
209 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
210 setTimeout(function(){that.code_mirror.focus();}, 50);
211 });
212 //construct the tooltip
213 tooltip.append(closelink);
214 tooltip.append(expandlink);
215 tooltip.append(morelink);
216 if(defstring){
217 defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
218 tooltip.append(defstring_html);
219 }
220 tooltip.append(pre);
221 var pos = this.code_mirror.cursorCoords();
222 tooltip.css('left',pos.x+'px');
223 tooltip.css('top',pos.yBot+'px');
224 $('body').append(tooltip);
225
226 // issues with cross-closing if multiple tooltip in less than 5sec
227 // keep it comented for now
228 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
229 };
230
111
231
112 CodeCell.prototype.finish_completing = function (matched_text, matches) {
232 CodeCell.prototype.finish_completing = function (matched_text, matches) {
113 // console.log("Got matches", matched_text, matches);
233 // console.log("Got matches", matched_text, matches);
234 var newm = new Array();
235 if(this.notebook.smart_completer)
236 {
237 kwargs = new Array();
238 other = new Array();
239 for(var i=0;i<matches.length; ++i){
240 if(matches[i].substr(-1) === '='){
241 kwargs.push(matches[i]);
242 }else{other.push(matches[i]);}
243 }
244 newm = kwargs.concat(other);
245 matches=newm;
246 }
114 if (!this.is_completing || matches.length === 0) {return;}
247 if (!this.is_completing || matches.length === 0) {return;}
115
248
249 //try to check if the user is typing tab at least twice after a word
250 // and completion is "done"
251 fallback_on_tooltip_after=2
252 if(matches.length==1 && matched_text === matches[0])
253 {
254 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
255 {
256 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
257 console.log('You should understand that there is no (more) completion for that !');
258 console.log("I'll show you the tooltip, will you stop bothering me ?");
259 this.request_tooltip_after_time(matched_text+'(',0,this);
260 return;
261 }
262 this.prevmatch=matched_text
263 this.npressed=this.npressed+1;
264 }
265 else
266 {
267 this.prevmatch="";
268 this.npressed=0;
269 }
270
116 var that = this;
271 var that = this;
117 var cur = this.completion_cursor;
272 var cur = this.completion_cursor;
118
273
@@ -170,6 +170,19 b' var IPython = (function (IPython) {'
170 };
170 };
171 };
171 };
172
172
173 Kernel.prototype.object_info_request = function (objname) {
174 if(typeof(objname)!=null)
175 {
176 var content = {
177 oname : objname.toString(),
178 };
179 var msg = this.get_msg("object_info_request", content);
180 this.shell_channel.send(JSON.stringify(msg));
181 return msg.header.msg_id;
182 }
183 return;
184 }
185
173 Kernel.prototype.execute = function (code) {
186 Kernel.prototype.execute = function (code) {
174 var content = {
187 var content = {
175 code : code,
188 code : code,
@@ -67,6 +67,7 b' var IPython = (function (IPython) {'
67 this.notebook_section = new IPython.NotebookSection('div#notebook_section');
67 this.notebook_section = new IPython.NotebookSection('div#notebook_section');
68 if (! IPython.read_only){
68 if (! IPython.read_only){
69 this.cell_section = new IPython.CellSection('div#cell_section');
69 this.cell_section = new IPython.CellSection('div#cell_section');
70 this.config_section = new IPython.ConfigSection('div#config_section');
70 this.kernel_section = new IPython.KernelSection('div#kernel_section');
71 this.kernel_section = new IPython.KernelSection('div#kernel_section');
71 }
72 }
72 this.help_section = new IPython.HelpSection('div#help_section');
73 this.help_section = new IPython.HelpSection('div#help_section');
@@ -27,6 +27,9 b' var IPython = (function (IPython) {'
27 this.style();
27 this.style();
28 this.create_elements();
28 this.create_elements();
29 this.bind_events();
29 this.bind_events();
30 this.set_tooltipontab(true);
31 this.set_smartcompleter(true);
32 this.set_timebeforetooltip(1200);
30 };
33 };
31
34
32
35
@@ -621,6 +624,21 b' var IPython = (function (IPython) {'
621 };
624 };
622
625
623
626
627 Notebook.prototype.set_timebeforetooltip = function (time) {
628 console.log("change time before tooltip to : "+time);
629 this.time_before_tooltip = time;
630 };
631
632 Notebook.prototype.set_tooltipontab = function (state) {
633 console.log("change tooltip on tab to : "+state);
634 this.tooltip_on_tab = state;
635 };
636
637 Notebook.prototype.set_smartcompleter = function (state) {
638 console.log("Smart completion (kwargs first) changed to to : "+state);
639 this.smart_completer = state;
640 };
641
624 Notebook.prototype.set_autoindent = function (state) {
642 Notebook.prototype.set_autoindent = function (state) {
625 var cells = this.cells();
643 var cells = this.cells();
626 len = cells.length;
644 len = cells.length;
@@ -700,9 +718,22 b' var IPython = (function (IPython) {'
700 this.dirty = true;
718 this.dirty = true;
701 } else if (msg_type === "complete_reply") {
719 } else if (msg_type === "complete_reply") {
702 cell.finish_completing(content.matched_text, content.matches);
720 cell.finish_completing(content.matched_text, content.matches);
703 };
721 } else if (msg_type === "object_info_reply"){
704 var payload = content.payload || [];
722 //console.log('back from object_info_request : ')
705 this.handle_payload(cell, payload);
723 rep = reply.content;
724 if(rep.found)
725 {
726 cell.finish_tooltip(rep);
727 }
728 } else {
729 //console.log("unknown reply:"+msg_type);
730 }
731 // when having a rely from object_info_reply,
732 // no payload so no nned to handle it
733 if(typeof(content.payload)!='undefined') {
734 var payload = content.payload || [];
735 this.handle_payload(cell, payload);
736 }
706 };
737 };
707
738
708
739
@@ -868,6 +899,30 b' var IPython = (function (IPython) {'
868 };
899 };
869
900
870
901
902 Notebook.prototype.request_tool_tip = function (cell,func) {
903 // Feel free to shorten this logic if you are better
904 // than me in regEx
905 // basicaly you shoul be able to get xxx.xxx.xxx from
906 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
907 // remove everything between matchin bracket (need to iterate)
908 matchBracket = /\([^\(\)]+\)/g;
909 oldfunc = func;
910 func = func.replace(matchBracket,"");
911 while( oldfunc != func )
912 {
913 oldfunc = func;
914 func = func.replace(matchBracket,"");
915 }
916 // remove everythin after last open bracket
917 endBracket = /\([^\(]*$/g;
918 func = func.replace(endBracket,"");
919 var re = /[a-zA-Z._]+$/g;
920 var msg_id = this.kernel.object_info_request(re.exec(func));
921 if(typeof(msg_id)!='undefined'){
922 this.msg_cell_map[msg_id] = cell.cell_id;
923 }
924 };
925
871 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
926 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
872 var msg_id = this.kernel.complete(line, cursor_pos);
927 var msg_id = this.kernel.complete(line, cursor_pos);
873 this.msg_cell_map[msg_id] = cell.cell_id;
928 this.msg_cell_map[msg_id] = cell.cell_id;
@@ -51,6 +51,7 b' $(document).ready(function () {'
51 IPython.quick_help.element.addClass('hidden'); // shortcuts are disabled in read_only
51 IPython.quick_help.element.addClass('hidden'); // shortcuts are disabled in read_only
52 $('button#new_notebook').addClass('hidden');
52 $('button#new_notebook').addClass('hidden');
53 $('div#cell_section').addClass('hidden');
53 $('div#cell_section').addClass('hidden');
54 $('div#config_section').addClass('hidden');
54 $('div#kernel_section').addClass('hidden');
55 $('div#kernel_section').addClass('hidden');
55 $('span#login_widget').removeClass('hidden');
56 $('span#login_widget').removeClass('hidden');
56 // left panel starts collapsed, but the collapse must happen after
57 // left panel starts collapsed, but the collapse must happen after
@@ -121,13 +121,52 b' var IPython = (function (IPython) {'
121 });
121 });
122 };
122 };
123
123
124 // ConfigSection
125
126 var ConfigSection = function () {
127 PanelSection.apply(this, arguments);
128 };
129
130 ConfigSection.prototype = new PanelSection();
131
132 ConfigSection.prototype.style = function () {
133 PanelSection.prototype.style.apply(this);
134 this.content.addClass('ui-helper-clearfix');
135 this.content.find('div.section_row').addClass('ui-helper-clearfix');
136
137 this.content.find('#tooltipontab').attr('title', 'Show tooltip if you press <Tab> after "(" or a white space');
138 this.content.find('#tooltipontab_label').attr('title', 'Show Tooltip when pressing Tab');
139
140 this.content.find('#timebeforetooltip').attr('title', 'Time before a tooltip auto-appear when "(" is pressed (negative value supress tooltip)');
141 this.content.find('#timebeforetooltip_label').attr('title', 'Time before a tooltip auto-appear when "(" is pressed (negative value supress tooltip)');
142
143 this.content.find('#smartcompleter').attr('title', 'When inside function call, completer try to propose kwargs first');
144 this.content.find('#smartcompleter_label').attr('title', 'When inside function call, completer try to propose kwargs first');
145 };
146
147
148 ConfigSection.prototype.bind_events = function () {
149 PanelSection.prototype.bind_events.apply(this);
150 this.content.find('#tooltipontab').change(function () {
151 var state = $('#tooltipontab').prop('checked');
152 IPython.notebook.set_tooltipontab(state);
153 });
154 this.content.find('#timebeforetooltip').change(function () {
155 var state = $('#timebeforetooltip').prop('value');
156 IPython.notebook.set_timebeforetooltip(state);
157 });
158 this.content.find('#smartcompleter').change(function () {
159 var state = $('#smartcompleter').prop('checked');
160 IPython.notebook.set_smartcompleter(state);
161 });
162 };
163
124 // CellSection
164 // CellSection
125
165
126 var CellSection = function () {
166 var CellSection = function () {
127 PanelSection.apply(this, arguments);
167 PanelSection.apply(this, arguments);
128 };
168 };
129
169
130
131 CellSection.prototype = new PanelSection();
170 CellSection.prototype = new PanelSection();
132
171
133
172
@@ -201,6 +240,10 b' var IPython = (function (IPython) {'
201 var state = $('#autoindent').prop('checked');
240 var state = $('#autoindent').prop('checked');
202 IPython.notebook.set_autoindent(state);
241 IPython.notebook.set_autoindent(state);
203 });
242 });
243 this.content.find('#tooltipontab').change(function () {
244 var state = $('#tooltipontab').prop('checked');
245 IPython.notebook.set_tooltipontab(state);
246 });
204 };
247 };
205
248
206
249
@@ -280,6 +323,7 b' var IPython = (function (IPython) {'
280 IPython.PanelSection = PanelSection;
323 IPython.PanelSection = PanelSection;
281 IPython.NotebookSection = NotebookSection;
324 IPython.NotebookSection = NotebookSection;
282 IPython.CellSection = CellSection;
325 IPython.CellSection = CellSection;
326 IPython.ConfigSection = ConfigSection;
283 IPython.KernelSection = KernelSection;
327 IPython.KernelSection = KernelSection;
284 IPython.HelpSection = HelpSection;
328 IPython.HelpSection = HelpSection;
285
329
@@ -251,6 +251,32 b''
251 </div>
251 </div>
252 </div>
252 </div>
253
253
254 <div id="config_section">
255 <div class="section_header">
256 <h3>Config</h3>
257 </div>
258 <div class="section_content">
259 <div class="section_row">
260 <span id="tooltipontab_span">
261 <input type="checkbox" id="tooltipontab" checked="true"></input>
262 </span>
263 <span class="checkbox_label" id="tooltipontab_label">Tooltip on tab:</span>
264 </div>
265 <div class="section_row">
266 <span id="smartcompleter_span">
267 <input type="checkbox" id="smartcompleter" checked="true"></input>
268 </span>
269 <span class="checkbox_label" id="smartcompleter_label">Smart completer:</span>
270 </div>
271 <div class="section_row">
272 <span id="timebeforetooltip_span">
273 <input type="text" id="timebeforetooltip" value="1200"></input>
274 </span>
275 <span class="numeric_input_label" id="timebeforetooltip_label">Time before tooltip : </span>
276 </div>
277 </div>
278 </div>
279
254 </div>
280 </div>
255 <div id="left_panel_splitter"></div>
281 <div id="left_panel_splitter"></div>
256 <div id="notebook_panel">
282 <div id="notebook_panel">
General Comments 0
You need to be logged in to leave comments. Login now