Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Completer usability 2 (rebased of pr #1082) #1108

Merged
merged 5 commits into from

3 participants

Matthias Bussonnier Fernando Perez Min RK
Matthias Bussonnier
Owner

rebased version of #1082 plus one fix.
ment to fix #1080
short recap:

  • dismiss the completer on .[]{}()...
  • continue to type in codemirror
  • space around =
  • uniformise behaviour between FF and chrome
Fernando Perez
Owner

I'll test this now, but as a general comment, it's better not to close a PR and open a new one just b/c of a rebase: that breaks the discussion flow of the old one, people who were already being notified stop being so, etc. The only real reason for closing a PR and starting a new one is if a completely new approach is required, and the old branch is more or less abandoned. But since in this case you're just rebasing and continuing, there's really no need for a whole new PR.

Having said that, don't worry this time: let's continue here. I just mention it for future cases.

Fernando Perez
Owner

OK, behavior-wise this is perfect. Awesome job!

I'd like @minrk to have a quick look at the JS as well, since he has by now picked up better JS skills than mine. I'm still going to review the code, but his eyes are better than mine :)

But unless we spot some minor fixes needed in terms of code/implementation, this is the functionality we needed. Many thanks for the great work.

I've checked on both Chrome and Firefox (on linux) and everything looks good.

IPython/frontend/html/notebook/static/js/codecell.js
((11 lines not shown))
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 = ['(',')','[',']','+','-','/','\\','.',' '];
Fernando Perez Owner
fperez added a note

Just for readability, use spaces around the commas here so it's easier to see what each element is:

chararr = [ '(', ')', '[', ']', '+', '-', '/', '\\', '.', ' ' ];
Min RK Owner
minrk added a note

or better yet, avoid all those quotes with str.split:

chararr = "()[]+-/\. ".split("")

Fernando Perez Owner
fperez added a note

nice, I didn't know JS had split too :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/codecell.js
((11 lines not shown))
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)});
Fernando Perez Owner
fperez added a note

Is it necessary to do this dynamically or would it be cleaner/simpler to simply put the static code list in there manually? I just don't know enough about JS to tell...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/codecell.js
((11 lines not shown))
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;
Fernando Perez Owner
fperez added a note

Is there no standard JS functionality to do this so that we need to use jQuery for it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Fernando Perez
Owner

I tested changing also the background of the fixed part to a different color (gray), and it doesn't really look very good. Your approach of using just boldface is nice and clean.

I reviewed the code and other than some small questions and quick fixes, it looks good to me. Once we also have @minrk's eyes on it and you address those small points, this can go in.

Matthias Bussonnier
Owner

To respond to your questions :

  • I'll add spaces
  • yes that can be made static, but I preferd readability. I already made enough mistake with keycode/keypress value to think that's worth.
  • I didn't found native Js function to do it... but if it exist it will be great

Sorry for the double PR, but the rebase had a 3 way merge and I preferd to keep the old branch, I could have pushed it on another branch and then override this one but it was too late

Fernando Perez
Owner
Min RK
Owner

I'm extremely far from a js expert, but I don't see anything obvious (other than the split() mentioned above). So if the behavior is right, then we should be set.

Fernando Perez
Owner

Well, every bit helps, since none of us is an expert :). OK @Carreau, go ahead and fix these last few little things, and we'll merge this one!

Carreau added some commits
Matthias Bussonnier Carreau usability and cross browser compat for completer
	- dissmiss the completer, append what alredy type, **Plus** one caracter in
	  some cases

	  List of special caracter that are handle are in a given list `()[]./\-+`
	  for the moment. usefull for exaple when typing :
	  >>> np.s<tab>in(
	  and not having to type '(' twice.

	  they are handle separately has the [a-zA-Z] ones because otherwise they
	  will screw up the regexp, and are opt-in to avoid bugs with invisible
	  caracters send because some browser have 'keypress' event for meta keys

	  close #1080

		Note to this commit :
	  list of test for the completer across browser with --pylab=inline flag

	  #test direct one completion
	  plt.an<tab>       -> plt.annotate

	  #test filter,tab, only one completion
	  plt.a<tab>n<tab>  -> plt.annotate

	  # test partial common beggining
	  # test dismmised if user erase
	  plt.a<tab>nn<backspace><backspace>u<tab>                -> completer to `aut`
	  ........................................<tab><tab><tab> -> nothing should append
	  .......................................................<backspace><backspace<backspace> -> completer cancelled

	  #test dismiss if no more completion
	  plt.s<tab>c  -> completer 3 choices
	  ...........u -> dismissed whith what user have typed.  `plt.scu`

	  # test dismiss in no completion, special symbol
	  # opt-in list of caracters +-/\()[].
	  np<tab>.s          -> a 'dot' sould dismiss the completer and be appended
	  .........<tab>in(  -> np.sin(
	  np.s<tab>in[       -> np.sin[
c630b34
Matthias Bussonnier Carreau Apply pep8 to js e480aee
Matthias Bussonnier Carreau completer update code-miror on the fly
	Following @fperez advice, change the completer apparence to avoid user confusion.

	- Append what the user type in the completer in code-miror, (Almost) as if
	  codemirror still have focus
	- distinguish between "fixed" completion  part, which was sent to the kernel
	  (now written in bold) and filtering one,handled only in JS,that the user
	  can errase without dismissing the completer

	I changed the action of <Space> to dismiss the completer with what have
	already been typed and inserting a space instead of "picking" the currently
	hilighted option

	<Escape> will still dissmiss the completer and remove everything the user as
	typed since the completer invocation

	Note that while the completer is shown, code-mirror does not show any
	blinking cursor
5f3907c
Matthias Bussonnier Carreau notebook: fix, only one completion autopick 3a94f81
Matthias Bussonnier Carreau notebook: code Readability. Add dismissing symbols
	* As minrk suggested, changes list of char to string.split()
	* add also a few dismissing symbold like `,` `=` and `*` for
	  the completer
b1cbd5f
Matthias Bussonnier
Owner

fix, rebased and force pushed to be up to date with the recent PR merges.
I've added *,= to the list of dismissing caracters.

Fernando Perez
Owner

Great, merging now so we can start stabilizing things for the 0.12 RC. Excellent work.

Fernando Perez fperez merged commit 964bfe4 into from
Fernando Perez fperez referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 7, 2011
  1. Matthias Bussonnier

    usability and cross browser compat for completer

    Carreau authored
    	- dissmiss the completer, append what alredy type, **Plus** one caracter in
    	  some cases
    
    	  List of special caracter that are handle are in a given list `()[]./\-+`
    	  for the moment. usefull for exaple when typing :
    	  >>> np.s<tab>in(
    	  and not having to type '(' twice.
    
    	  they are handle separately has the [a-zA-Z] ones because otherwise they
    	  will screw up the regexp, and are opt-in to avoid bugs with invisible
    	  caracters send because some browser have 'keypress' event for meta keys
    
    	  close #1080
    
    		Note to this commit :
    	  list of test for the completer across browser with --pylab=inline flag
    
    	  #test direct one completion
    	  plt.an<tab>       -> plt.annotate
    
    	  #test filter,tab, only one completion
    	  plt.a<tab>n<tab>  -> plt.annotate
    
    	  # test partial common beggining
    	  # test dismmised if user erase
    	  plt.a<tab>nn<backspace><backspace>u<tab>                -> completer to `aut`
    	  ........................................<tab><tab><tab> -> nothing should append
    	  .......................................................<backspace><backspace<backspace> -> completer cancelled
    
    	  #test dismiss if no more completion
    	  plt.s<tab>c  -> completer 3 choices
    	  ...........u -> dismissed whith what user have typed.  `plt.scu`
    
    	  # test dismiss in no completion, special symbol
    	  # opt-in list of caracters +-/\()[].
    	  np<tab>.s          -> a 'dot' sould dismiss the completer and be appended
    	  .........<tab>in(  -> np.sin(
    	  np.s<tab>in[       -> np.sin[
  2. Matthias Bussonnier

    Apply pep8 to js

    Carreau authored
  3. Matthias Bussonnier

    completer update code-miror on the fly

    Carreau authored
    	Following @fperez advice, change the completer apparence to avoid user confusion.
    
    	- Append what the user type in the completer in code-miror, (Almost) as if
    	  codemirror still have focus
    	- distinguish between "fixed" completion  part, which was sent to the kernel
    	  (now written in bold) and filtering one,handled only in JS,that the user
    	  can errase without dismissing the completer
    
    	I changed the action of <Space> to dismiss the completer with what have
    	already been typed and inserting a space instead of "picking" the currently
    	hilighted option
    
    	<Escape> will still dissmiss the completer and remove everything the user as
    	typed since the completer invocation
    
    	Note that while the completer is shown, code-mirror does not show any
    	blinking cursor
  4. Matthias Bussonnier
  5. Matthias Bussonnier

    notebook: code Readability. Add dismissing symbols

    Carreau authored
    	* As minrk suggested, changes list of char to string.split()
    	* add also a few dismissing symbold like `,` `=` and `*` for
    	  the completer
This page is out of date. Refresh to see the latest.
5 IPython/frontend/html/notebook/static/css/notebook.css
View
@@ -419,6 +419,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;
127 IPython/frontend/html/notebook/static/js/codecell.js
View
@@ -218,7 +218,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);
@@ -241,12 +241,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 = '()[]+-/\\. ,=*'.split("");
+ codearr = chararr.map(function(x){return x.charCodeAt(0)});
+ return jQuery.inArray(code, codearr) != -1;
+ }
+
}
// smart completion, sort kwarg ending with '='
@@ -255,25 +266,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;
}
@@ -283,8 +295,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)
{
@@ -294,13 +306,13 @@ var IPython = (function (IPython) {
this.request_tooltip_after_time(matched_text+'(',0);
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
//==================================
@@ -313,23 +325,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);
};
@@ -350,22 +368,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');
@@ -374,7 +393,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')
@@ -392,25 +411,31 @@ 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();
@@ -418,35 +443,41 @@ var IPython = (function (IPython) {
// 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) {
Something went wrong with that request. Please try again.