##// END OF EJS Templates
Merge pull request #1039 from Carreau/notebook-as-you-type-completer...
Fernando Perez -
r5528:b44cdc3d merge
parent child Browse files
Show More
@@ -406,6 +406,15 b' div.text_cell_render {'
406 min-height:50px;
406 min-height:50px;
407 }
407 }
408
408
409 .completions p{
410 background: #DDF;
411 /*outline: none;
412 padding: 0px;*/
413 border-bottom: black solid 1px;
414 padding: 1px;
415 font-family: monospace;
416 }
417
409 @media print {
418 @media print {
410 body { overflow: visible !important; }
419 body { overflow: visible !important; }
411 .ui-widget-content { border: 0px; }
420 .ui-widget-content { border: 0px; }
@@ -73,8 +73,8 b' var IPython = (function (IPython) {'
73 var that = this;
73 var that = this;
74 // whatever key is pressed, first, cancel the tooltip request before
74 // whatever key is pressed, first, cancel the tooltip request before
75 // they are sent, and remove tooltip if any
75 // they are sent, and remove tooltip if any
76 if(event.type === 'keydown' && this.tooltip_timeout != null){
76 if(event.type === 'keydown' ){
77 CodeCell.prototype.remove_and_cancell_tooltip(that.tooltip_timeout);
77 CodeCell.prototype.remove_and_cancel_tooltip(that.tooltip_timeout);
78 that.tooltip_timeout=null;
78 that.tooltip_timeout=null;
79 }
79 }
80
80
@@ -145,12 +145,13 b' var IPython = (function (IPython) {'
145 return false;
145 return false;
146 };
146 };
147
147
148 CodeCell.prototype.remove_and_cancell_tooltip = function(timeout)
148 CodeCell.prototype.remove_and_cancel_tooltip = function(timeout)
149 {
149 {
150 // note that we don't handle closing directly inside the calltip
150 // note that we don't handle closing directly inside the calltip
151 // as in the completer, because it is not focusable, so won't
151 // as in the completer, because it is not focusable, so won't
152 // get the event.
152 // get the event.
153 clearTimeout(timeout);
153 if(timeout != null)
154 { clearTimeout(timeout);}
154 $('#tooltip').remove();
155 $('#tooltip').remove();
155 }
156 }
156
157
@@ -229,9 +230,24 b' var IPython = (function (IPython) {'
229 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
230 // setTimeout(CodeCell.prototype.remove_and_cancell_tooltip, 5000);
230 };
231 };
231
232
232
233 // As you type completer
233 CodeCell.prototype.finish_completing = function (matched_text, matches) {
234 CodeCell.prototype.finish_completing = function (matched_text, matches) {
234 // console.log("Got matches", matched_text, matches);
235 //return if not completing or nothing to complete
236 if (!this.is_completing || matches.length === 0) {return;}
237
238 // for later readability
239 var key = { tab:9,
240 esc:27,
241 backspace:8,
242 space:13,
243 shift:16,
244 enter:32,
245 // _ is 189
246 isCompSymbol : function (code)
247 {return ((code>64 && code <=122)|| code == 189)}
248 }
249
250 // smart completion, sort kwarg ending with '='
235 var newm = new Array();
251 var newm = new Array();
236 if(this.notebook.smart_completer)
252 if(this.notebook.smart_completer)
237 {
253 {
@@ -245,7 +261,23 b' var IPython = (function (IPython) {'
245 newm = kwargs.concat(other);
261 newm = kwargs.concat(other);
246 matches=newm;
262 matches=newm;
247 }
263 }
248 if (!this.is_completing || matches.length === 0) {return;}
264 // end sort kwargs
265
266 // give common prefix of a array of string
267 function sharedStart(A){
268 if(A.length > 1 ){
269 var tem1, tem2, s, A= A.slice(0).sort();
270 tem1= A[0];
271 s= tem1.length;
272 tem2= A.pop();
273 while(s && tem2.indexOf(tem1)== -1){
274 tem1= tem1.substring(0, --s);
275 }
276 return tem1;
277 }
278 return "";
279 }
280
249
281
250 //try to check if the user is typing tab at least twice after a word
282 //try to check if the user is typing tab at least twice after a word
251 // and completion is "done"
283 // and completion is "done"
@@ -268,57 +300,104 b' var IPython = (function (IPython) {'
268 this.prevmatch="";
300 this.prevmatch="";
269 this.npressed=0;
301 this.npressed=0;
270 }
302 }
271
303 // end fallback on tooltip
304 //==================================
305 // Real completion logic start here
272 var that = this;
306 var that = this;
273 var cur = this.completion_cursor;
307 var cur = this.completion_cursor;
308 var done = false;
309
310 // call to dismmiss the completer
311 var close = function () {
312 if (done) return;
313 done = true;
314 if (complete!=undefined)
315 {complete.remove();}
316 that.is_completing = false;
317 that.completion_cursor = null;
318 };
274
319
320 // insert the given text and exit the completer
275 var insert = function (selected_text) {
321 var insert = function (selected_text) {
276 that.code_mirror.replaceRange(
322 that.code_mirror.replaceRange(
277 selected_text,
323 selected_text,
278 {line: cur.line, ch: (cur.ch-matched_text.length)},
324 {line: cur.line, ch: (cur.ch-matched_text.length)},
279 {line: cur.line, ch: cur.ch}
325 {line: cur.line, ch: cur.ch}
280 );
326 );
327 event.stopPropagation();
328 event.preventDefault();
329 close();
330 setTimeout(function(){that.code_mirror.focus();}, 50);
281 };
331 };
282
332
283 if (matches.length === 1) {
333 // insert the curent highlited selection and exit
284 insert(matches[0]);
334 var pick = function () {
285 setTimeout(function(){that.code_mirror.focus();}, 50);
335 insert(select.val()[0]);
286 return;
287 };
336 };
288
337
338
339 // Define function to clear the completer, refill it with the new
340 // matches, update the pseuso typing field. autopick insert match if
341 // only one left, in no matches (anymore) dismiss itself by pasting
342 // what the user have typed until then
343 var complete_with = function(matches,typed_text,autopick)
344 {
345 // If autopick an only one match, past.
346 // Used to 'pick' when pressing tab
347 if (matches.length < 1) {
348 insert(typed_text);
349 } else if (autopick && matches.length==1) {
350 insert(matches[0]);
351 }
352 //clear the previous completion if any
353 complete.children().children().remove();
354 $('#asyoutype').text(typed_text);
355 select=$('#asyoutypeselect');
356 for (var i=0; i<matches.length; ++i) {
357 select.append($('<option/>').html(matches[i]));
358 }
359 select.children().first().attr('selected','true');
360 }
361
362 // create html for completer
289 var complete = $('<div/>').addClass('completions');
363 var complete = $('<div/>').addClass('completions');
364 complete.attr('id','complete');
365 complete.append($('<p/>').attr('id', 'asyoutype').html(matched_text));//pseudo input field
366
290 var select = $('<select/>').attr('multiple','true');
367 var select = $('<select/>').attr('multiple','true');
291 for (var i=0; i<matches.length; ++i) {
368 select.attr('id', 'asyoutypeselect')
292 select.append($('<option/>').text(matches[i]));
369 select.attr('size',Math.min(10,matches.length));
293 }
294 select.children().first().attr('selected','true');
295 select.attr('size',Math.min(10,matches.length));
296 var pos = this.code_mirror.cursorCoords();
370 var pos = this.code_mirror.cursorCoords();
371
372 // TODO: I propose to remove enough horizontal pixel
373 // to align the text later
297 complete.css('left',pos.x+'px');
374 complete.css('left',pos.x+'px');
298 complete.css('top',pos.yBot+'px');
375 complete.css('top',pos.yBot+'px');
299 complete.append(select);
376 complete.append(select);
300
377
301 $('body').append(complete);
378 $('body').append(complete);
302 var done = false;
303
304 var close = function () {
305 if (done) return;
306 done = true;
307 complete.remove();
308 that.is_completing = false;
309 that.completion_cursor = null;
310 };
311
312 var pick = function () {
313 insert(select.val()[0]);
314 close();
315 setTimeout(function(){that.code_mirror.focus();}, 50);
316 };
317
379
318 select.blur(close);
380 // So a first actual completion. see if all the completion start wit
319 select.keydown(function (event) {
381 // the same letter and complete if necessary
382 fastForward = sharedStart(matches)
383 typed_characters= fastForward.substr(matched_text.length);
384 complete_with(matches,matched_text+typed_characters,true);
385 filterd=matches;
386 // Give focus to select, and make it filter the match as the user type
387 // by filtering the previous matches. Called by .keypress and .keydown
388 var downandpress = function (event,press_or_down) {
320 var code = event.which;
389 var code = event.which;
321 if (code === 13 || code === 32) {
390 var autopick = false; // auto 'pick' if only one match
391 if (press_or_down === 0){
392 press=true; down=false; //Are we called from keypress or keydown
393 } else if (press_or_down == 1){
394 press=false; down=true;
395 }
396 if (code === key.shift) {
397 // nothing on Shift
398 return;
399 }
400 if (code === key.space || code === key.enter) {
322 // Pressing SPACE or ENTER will cause a pick
401 // Pressing SPACE or ENTER will cause a pick
323 event.stopPropagation();
402 event.stopPropagation();
324 event.preventDefault();
403 event.preventDefault();
@@ -327,16 +406,47 b' var IPython = (function (IPython) {'
327 // We don't want the document keydown handler to handle UP/DOWN,
406 // We don't want the document keydown handler to handle UP/DOWN,
328 // but we want the default action.
407 // but we want the default action.
329 event.stopPropagation();
408 event.stopPropagation();
330 } else {
409 //} else if ( key.isCompSymbol(code)|| (code==key.backspace)||(code==key.tab && down)){
331 // All other key presses exit completion.
410 } else if ( (code==key.backspace)||(code==key.tab) || press || key.isCompSymbol(code)){
332 event.stopPropagation();
411 if((code != key.backspace) && (code != key.tab) && press)
333 event.preventDefault();
412 {
334 close();
413 var newchar = String.fromCharCode(code);
335 that.code_mirror.focus();
414 typed_characters=typed_characters+newchar;
415 } else if (code == key.tab) {
416 fastForward = sharedStart(filterd)
417 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
418 typed_characters=typed_characters+ffsub;
419 autopick=true;
420 event.stopPropagation();
421 event.preventDefault();
422 } else if (code == key.backspace) {
423 // cancel if user have erase everything, otherwise decrease
424 // what we filter with
425 if (typed_characters.length <= 0)
426 {
427 insert(matched_text)
428 }
429 typed_characters=typed_characters.substr(0,typed_characters.length-1);
430 }
431 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
432 filterd = matches.filter(function(x){return re.test(x)});
433 complete_with(filterd,matched_text+typed_characters,autopick);
434 } else if(down){ // abort only on .keydown
435 // abort with what the user have pressed until now
436 console.log('aborting with keycode : '+code+' is down :'+down);
437 insert(matched_text+typed_characters);
336 }
438 }
439 }
440 select.keydown(function (event) {
441 downandpress(event,1)
442 });
443 select.keypress(function (event) {
444 downandpress(event,0)
337 });
445 });
338 // Double click also causes a pick.
446 // Double click also causes a pick.
447 // and bind the last actions.
339 select.dblclick(pick);
448 select.dblclick(pick);
449 select.blur(close);
340 select.focus();
450 select.focus();
341 };
451 };
342
452
@@ -171,7 +171,7 b' var IPython = (function (IPython) {'
171 };
171 };
172
172
173 Kernel.prototype.object_info_request = function (objname) {
173 Kernel.prototype.object_info_request = function (objname) {
174 if(typeof(objname)!=null)
174 if(typeof(objname)!=null && objname!=null)
175 {
175 {
176 var content = {
176 var content = {
177 oname : objname.toString(),
177 oname : objname.toString(),
General Comments 0
You need to be logged in to leave comments. Login now