##// END OF EJS Templates
allow number in tokens
Matthias BUSSONNIER -
Show More
@@ -1,230 +1,230 b''
1 // function completer.
1 // function completer.
2 //
2 //
3 // completer should be a class that take an cell instance
3 // completer should be a class that take an cell instance
4
4
5 var IPython = (function(IPython ) {
5 var IPython = (function(IPython ) {
6 // that will prevent us from misspelling
6 // that will prevent us from misspelling
7 "use strict";
7 "use strict";
8
8
9 // easyier key mapping
9 // easyier key mapping
10 var key = IPython.utils.keycodes;
10 var key = IPython.utils.keycodes;
11
11
12 // what is the common start of all completions
12 // what is the common start of all completions
13 function sharedStart(B){
13 function sharedStart(B){
14 if(B.length == 1){return B[0]}
14 if(B.length == 1){return B[0]}
15 var A = new Array()
15 var A = new Array()
16 for(var i=0; i< B.length; i++)
16 for(var i=0; i< B.length; i++)
17 {
17 {
18 A.push(B[i].str);
18 A.push(B[i].str);
19 }
19 }
20 if(A.length > 1 ){
20 if(A.length > 1 ){
21 var tem1, tem2, s, A = A.slice(0).sort();
21 var tem1, tem2, s, A = A.slice(0).sort();
22 tem1 = A[0];
22 tem1 = A[0];
23 s = tem1.length;
23 s = tem1.length;
24 tem2 = A.pop();
24 tem2 = A.pop();
25 while(s && tem2.indexOf(tem1) == -1){
25 while(s && tem2.indexOf(tem1) == -1){
26 tem1 = tem1.substring(0, --s);
26 tem1 = tem1.substring(0, --s);
27 }
27 }
28 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
28 if (tem1 == "" || tem2.indexOf(tem1) != 0){return null;}
29 return { str : tem1,
29 return { str : tem1,
30 type : "computed",
30 type : "computed",
31 from : B[0].from,
31 from : B[0].from,
32 to : B[0].to
32 to : B[0].to
33 };
33 };
34 }
34 }
35 return null;
35 return null;
36 }
36 }
37
37
38
38
39 var Completer = function(cell) {
39 var Completer = function(cell) {
40 this.cell = cell;
40 this.cell = cell;
41 this.editor = cell.code_mirror;
41 this.editor = cell.code_mirror;
42 // if last caractere before cursor is not in this, we stop completing
42 // if last caractere before cursor is not in this, we stop completing
43 this.reg = /[A-Za-z.]/;
43 this.reg = /[0-9a-z.]/i; // casse insensitive
44 }
44 }
45
45
46 Completer.prototype.kernelCompletionRequest = function(){
46 Completer.prototype.kernelCompletionRequest = function(){
47 var cur = this.editor.getCursor();
47 var cur = this.editor.getCursor();
48 var line = this.editor.getLine(cur.line);
48 var line = this.editor.getLine(cur.line);
49 // one could fork here and directly call finish completing if kernel is busy
49 // one could fork here and directly call finish completing if kernel is busy
50 IPython.notebook.complete_cell(this.cell, line, cur.ch);
50 IPython.notebook.complete_cell(this.cell, line, cur.ch);
51 }
51 }
52
52
53
53
54 Completer.prototype.startCompletion = function()
54 Completer.prototype.startCompletion = function()
55 {
55 {
56 // call for a 'first' completion, that will set the editor and do some
56 // call for a 'first' completion, that will set the editor and do some
57 // special behaviour like autopicking if only one completion availlable
57 // special behaviour like autopicking if only one completion availlable
58 //
58 //
59 if (this.editor.somethingSelected()) return;
59 if (this.editor.somethingSelected()) return;
60 this.done = false;
60 this.done = false;
61 // use to get focus back on opera
61 // use to get focus back on opera
62 this.carryOnCompletion(true);
62 this.carryOnCompletion(true);
63 }
63 }
64
64
65 Completer.prototype.carryOnCompletion = function(ff) {
65 Completer.prototype.carryOnCompletion = function(ff) {
66 // Pass true as parameter if you want the commpleter to autopick when
66 // Pass true as parameter if you want the commpleter to autopick when
67 // only one completion. This function is automatically reinvoked at
67 // only one completion. This function is automatically reinvoked at
68 // each keystroke with ff = false
68 // each keystroke with ff = false
69
69
70 var cur = this.editor.getCursor();
70 var cur = this.editor.getCursor();
71 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur);
71 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-1},cur);
72
72
73 // we need to check that we are still on a word boundary
73 // we need to check that we are still on a word boundary
74 // because while typing the completer is still reinvoking itself
74 // because while typing the completer is still reinvoking itself
75 if(!this.reg.test(pre_cursor)){ this.close(); return;}
75 if(!this.reg.test(pre_cursor)){ this.close(); return;}
76
76
77 this.autopick = false;
77 this.autopick = false;
78 if( ff != 'undefined' && ff==true)
78 if( ff != 'undefined' && ff==true)
79 { this.autopick=true; }
79 { this.autopick=true; }
80
80
81 // We want a single cursor position.
81 // We want a single cursor position.
82 if (this.editor.somethingSelected()) return;
82 if (this.editor.somethingSelected()) return;
83
83
84 //one kernel completion came back, finish_completing will be called with the results
84 //one kernel completion came back, finish_completing will be called with the results
85 this.kernelCompletionRequest();
85 this.kernelCompletionRequest();
86 }
86 }
87
87
88 Completer.prototype.finish_completing =function (matched_text, matches) {
88 Completer.prototype.finish_completing =function (matched_text, matches) {
89 // let's build a function that wrap all that stuff into what is needed
89 // let's build a function that wrap all that stuff into what is needed
90 // for the new completer:
90 // for the new completer:
91
91
92 var cur = this.editor.getCursor();
92 var cur = this.editor.getCursor();
93 var results = CodeMirror.contextHint(this.editor);
93 var results = CodeMirror.contextHint(this.editor);
94
94
95 // append the introspection result, in order, at at the beginning of
95 // append the introspection result, in order, at at the beginning of
96 // the table and compute the replacement range from current cursor
96 // the table and compute the replacement range from current cursor
97 // positon and matched_text length.
97 // positon and matched_text length.
98 for(var i= matches.length-1; i>=0 ;--i)
98 for(var i= matches.length-1; i>=0 ;--i)
99 {
99 {
100 results.unshift(
100 results.unshift(
101 { str : matches[i],
101 { str : matches[i],
102 type : "introspection",
102 type : "introspection",
103 from : {line: cur.line, ch: cur.ch-matched_text.length},
103 from : {line: cur.line, ch: cur.ch-matched_text.length},
104 to : {line: cur.line, ch: cur.ch}
104 to : {line: cur.line, ch: cur.ch}
105 })
105 })
106 }
106 }
107
107
108 // one the 2 sources results have been merge, deal with it
108 // one the 2 sources results have been merge, deal with it
109 this.raw_result = results;
109 this.raw_result = results;
110
110
111 // if empty result return
111 // if empty result return
112 if (!this.raw_result || !this.raw_result.length) return;
112 if (!this.raw_result || !this.raw_result.length) return;
113
113
114 // When there is only one completion, use it directly.
114 // When there is only one completion, use it directly.
115 if (this.autopick == true && this.raw_result.length == 1)
115 if (this.autopick == true && this.raw_result.length == 1)
116 {
116 {
117 this.insert(this.raw_result[0]);
117 this.insert(this.raw_result[0]);
118 return true;
118 return true;
119 }
119 }
120
120
121 if (this.raw_result.length == 1)
121 if (this.raw_result.length == 1)
122 {
122 {
123 // test if first and only completion totally matches
123 // test if first and only completion totally matches
124 // what is typed, in this case dismiss
124 // what is typed, in this case dismiss
125 var str = this.raw_result[0].str
125 var str = this.raw_result[0].str
126 var cur = this.editor.getCursor();
126 var cur = this.editor.getCursor();
127 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur);
127 var pre_cursor = this.editor.getRange({line:cur.line,ch:cur.ch-str.length},cur);
128 if(pre_cursor == str)
128 if(pre_cursor == str)
129 { this.close(); return ; }
129 { this.close(); return ; }
130 }
130 }
131
131
132 this.complete = $('<div/>').addClass('completions');
132 this.complete = $('<div/>').addClass('completions');
133 this.complete.attr('id','complete');
133 this.complete.attr('id','complete');
134
134
135 this.sel = $('<select/>')
135 this.sel = $('<select/>')
136 .attr('multiple','true')
136 .attr('multiple','true')
137 .attr('size',Math.min(10,this.raw_result.length));
137 .attr('size',Math.min(10,this.raw_result.length));
138 var pos = this.editor.cursorCoords();
138 var pos = this.editor.cursorCoords();
139
139
140 // TODO: I propose to remove enough horizontal pixel
140 // TODO: I propose to remove enough horizontal pixel
141 // to align the text later
141 // to align the text later
142 this.complete.css('left',pos.x+'px');
142 this.complete.css('left',pos.x+'px');
143 this.complete.css('top',pos.yBot+'px');
143 this.complete.css('top',pos.yBot+'px');
144 this.complete.append(this.sel);
144 this.complete.append(this.sel);
145
145
146 $('body').append(this.complete);
146 $('body').append(this.complete);
147 //build the container
147 //build the container
148 var that = this;
148 var that = this;
149 this.sel.dblclick(function(){that.pick()});
149 this.sel.dblclick(function(){that.pick()});
150 this.sel.blur(this.close);
150 this.sel.blur(this.close);
151 this.sel.keydown(function(event){that.keydown(event)});
151 this.sel.keydown(function(event){that.keydown(event)});
152
152
153 this.build_gui_list(this.raw_result);
153 this.build_gui_list(this.raw_result);
154
154
155 this.sel.focus();
155 this.sel.focus();
156 // Opera sometimes ignores focusing a freshly created node
156 // Opera sometimes ignores focusing a freshly created node
157 if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100);
157 if (window.opera) setTimeout(function(){if (!this.done) this.sel.focus();}, 100);
158 return true;
158 return true;
159 }
159 }
160
160
161 Completer.prototype.insert = function(completion) {
161 Completer.prototype.insert = function(completion) {
162 this.editor.replaceRange(completion.str, completion.from, completion.to);
162 this.editor.replaceRange(completion.str, completion.from, completion.to);
163 }
163 }
164
164
165 Completer.prototype.build_gui_list = function(completions){
165 Completer.prototype.build_gui_list = function(completions){
166 // Need to clear the all list
166 // Need to clear the all list
167 for (var i = 0; i < completions.length; ++i) {
167 for (var i = 0; i < completions.length; ++i) {
168 var opt = $('<option/>')
168 var opt = $('<option/>')
169 .text(completions[i].str)
169 .text(completions[i].str)
170 .addClass(completions[i].type);
170 .addClass(completions[i].type);
171 this.sel.append(opt);
171 this.sel.append(opt);
172 }
172 }
173 this.sel.children().first().attr('selected','true');
173 this.sel.children().first().attr('selected','true');
174 }
174 }
175
175
176 Completer.prototype.close = function() {
176 Completer.prototype.close = function() {
177 if (this.done) return;
177 if (this.done) return;
178 this.done = true;
178 this.done = true;
179 $('.completions').remove();
179 $('.completions').remove();
180 }
180 }
181
181
182 Completer.prototype.pick = function(){
182 Completer.prototype.pick = function(){
183 this.insert(this.raw_result[this.sel[0].selectedIndex]);
183 this.insert(this.raw_result[this.sel[0].selectedIndex]);
184 this.close();
184 this.close();
185 var that = this;
185 var that = this;
186 setTimeout(function(){that.editor.focus();}, 50);
186 setTimeout(function(){that.editor.focus();}, 50);
187 }
187 }
188
188
189
189
190 Completer.prototype.keydown = function(event) {
190 Completer.prototype.keydown = function(event) {
191 var code = event.keyCode;
191 var code = event.keyCode;
192 // Enter
192 // Enter
193 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
193 if (code == key.enter) {CodeMirror.e_stop(event); this.pick();}
194 // Escape or backspace
194 // Escape or backspace
195 else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();}
195 else if (code == key.esc ) {CodeMirror.e_stop(event); this.close(); this.editor.focus();}
196 else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();}
196 else if (code == key.space || code == key.backspace) {this.close(); this.editor.focus();}
197 else if (code == key.tab){
197 else if (code == key.tab){
198 //all the fastforwarding operation,
198 //all the fastforwarding operation,
199 //Check that shared start is not null which can append with prefixed completion
199 //Check that shared start is not null which can append with prefixed completion
200 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
200 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
201 // to erase py
201 // to erase py
202 var sh = sharedStart(this.raw_result);
202 var sh = sharedStart(this.raw_result);
203 if(sh){
203 if(sh){
204 this.insert(sh);
204 this.insert(sh);
205 }
205 }
206 this.close();
206 this.close();
207 CodeMirror.e_stop(event);
207 CodeMirror.e_stop(event);
208 this.editor.focus();
208 this.editor.focus();
209 //reinvoke self
209 //reinvoke self
210 var that = this;
210 var that = this;
211 setTimeout(function(){that.carryOnCompletion();}, 50);
211 setTimeout(function(){that.carryOnCompletion();}, 50);
212 }
212 }
213 else if (code == key.upArrow || code == key.downArrow) {
213 else if (code == key.upArrow || code == key.downArrow) {
214 // need to do that to be able to move the arrow
214 // need to do that to be able to move the arrow
215 // when on the first or last line ofo a code cell
215 // when on the first or last line ofo a code cell
216 event.stopPropagation();
216 event.stopPropagation();
217 }
217 }
218 else if (code != key.upArrow && code != key.downArrow) {
218 else if (code != key.upArrow && code != key.downArrow) {
219 this.close(); this.editor.focus();
219 this.close(); this.editor.focus();
220 //we give focus to the editor immediately and call sell in 50 ms
220 //we give focus to the editor immediately and call sell in 50 ms
221 var that = this;
221 var that = this;
222 setTimeout(function(){that.carryOnCompletion();}, 50);
222 setTimeout(function(){that.carryOnCompletion();}, 50);
223 }
223 }
224 }
224 }
225
225
226
226
227 IPython.Completer = Completer;
227 IPython.Completer = Completer;
228
228
229 return IPython;
229 return IPython;
230 }(IPython));
230 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now