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