usability and cross browser compat for completer #1082

Closed
wants to merge 4 commits into
from
View
5 IPython/frontend/html/notebook/static/css/notebook.css
@@ -406,6 +406,11 @@ div.text_cell_render {
min-height:50px;
}
+/*fixed part of the completion*/
+.completions p b{
+ font-weight:bold;
+}
+
.completions p{
background: #DDF;
/*outline: none;
View
127 IPython/frontend/html/notebook/static/js/codecell.js
@@ -216,7 +216,7 @@ var IPython = (function (IPython) {
tooltip.append(expandlink);
tooltip.append(morelink);
if(defstring){
- defstring_html= $('<pre/>').html(utils.fixConsole(defstring));
+ defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
tooltip.append(defstring_html);
}
tooltip.append(pre);
@@ -239,12 +239,23 @@ var IPython = (function (IPython) {
var key = { tab:9,
esc:27,
backspace:8,
- space:13,
+ space:32,
shift:16,
- enter:32,
- // _ is 189
+ enter:13,
+ // _ is 95
isCompSymbol : function (code)
- {return ((code>64 && code <=122)|| code == 189)}
+ {
+ return (code > 64 && code <= 90)
+ || (code >= 97 && code <= 122)
+ || (code == 95)
+ },
+ dismissAndAppend : function (code)
+ {
+ chararr = ['(',')','[',']','+','-','/','\\','.',' '];
+ codearr = chararr.map(function(x){return x.charCodeAt(0)});
+ return jQuery.inArray(code, codearr) != -1;
+ }
+
}
// smart completion, sort kwarg ending with '='
@@ -253,25 +264,26 @@ var IPython = (function (IPython) {
{
kwargs = new Array();
other = new Array();
- for(var i=0;i<matches.length; ++i){
+ for(var i = 0 ; i<matches.length ; ++i){
if(matches[i].substr(-1) === '='){
kwargs.push(matches[i]);
}else{other.push(matches[i]);}
}
newm = kwargs.concat(other);
- matches=newm;
+ matches = newm;
}
// end sort kwargs
// give common prefix of a array of string
function sharedStart(A){
+ if(A.length == 1){return A[0]}
if(A.length > 1 ){
- var tem1, tem2, s, A= A.slice(0).sort();
- tem1= A[0];
- s= tem1.length;
- tem2= A.pop();
- while(s && tem2.indexOf(tem1)== -1){
- tem1= tem1.substring(0, --s);
+ var tem1, tem2, s, A = A.slice(0).sort();
+ tem1 = A[0];
+ s = tem1.length;
+ tem2 = A.pop();
+ while(s && tem2.indexOf(tem1) == -1){
+ tem1 = tem1.substring(0, --s);
}
return tem1;
}
@@ -281,8 +293,8 @@ var IPython = (function (IPython) {
//try to check if the user is typing tab at least twice after a word
// and completion is "done"
- fallback_on_tooltip_after=2
- if(matches.length==1 && matched_text === matches[0])
+ fallback_on_tooltip_after = 2
+ if(matches.length == 1 && matched_text === matches[0])
{
if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
{
@@ -292,13 +304,13 @@ var IPython = (function (IPython) {
this.request_tooltip_after_time(matched_text+'(',0,this);
return;
}
- this.prevmatch=matched_text
- this.npressed=this.npressed+1;
+ this.prevmatch = matched_text
+ this.npressed = this.npressed+1;
}
else
{
- this.prevmatch="";
- this.npressed=0;
+ this.prevmatch = "";
+ this.npressed = 0;
}
// end fallback on tooltip
//==================================
@@ -311,23 +323,29 @@ var IPython = (function (IPython) {
var close = function () {
if (done) return;
done = true;
- if (complete!=undefined)
+ if (complete != undefined)
{complete.remove();}
that.is_completing = false;
that.completion_cursor = null;
};
- // insert the given text and exit the completer
- var insert = function (selected_text, event) {
+ // update codemirror with the typed text
+ prev = matched_text
+ var update = function (inserted_text, event) {
that.code_mirror.replaceRange(
- selected_text,
+ inserted_text,
{line: cur.line, ch: (cur.ch-matched_text.length)},
- {line: cur.line, ch: cur.ch}
+ {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
);
+ prev = inserted_text
if(event != null){
event.stopPropagation();
event.preventDefault();
}
+ };
+ // insert the given text and exit the completer
+ var insert = function (selected_text, event) {
+ update(selected_text)
close();
setTimeout(function(){that.code_mirror.focus();}, 50);
};
@@ -348,22 +366,23 @@ var IPython = (function (IPython) {
// Used to 'pick' when pressing tab
if (matches.length < 1) {
insert(typed_text,event);
- if(event !=null){
+ if(event != null){
event.stopPropagation();
event.preventDefault();
}
- } else if (autopick && matches.length==1) {
+ } else if (autopick && matches.length == 1) {
insert(matches[0],event);
- if(event !=null){
+ if(event != null){
event.stopPropagation();
event.preventDefault();
}
}
//clear the previous completion if any
+ update(typed_text,event);
complete.children().children().remove();
- $('#asyoutype').text(typed_text);
- select=$('#asyoutypeselect');
- for (var i=0; i<matches.length; ++i) {
+ $('#asyoutype').html("<b>"+matched_text+"</b>"+typed_text.substr(matched_text.length));
+ select = $('#asyoutypeselect');
+ for (var i = 0; i<matches.length; ++i) {
select.append($('<option/>').html(matches[i]));
}
select.children().first().attr('selected','true');
@@ -372,7 +391,7 @@ var IPython = (function (IPython) {
// create html for completer
var complete = $('<div/>').addClass('completions');
complete.attr('id','complete');
- complete.append($('<p/>').attr('id', 'asyoutype').html(matched_text));//pseudo input field
+ complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
var select = $('<select/>').attr('multiple','true');
select.attr('id', 'asyoutypeselect')
@@ -390,61 +409,73 @@ var IPython = (function (IPython) {
// So a first actual completion. see if all the completion start wit
// the same letter and complete if necessary
fastForward = sharedStart(matches)
- typed_characters= fastForward.substr(matched_text.length);
+ typed_characters = fastForward.substr(matched_text.length);
complete_with(matches,matched_text+typed_characters,true,null);
- filterd=matches;
+ filterd = matches;
// Give focus to select, and make it filter the match as the user type
// by filtering the previous matches. Called by .keypress and .keydown
var downandpress = function (event,press_or_down) {
var code = event.which;
var autopick = false; // auto 'pick' if only one match
if (press_or_down === 0){
- press=true; down=false; //Are we called from keypress or keydown
+ press = true; down = false; //Are we called from keypress or keydown
} else if (press_or_down == 1){
- press=false; down=true;
+ press = false; down = true;
}
if (code === key.shift) {
// nothing on Shift
return;
}
- if (code === key.space || code === key.enter) {
- // Pressing SPACE or ENTER will cause a pick
+ if (key.dismissAndAppend(code) && press) {
+ var newchar = String.fromCharCode(code);
+ typed_characters = typed_characters+newchar;
+ insert(matched_text+typed_characters,event);
+ return
+ }
+ if (code === key.enter) {
+ // Pressing ENTER will cause a pick
event.stopPropagation();
event.preventDefault();
pick();
} else if (code === 38 || code === 40) {
// We don't want the document keydown handler to handle UP/DOWN,
// but we want the default action.
event.stopPropagation();
- //} else if ( key.isCompSymbol(code)|| (code==key.backspace)||(code==key.tab && down)){
- } else if ( (code==key.backspace)||(code==key.tab && down) || press || key.isCompSymbol(code)){
+ } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
if( key.isCompSymbol(code) && press)
{
var newchar = String.fromCharCode(code);
- typed_characters=typed_characters+newchar;
+ typed_characters = typed_characters+newchar;
} else if (code == key.tab) {
fastForward = sharedStart(filterd)
ffsub = fastForward.substr(matched_text.length+typed_characters.length);
- typed_characters=typed_characters+ffsub;
- autopick=true;
- event.stopPropagation();
- event.preventDefault();
+ typed_characters = typed_characters+ffsub;
+ autopick = true;
} else if (code == key.backspace && down) {
// cancel if user have erase everything, otherwise decrease
// what we filter with
+ event.preventDefault();
if (typed_characters.length <= 0)
{
insert(matched_text,event)
+ return
}
- typed_characters=typed_characters.substr(0,typed_characters.length-1);
- }else{return}
+ typed_characters = typed_characters.substr(0,typed_characters.length-1);
+ } else if (press && code != key.backspace && code != key.tab && code != 0){
+ insert(matched_text+typed_characters,event);
+ return
+ } else {
+ return
+ }
re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
filterd = matches.filter(function(x){return re.test(x)});
complete_with(filterd,matched_text+typed_characters,autopick,event);
- } else if(down){ // abort only on .keydown
+ } else if( code == key.esc) {
+ // dismiss the completer and go back to before invoking it
+ insert(matched_text,event);
+ } else if( press ){ // abort only on .keypress or esc
// abort with what the user have pressed until now
console.log('aborting with keycode : '+code+' is down :'+down);
- insert(matched_text+typed_characters,event);
}
}
select.keydown(function (event) {