##// END OF EJS Templates
set <select> width to auto
Juergen Hasch -
Show More
@@ -1,314 +1,314 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 var IPython = (function (IPython) {
4 var IPython = (function (IPython) {
5 // that will prevent us from misspelling
5 // that will prevent us from misspelling
6 "use strict";
6 "use strict";
7
7
8 // easyier key mapping
8 // easyier key mapping
9 var key = IPython.utils.keycodes;
9 var key = IPython.utils.keycodes;
10
10
11 function prepend_n_prc(str, n) {
11 function prepend_n_prc(str, n) {
12 for( var i =0 ; i< n ; i++)
12 for( var i =0 ; i< n ; i++)
13 { str = '%'+str }
13 { str = '%'+str }
14 return str;
14 return str;
15 }
15 }
16
16
17 function _existing_completion(item, completion_array){
17 function _existing_completion(item, completion_array){
18 for( var c in completion_array ) {
18 for( var c in completion_array ) {
19 if(completion_array[c].substr(-item.length) == item)
19 if(completion_array[c].substr(-item.length) == item)
20 { return true; }
20 { return true; }
21 }
21 }
22 return false;
22 return false;
23 }
23 }
24
24
25 // what is the common start of all completions
25 // what is the common start of all completions
26 function shared_start(B, drop_prct) {
26 function shared_start(B, drop_prct) {
27 if (B.length == 1) {
27 if (B.length == 1) {
28 return B[0];
28 return B[0];
29 }
29 }
30 var A = new Array();
30 var A = new Array();
31 var common;
31 var common;
32 var min_lead_prct = 10;
32 var min_lead_prct = 10;
33 for (var i = 0; i < B.length; i++) {
33 for (var i = 0; i < B.length; i++) {
34 var str = B[i].str;
34 var str = B[i].str;
35 var localmin = 0;
35 var localmin = 0;
36 if(drop_prct == true){
36 if(drop_prct == true){
37 while ( str.substr(0, 1) == '%') {
37 while ( str.substr(0, 1) == '%') {
38 localmin = localmin+1;
38 localmin = localmin+1;
39 str = str.substring(1);
39 str = str.substring(1);
40 }
40 }
41 }
41 }
42 min_lead_prct = Math.min(min_lead_prct, localmin);
42 min_lead_prct = Math.min(min_lead_prct, localmin);
43 A.push(str);
43 A.push(str);
44 }
44 }
45
45
46 if (A.length > 1) {
46 if (A.length > 1) {
47 var tem1, tem2, s;
47 var tem1, tem2, s;
48 A = A.slice(0).sort();
48 A = A.slice(0).sort();
49 tem1 = A[0];
49 tem1 = A[0];
50 s = tem1.length;
50 s = tem1.length;
51 tem2 = A.pop();
51 tem2 = A.pop();
52 while (s && tem2.indexOf(tem1) == -1) {
52 while (s && tem2.indexOf(tem1) == -1) {
53 tem1 = tem1.substring(0, --s);
53 tem1 = tem1.substring(0, --s);
54 }
54 }
55 if (tem1 == "" || tem2.indexOf(tem1) != 0) {
55 if (tem1 == "" || tem2.indexOf(tem1) != 0) {
56 return prepend_n_prc('', min_lead_prct);
56 return prepend_n_prc('', min_lead_prct);
57 }
57 }
58 return {
58 return {
59 str: prepend_n_prc(tem1, min_lead_prct),
59 str: prepend_n_prc(tem1, min_lead_prct),
60 type: "computed",
60 type: "computed",
61 from: B[0].from,
61 from: B[0].from,
62 to: B[0].to
62 to: B[0].to
63 };
63 };
64 }
64 }
65 return null;
65 return null;
66 }
66 }
67
67
68
68
69 var Completer = function (cell) {
69 var Completer = function (cell) {
70 this.cell = cell;
70 this.cell = cell;
71 this.editor = cell.code_mirror;
71 this.editor = cell.code_mirror;
72 var that = this;
72 var that = this;
73 $([IPython.events]).on('status_busy.Kernel', function () {
73 $([IPython.events]).on('status_busy.Kernel', function () {
74 that.skip_kernel_completion = true;
74 that.skip_kernel_completion = true;
75 });
75 });
76 $([IPython.events]).on('status_idle.Kernel', function () {
76 $([IPython.events]).on('status_idle.Kernel', function () {
77 that.skip_kernel_completion = false;
77 that.skip_kernel_completion = false;
78 });
78 });
79 };
79 };
80
80
81
81
82 Completer.prototype.startCompletion = function () {
82 Completer.prototype.startCompletion = function () {
83 // call for a 'first' completion, that will set the editor and do some
83 // call for a 'first' completion, that will set the editor and do some
84 // special behaviour like autopicking if only one completion availlable
84 // special behaviour like autopicking if only one completion availlable
85 //
85 //
86 if (this.editor.somethingSelected()) return;
86 if (this.editor.somethingSelected()) return;
87 this.done = false;
87 this.done = false;
88 // use to get focus back on opera
88 // use to get focus back on opera
89 this.carry_on_completion(true);
89 this.carry_on_completion(true);
90 };
90 };
91
91
92 Completer.prototype.carry_on_completion = function (ff) {
92 Completer.prototype.carry_on_completion = function (ff) {
93 // Pass true as parameter if you want the commpleter to autopick when
93 // Pass true as parameter if you want the commpleter to autopick when
94 // only one completion. This function is automatically reinvoked at
94 // only one completion. This function is automatically reinvoked at
95 // each keystroke with ff = false
95 // each keystroke with ff = false
96 var cur = this.editor.getCursor();
96 var cur = this.editor.getCursor();
97 var line = this.editor.getLine(cur.line);
97 var line = this.editor.getLine(cur.line);
98 var pre_cursor = this.editor.getRange({
98 var pre_cursor = this.editor.getRange({
99 line: cur.line,
99 line: cur.line,
100 ch: cur.ch - 1
100 ch: cur.ch - 1
101 }, cur);
101 }, cur);
102
102
103 // we need to check that we are still on a word boundary
103 // we need to check that we are still on a word boundary
104 // because while typing the completer is still reinvoking itself
104 // because while typing the completer is still reinvoking itself
105 if (!/[0-9a-z._/\\:~-]/i.test(pre_cursor)) {
105 if (!/[0-9a-z._/\\:~-]/i.test(pre_cursor)) {
106 this.close();
106 this.close();
107 return;
107 return;
108 }
108 }
109
109
110 this.autopick = false;
110 this.autopick = false;
111 if (ff != 'undefined' && ff == true) {
111 if (ff != 'undefined' && ff == true) {
112 this.autopick = true;
112 this.autopick = true;
113 }
113 }
114
114
115 // We want a single cursor position.
115 // We want a single cursor position.
116 if (this.editor.somethingSelected()) return;
116 if (this.editor.somethingSelected()) return;
117
117
118 // one kernel completion came back, finish_completing will be called with the results
118 // one kernel completion came back, finish_completing will be called with the results
119 // we fork here and directly call finish completing if kernel is busy
119 // we fork here and directly call finish completing if kernel is busy
120 if (this.skip_kernel_completion == true) {
120 if (this.skip_kernel_completion == true) {
121 this.finish_completing({
121 this.finish_completing({
122 'matches': [],
122 'matches': [],
123 matched_text: ""
123 matched_text: ""
124 })
124 })
125 } else {
125 } else {
126 var callbacks = {
126 var callbacks = {
127 'complete_reply': $.proxy(this.finish_completing, this)
127 'complete_reply': $.proxy(this.finish_completing, this)
128 };
128 };
129 this.cell.kernel.complete(line, cur.ch, callbacks);
129 this.cell.kernel.complete(line, cur.ch, callbacks);
130 }
130 }
131 };
131 };
132
132
133 Completer.prototype.finish_completing = function (content) {
133 Completer.prototype.finish_completing = function (content) {
134 // let's build a function that wrap all that stuff into what is needed
134 // let's build a function that wrap all that stuff into what is needed
135 // for the new completer:
135 // for the new completer:
136 var matched_text = content.matched_text;
136 var matched_text = content.matched_text;
137 var matches = content.matches;
137 var matches = content.matches;
138
138
139 var cur = this.editor.getCursor();
139 var cur = this.editor.getCursor();
140 var results = CodeMirror.contextHint(this.editor);
140 var results = CodeMirror.contextHint(this.editor);
141 var filterd_results = Array();
141 var filterd_results = Array();
142 //remove results from context completion
142 //remove results from context completion
143 //that are already in kernel completion
143 //that are already in kernel completion
144 for(var elm in results) {
144 for(var elm in results) {
145 if(_existing_completion(results[elm]['str'], matches) == false)
145 if(_existing_completion(results[elm]['str'], matches) == false)
146 { filterd_results.push(results[elm]); }
146 { filterd_results.push(results[elm]); }
147 }
147 }
148
148
149 // append the introspection result, in order, at at the beginning of
149 // append the introspection result, in order, at at the beginning of
150 // the table and compute the replacement range from current cursor
150 // the table and compute the replacement range from current cursor
151 // positon and matched_text length.
151 // positon and matched_text length.
152 for (var i = matches.length - 1; i >= 0; --i) {
152 for (var i = matches.length - 1; i >= 0; --i) {
153 filterd_results.unshift({
153 filterd_results.unshift({
154 str: matches[i],
154 str: matches[i],
155 type: "introspection",
155 type: "introspection",
156 from: {
156 from: {
157 line: cur.line,
157 line: cur.line,
158 ch: cur.ch - matched_text.length
158 ch: cur.ch - matched_text.length
159 },
159 },
160 to: {
160 to: {
161 line: cur.line,
161 line: cur.line,
162 ch: cur.ch
162 ch: cur.ch
163 }
163 }
164 });
164 });
165 }
165 }
166
166
167 // one the 2 sources results have been merge, deal with it
167 // one the 2 sources results have been merge, deal with it
168 this.raw_result = filterd_results;
168 this.raw_result = filterd_results;
169
169
170 // if empty result return
170 // if empty result return
171 if (!this.raw_result || !this.raw_result.length) return;
171 if (!this.raw_result || !this.raw_result.length) return;
172
172
173 // When there is only one completion, use it directly.
173 // When there is only one completion, use it directly.
174 if (this.autopick == true && this.raw_result.length == 1) {
174 if (this.autopick == true && this.raw_result.length == 1) {
175 this.insert(this.raw_result[0]);
175 this.insert(this.raw_result[0]);
176 return;
176 return;
177 }
177 }
178
178
179 if (this.raw_result.length == 1) {
179 if (this.raw_result.length == 1) {
180 // test if first and only completion totally matches
180 // test if first and only completion totally matches
181 // what is typed, in this case dismiss
181 // what is typed, in this case dismiss
182 var str = this.raw_result[0].str;
182 var str = this.raw_result[0].str;
183 var pre_cursor = this.editor.getRange({
183 var pre_cursor = this.editor.getRange({
184 line: cur.line,
184 line: cur.line,
185 ch: cur.ch - str.length
185 ch: cur.ch - str.length
186 }, cur);
186 }, cur);
187 if (pre_cursor == str) {
187 if (pre_cursor == str) {
188 this.close();
188 this.close();
189 return;
189 return;
190 }
190 }
191 }
191 }
192
192
193 this.complete = $('<div/>').addClass('completions');
193 this.complete = $('<div/>').addClass('completions');
194 this.complete.attr('id', 'complete');
194 this.complete.attr('id', 'complete');
195
195
196 this.sel = $('<select/>').attr('multiple', 'true').attr('size', Math.min(10, this.raw_result.length));
196 this.sel = $('<select style="width: auto"/>').attr('multiple', 'true').attr('size', Math.min(10, this.raw_result.length));
197 var pos = this.editor.cursorCoords();
197 var pos = this.editor.cursorCoords();
198
198
199 // TODO: I propose to remove enough horizontal pixel
199 // TODO: I propose to remove enough horizontal pixel
200 // to align the text later
200 // to align the text later
201 this.complete.css('left', pos.x + 'px');
201 this.complete.css('left', pos.x + 'px');
202 this.complete.css('top', pos.yBot + 'px');
202 this.complete.css('top', pos.yBot + 'px');
203 this.complete.append(this.sel);
203 this.complete.append(this.sel);
204
204
205 $('body').append(this.complete);
205 $('body').append(this.complete);
206 //build the container
206 //build the container
207 var that = this;
207 var that = this;
208 this.sel.dblclick(function () {
208 this.sel.dblclick(function () {
209 that.pick();
209 that.pick();
210 });
210 });
211 this.sel.blur(this.close);
211 this.sel.blur(this.close);
212 this.sel.keydown(function (event) {
212 this.sel.keydown(function (event) {
213 that.keydown(event);
213 that.keydown(event);
214 });
214 });
215
215
216 this.build_gui_list(this.raw_result);
216 this.build_gui_list(this.raw_result);
217
217
218 this.sel.focus();
218 this.sel.focus();
219 // Opera sometimes ignores focusing a freshly created node
219 // Opera sometimes ignores focusing a freshly created node
220 if (window.opera) setTimeout(function () {
220 if (window.opera) setTimeout(function () {
221 if (!this.done) this.sel.focus();
221 if (!this.done) this.sel.focus();
222 }, 100);
222 }, 100);
223 return true;
223 return true;
224 }
224 }
225
225
226 Completer.prototype.insert = function (completion) {
226 Completer.prototype.insert = function (completion) {
227 this.editor.replaceRange(completion.str, completion.from, completion.to);
227 this.editor.replaceRange(completion.str, completion.from, completion.to);
228 }
228 }
229
229
230 Completer.prototype.build_gui_list = function (completions) {
230 Completer.prototype.build_gui_list = function (completions) {
231 // Need to clear the all list
231 // Need to clear the all list
232 for (var i = 0; i < completions.length; ++i) {
232 for (var i = 0; i < completions.length; ++i) {
233 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
233 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
234 this.sel.append(opt);
234 this.sel.append(opt);
235 }
235 }
236 this.sel.children().first().attr('selected', 'true');
236 this.sel.children().first().attr('selected', 'true');
237 }
237 }
238
238
239 Completer.prototype.close = function () {
239 Completer.prototype.close = function () {
240 if (this.done) return;
240 if (this.done) return;
241 this.done = true;
241 this.done = true;
242 $('.completions').remove();
242 $('.completions').remove();
243 }
243 }
244
244
245 Completer.prototype.pick = function () {
245 Completer.prototype.pick = function () {
246 this.insert(this.raw_result[this.sel[0].selectedIndex]);
246 this.insert(this.raw_result[this.sel[0].selectedIndex]);
247 this.close();
247 this.close();
248 var that = this;
248 var that = this;
249 setTimeout(function () {
249 setTimeout(function () {
250 that.editor.focus();
250 that.editor.focus();
251 }, 50);
251 }, 50);
252 }
252 }
253
253
254
254
255 Completer.prototype.keydown = function (event) {
255 Completer.prototype.keydown = function (event) {
256 var code = event.keyCode;
256 var code = event.keyCode;
257 var that = this;
257 var that = this;
258 var special_key = false;
258 var special_key = false;
259
259
260 // detect special keys like SHIFT,PGUP,...
260 // detect special keys like SHIFT,PGUP,...
261 for( var _key in key ) {
261 for( var _key in key ) {
262 if (code == key[_key] ) {
262 if (code == key[_key] ) {
263 special_key = true;
263 special_key = true;
264 }
264 }
265 };
265 };
266
266
267 // Enter
267 // Enter
268 if (code == key.ENTER) {
268 if (code == key.ENTER) {
269 CodeMirror.e_stop(event);
269 CodeMirror.e_stop(event);
270 this.pick();
270 this.pick();
271 }
271 }
272 // Escape or backspace
272 // Escape or backspace
273 else if (code == key.ESC) {
273 else if (code == key.ESC) {
274 CodeMirror.e_stop(event);
274 CodeMirror.e_stop(event);
275 this.close();
275 this.close();
276 this.editor.focus();
276 this.editor.focus();
277 } else if (code == key.SPACE || code == key.BACKSPACE) {
277 } else if (code == key.SPACE || code == key.BACKSPACE) {
278 this.close();
278 this.close();
279 this.editor.focus();
279 this.editor.focus();
280 } else if (code == key.TAB) {
280 } else if (code == key.TAB) {
281 //all the fastforwarding operation,
281 //all the fastforwarding operation,
282 //Check that shared start is not null which can append with prefixed completion
282 //Check that shared start is not null which can append with prefixed completion
283 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
283 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
284 // to erase py
284 // to erase py
285 var sh = shared_start(this.raw_result, true);
285 var sh = shared_start(this.raw_result, true);
286 if (sh) {
286 if (sh) {
287 this.insert(sh);
287 this.insert(sh);
288 }
288 }
289 this.close();
289 this.close();
290 CodeMirror.e_stop(event);
290 CodeMirror.e_stop(event);
291 this.editor.focus();
291 this.editor.focus();
292 //reinvoke self
292 //reinvoke self
293 setTimeout(function () {
293 setTimeout(function () {
294 that.carry_on_completion();
294 that.carry_on_completion();
295 }, 50);
295 }, 50);
296 } else if (code == key.UPARROW || code == key.DOWNARROW) {
296 } else if (code == key.UPARROW || code == key.DOWNARROW) {
297 // need to do that to be able to move the arrow
297 // need to do that to be able to move the arrow
298 // when on the first or last line ofo a code cell
298 // when on the first or last line ofo a code cell
299 event.stopPropagation();
299 event.stopPropagation();
300 } else if (special_key != true) {
300 } else if (special_key != true) {
301 this.close();
301 this.close();
302 this.editor.focus();
302 this.editor.focus();
303 //we give focus to the editor immediately and call sell in 50 ms
303 //we give focus to the editor immediately and call sell in 50 ms
304 setTimeout(function () {
304 setTimeout(function () {
305 that.carry_on_completion();
305 that.carry_on_completion();
306 }, 50);
306 }, 50);
307 }
307 }
308 }
308 }
309
309
310
310
311 IPython.Completer = Completer;
311 IPython.Completer = Completer;
312
312
313 return IPython;
313 return IPython;
314 }(IPython));
314 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now