##// END OF EJS Templates
Code comments and minor fixes.
Brian E. Granger -
Show More
@@ -1,491 +1,494 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Cell
10 10 //============================================================================
11 11 /**
12 12 * An extendable module that provide base functionnality to create cell for notebook.
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule Cell
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19 "use strict";
20 20
21 21 var utils = IPython.utils;
22 22
23 23 /**
24 24 * The Base `Cell` class from which to inherit
25 25 * @class Cell
26 26 **/
27 27
28 28 /*
29 29 * @constructor
30 30 *
31 31 * @param {object|undefined} [options]
32 32 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
33 33 */
34 34 var Cell = function (options) {
35 35
36 36 options = this.mergeopt(Cell, options);
37 37 // superclass default overwrite our default
38 38
39 39 this.placeholder = options.placeholder || '';
40 40 this.read_only = options.cm_config.readOnly;
41 41 this.selected = false;
42 42 this.rendered = false;
43 43 this.mode = 'command';
44 44 this.metadata = {};
45 45 // load this from metadata later ?
46 46 this.user_highlight = 'auto';
47 47 this.cm_config = options.cm_config;
48 48 this.cell_id = utils.uuid();
49 49 this._options = options;
50 50
51 51 // For JS VM engines optimisation, attributes should be all set (even
52 52 // to null) in the constructor, and if possible, if different subclass
53 53 // have new attributes with same name, they should be created in the
54 54 // same order. Easiest is to create and set to null in parent class.
55 55
56 56 this.element = null;
57 57 this.cell_type = this.cell_type || null;
58 58 this.code_mirror = null;
59 59
60 60
61 61 this.create_element();
62 62 if (this.element !== null) {
63 63 this.element.data("cell", this);
64 64 this.bind_events();
65 65 }
66 66 };
67 67
68 68 Cell.options_default = {
69 69 cm_config : {
70 70 indentUnit : 4,
71 71 readOnly: false,
72 72 theme: "default"
73 73 }
74 74 };
75 75
76 76 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
77 77 // by disabling drag/drop altogether on Safari
78 78 // https://github.com/marijnh/CodeMirror/issues/332
79 79
80 80 if (utils.browser[0] == "Safari") {
81 81 Cell.options_default.cm_config.dragDrop = false;
82 82 }
83 83
84 84 Cell.prototype.mergeopt = function(_class, options, overwrite){
85 85 options = options || {};
86 86 overwrite = overwrite || {};
87 87 return $.extend(true, {}, _class.options_default, options, overwrite)
88 88
89 89 }
90 90
91 91
92 92
93 93 /**
94 94 * Empty. Subclasses must implement create_element.
95 95 * This should contain all the code to create the DOM element in notebook
96 96 * and will be called by Base Class constructor.
97 97 * @method create_element
98 98 */
99 99 Cell.prototype.create_element = function () {
100 100 };
101 101
102 102
103 103 /**
104 104 * Subclasses can implement override bind_events.
105 105 * Be carefull to call the parent method when overwriting as it fires event.
106 106 * this will be triggerd after create_element in constructor.
107 107 * @method bind_events
108 108 */
109 109 Cell.prototype.bind_events = function () {
110 110 var that = this;
111 111 // We trigger events so that Cell doesn't have to depend on Notebook.
112 112 that.element.click(function (event) {
113 113 if (that.selected === false) {
114 114 $([IPython.events]).trigger('select.Cell', {'cell':that});
115 115 };
116 116 });
117 117 that.element.focusin(function (event) {
118 118 if (that.selected === false) {
119 119 $([IPython.events]).trigger('select.Cell', {'cell':that});
120 120 };
121 121 });
122 122 that.element.focusout(function (event) {
123 123 var is_or_has = function (a, b) {
124 124 // Is b a child of a or a itself?
125 125 return a.has(b).length !==0 || a.is(b);
126 126 }
127 127 if (that.mode === 'edit') {
128 // Most of the time, when a cell is in edit mode and focusout
129 // fires, it means we should enter command mode. But there are cases
130 // when we should not enter command mode.
128 131 setTimeout(function () {
129 132 var trigger = true;
130 133 var target = $(document.activeElement);
131 var completer = that.element.find($('div.completions'));
132 var tooltip = $('div#tooltip')
134 var completer = $('div.completions');
135 var tooltip = $('div#tooltip');
133 136 if (target.length > 0) {
134 137 // If the focused element (target) is inside the cell
135 138 // (that.element) don't enter command mode.
136 139 if (is_or_has(that.element, target)) {
137 140 trigger = false;
138 141 // The focused element is outside the cell
139 142 } else {
140 143 // If the focused element is the tooltip or completer
141 144 // don't enter command mode, otherwise do.
142 145 trigger = true;
143 146 if (tooltip.length > 0 && is_or_has(tooltip, target)) {
144 147 trigger = false;
145 148 } else if (completer.length > 0 && is_or_has(completer, target)) {
146 149 trigger = false;
147 150 }
148 151 }
149 152 }
150 153 if (trigger) {
151 154 $([IPython.events]).trigger('command_mode.Cell', {'cell':that});
152 155 }
153 156 }, 1);
154 157 };
155 158 });
156 159 if (this.code_mirror) {
157 160 this.code_mirror.on("change", function(cm, change) {
158 161 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
159 162 });
160 163 };
161 164 if (this.code_mirror) {
162 165 this.code_mirror.on('focus', function(cm, change) {
163 166 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
164 167 });
165 168 };
166 169 };
167 170
168 171 /**
169 172 * Triger typsetting of math by mathjax on current cell element
170 173 * @method typeset
171 174 */
172 175 Cell.prototype.typeset = function () {
173 176 if (window.MathJax) {
174 177 var cell_math = this.element.get(0);
175 178 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
176 179 };
177 180 };
178 181
179 182 /**
180 183 * handle cell level logic when a cell is selected
181 184 * @method select
182 185 * @return is the action being taken
183 186 */
184 187 Cell.prototype.select = function () {
185 188 if (!this.selected) {
186 189 this.element.addClass('selected');
187 190 this.element.removeClass('unselected');
188 191 this.selected = true;
189 192 return true;
190 193 } else {
191 194 return false;
192 195 };
193 196 };
194 197
195 198 /**
196 199 * handle cell level logic when a cell is unselected
197 200 * @method unselect
198 201 * @return is the action being taken
199 202 */
200 203 Cell.prototype.unselect = function () {
201 204 if (this.selected) {
202 205 this.element.addClass('unselected');
203 206 this.element.removeClass('selected');
204 207 this.selected = false;
205 208 return true;
206 209 } else {
207 210 return false;
208 211 };
209 212 };
210 213
211 214 /**
212 215 * handle cell level logic when a cell is rendered
213 216 * @method render
214 217 * @return is the action being taken
215 218 */
216 219 Cell.prototype.render = function () {
217 220 if (!this.rendered) {
218 221 this.element.addClass('rendered');
219 222 this.element.removeClass('unrendered');
220 223 this.rendered = true;
221 224 return true;
222 225 } else {
223 226 return false;
224 227 };
225 228 };
226 229
227 230 /**
228 231 * handle cell level logic when a cell is unrendered
229 232 * @method unrender
230 233 * @return is the action being taken
231 234 */
232 235 Cell.prototype.unrender = function () {
233 236 if (this.rendered) {
234 237 this.element.addClass('unrendered');
235 238 this.element.removeClass('rendered');
236 239 this.rendered = false;
237 240 return true;
238 241 } else {
239 242 return false;
240 243 };
241 244 };
242 245
243 246 /**
244 247 * enter the command mode for the cell
245 248 * @method command_mode
246 249 * @return is the action being taken
247 250 */
248 251 Cell.prototype.command_mode = function () {
249 252 if (this.mode !== 'command') {
250 253 this.element.addClass('command_mode');
251 254 this.element.removeClass('edit_mode');
252 255 this.mode = 'command';
253 256 return true;
254 257 } else {
255 258 return false;
256 259 };
257 260 };
258 261
259 262 /**
260 263 * enter the edit mode for the cell
261 264 * @method command_mode
262 265 * @return is the action being taken
263 266 */
264 267 Cell.prototype.edit_mode = function () {
265 268 if (this.mode !== 'edit') {
266 269 this.element.addClass('edit_mode');
267 270 this.element.removeClass('command_mode');
268 271 this.mode = 'edit';
269 272 return true;
270 273 } else {
271 274 return false;
272 275 };
273 276 }
274 277
275 278 /**
276 279 * Focus the cell in the DOM sense
277 280 * @method focus_cell
278 281 */
279 282 Cell.prototype.focus_cell = function () {
280 283 this.element.focus();
281 284 }
282 285
283 286 /**
284 287 * Focus the editor area so a user can type
285 288 * @method focus_editor
286 289 */
287 290 Cell.prototype.focus_editor = function () {
288 291 this.refresh();
289 292 this.code_mirror.focus();
290 293 }
291 294
292 295 /**
293 296 * Refresh codemirror instance
294 297 * @method refresh
295 298 */
296 299 Cell.prototype.refresh = function () {
297 300 this.code_mirror.refresh();
298 301 };
299 302
300 303 /**
301 304 * should be overritten by subclass
302 305 * @method get_text
303 306 */
304 307 Cell.prototype.get_text = function () {
305 308 };
306 309
307 310 /**
308 311 * should be overritten by subclass
309 312 * @method set_text
310 313 * @param {string} text
311 314 */
312 315 Cell.prototype.set_text = function (text) {
313 316 };
314 317
315 318 /**
316 319 * should be overritten by subclass
317 320 * serialise cell to json.
318 321 * @method toJSON
319 322 **/
320 323 Cell.prototype.toJSON = function () {
321 324 var data = {};
322 325 data.metadata = this.metadata;
323 326 data.cell_type = this.cell_type;
324 327 return data;
325 328 };
326 329
327 330
328 331 /**
329 332 * should be overritten by subclass
330 333 * @method fromJSON
331 334 **/
332 335 Cell.prototype.fromJSON = function (data) {
333 336 if (data.metadata !== undefined) {
334 337 this.metadata = data.metadata;
335 338 }
336 339 this.celltoolbar.rebuild();
337 340 };
338 341
339 342
340 343 /**
341 344 * can the cell be split into two cells
342 345 * @method is_splittable
343 346 **/
344 347 Cell.prototype.is_splittable = function () {
345 348 return true;
346 349 };
347 350
348 351
349 352 /**
350 353 * can the cell be merged with other cells
351 354 * @method is_mergeable
352 355 **/
353 356 Cell.prototype.is_mergeable = function () {
354 357 return true;
355 358 };
356 359
357 360
358 361 /**
359 362 * @return {String} - the text before the cursor
360 363 * @method get_pre_cursor
361 364 **/
362 365 Cell.prototype.get_pre_cursor = function () {
363 366 var cursor = this.code_mirror.getCursor();
364 367 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
365 368 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
366 369 return text;
367 370 }
368 371
369 372
370 373 /**
371 374 * @return {String} - the text after the cursor
372 375 * @method get_post_cursor
373 376 **/
374 377 Cell.prototype.get_post_cursor = function () {
375 378 var cursor = this.code_mirror.getCursor();
376 379 var last_line_num = this.code_mirror.lineCount()-1;
377 380 var last_line_len = this.code_mirror.getLine(last_line_num).length;
378 381 var end = {line:last_line_num, ch:last_line_len}
379 382 var text = this.code_mirror.getRange(cursor, end);
380 383 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
381 384 return text;
382 385 };
383 386
384 387 /**
385 388 * Show/Hide CodeMirror LineNumber
386 389 * @method show_line_numbers
387 390 *
388 391 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
389 392 **/
390 393 Cell.prototype.show_line_numbers = function (value) {
391 394 this.code_mirror.setOption('lineNumbers', value);
392 395 this.code_mirror.refresh();
393 396 };
394 397
395 398 /**
396 399 * Toggle CodeMirror LineNumber
397 400 * @method toggle_line_numbers
398 401 **/
399 402 Cell.prototype.toggle_line_numbers = function () {
400 403 var val = this.code_mirror.getOption('lineNumbers');
401 404 this.show_line_numbers(!val);
402 405 };
403 406
404 407 /**
405 408 * Force codemirror highlight mode
406 409 * @method force_highlight
407 410 * @param {object} - CodeMirror mode
408 411 **/
409 412 Cell.prototype.force_highlight = function(mode) {
410 413 this.user_highlight = mode;
411 414 this.auto_highlight();
412 415 };
413 416
414 417 /**
415 418 * Try to autodetect cell highlight mode, or use selected mode
416 419 * @methods _auto_highlight
417 420 * @private
418 421 * @param {String|object|undefined} - CodeMirror mode | 'auto'
419 422 **/
420 423 Cell.prototype._auto_highlight = function (modes) {
421 424 //Here we handle manually selected modes
422 425 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
423 426 {
424 427 var mode = this.user_highlight;
425 428 CodeMirror.autoLoadMode(this.code_mirror, mode);
426 429 this.code_mirror.setOption('mode', mode);
427 430 return;
428 431 }
429 432 var current_mode = this.code_mirror.getOption('mode', mode);
430 433 var first_line = this.code_mirror.getLine(0);
431 434 // loop on every pairs
432 435 for( var mode in modes) {
433 436 var regs = modes[mode]['reg'];
434 437 // only one key every time but regexp can't be keys...
435 438 for(var reg in regs ) {
436 439 // here we handle non magic_modes
437 440 if(first_line.match(regs[reg]) != null) {
438 441 if(current_mode == mode){
439 442 return;
440 443 }
441 444 if (mode.search('magic_') != 0) {
442 445 this.code_mirror.setOption('mode', mode);
443 446 CodeMirror.autoLoadMode(this.code_mirror, mode);
444 447 return;
445 448 }
446 449 var open = modes[mode]['open']|| "%%";
447 450 var close = modes[mode]['close']|| "%%end";
448 451 var mmode = mode;
449 452 mode = mmode.substr(6);
450 453 if(current_mode == mode){
451 454 return;
452 455 }
453 456 CodeMirror.autoLoadMode(this.code_mirror, mode);
454 457 // create on the fly a mode that swhitch between
455 458 // plain/text and smth else otherwise `%%` is
456 459 // source of some highlight issues.
457 460 // we use patchedGetMode to circumvent a bug in CM
458 461 CodeMirror.defineMode(mmode , function(config) {
459 462 return CodeMirror.multiplexingMode(
460 463 CodeMirror.patchedGetMode(config, 'text/plain'),
461 464 // always set someting on close
462 465 {open: open, close: close,
463 466 mode: CodeMirror.patchedGetMode(config, mode),
464 467 delimStyle: "delimit"
465 468 }
466 469 );
467 470 });
468 471 this.code_mirror.setOption('mode', mmode);
469 472 return;
470 473 }
471 474 }
472 475 }
473 476 // fallback on default
474 477 var default_mode
475 478 try {
476 479 default_mode = this._options.cm_config.mode;
477 480 } catch(e) {
478 481 default_mode = 'text/plain';
479 482 }
480 483 if( current_mode === default_mode){
481 484 return
482 485 }
483 486 this.code_mirror.setOption('mode', default_mode);
484 487 };
485 488
486 489 IPython.Cell = Cell;
487 490
488 491 return IPython;
489 492
490 493 }(IPython));
491 494
@@ -1,357 +1,357 b''
1 1 // function completer.
2 2 //
3 3 // completer should be a class that takes 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 // easier 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 }
15 15 return str;
16 16 }
17 17
18 18 function _existing_completion(item, completion_array){
19 19 for( var c in completion_array ) {
20 20 if(completion_array[c].trim().substr(-item.length) == item)
21 21 { return true; }
22 22 }
23 23 return false;
24 24 }
25 25
26 26 // what is the common start of all completions
27 27 function shared_start(B, drop_prct) {
28 28 if (B.length == 1) {
29 29 return B[0];
30 30 }
31 31 var A = new Array();
32 32 var common;
33 33 var min_lead_prct = 10;
34 34 for (var i = 0; i < B.length; i++) {
35 35 var str = B[i].str;
36 36 var localmin = 0;
37 37 if(drop_prct === true){
38 38 while ( str.substr(0, 1) == '%') {
39 39 localmin = localmin+1;
40 40 str = str.substring(1);
41 41 }
42 42 }
43 43 min_lead_prct = Math.min(min_lead_prct, localmin);
44 44 A.push(str);
45 45 }
46 46
47 47 if (A.length > 1) {
48 48 var tem1, tem2, s;
49 49 A = A.slice(0).sort();
50 50 tem1 = A[0];
51 51 s = tem1.length;
52 52 tem2 = A.pop();
53 53 while (s && tem2.indexOf(tem1) == -1) {
54 54 tem1 = tem1.substring(0, --s);
55 55 }
56 56 if (tem1 === "" || tem2.indexOf(tem1) !== 0) {
57 57 return {
58 58 str:prepend_n_prc('', min_lead_prct),
59 59 type: "computed",
60 60 from: B[0].from,
61 61 to: B[0].to
62 62 };
63 63 }
64 64 return {
65 65 str: prepend_n_prc(tem1, min_lead_prct),
66 66 type: "computed",
67 67 from: B[0].from,
68 68 to: B[0].to
69 69 };
70 70 }
71 71 return null;
72 72 }
73 73
74 74
75 75 var Completer = function (cell) {
76 76 this.cell = cell;
77 77 this.editor = cell.code_mirror;
78 78 var that = this;
79 79 $([IPython.events]).on('status_busy.Kernel', function () {
80 80 that.skip_kernel_completion = true;
81 81 });
82 82 $([IPython.events]).on('status_idle.Kernel', function () {
83 83 that.skip_kernel_completion = false;
84 84 });
85 85 };
86 86
87 87
88 88 Completer.prototype.startCompletion = function () {
89 89 // call for a 'first' completion, that will set the editor and do some
90 90 // special behaviour like autopicking if only one completion availlable
91 91 //
92 92 if (this.editor.somethingSelected()) return;
93 93 this.done = false;
94 94 // use to get focus back on opera
95 95 this.carry_on_completion(true);
96 96 };
97 97
98 98
99 99 // easy access for julia to monkeypatch
100 100 //
101 101 Completer.reinvoke_re = /[%0-9a-z._/\\:~-]/i;
102 102
103 103 Completer.prototype.reinvoke= function(pre_cursor, block, cursor){
104 104 return Completer.reinvoke_re.test(pre_cursor);
105 105 }
106 106
107 107 /**
108 108 *
109 109 * pass true as parameter if this is the first invocation of the completer
110 110 * this will prevent the completer to dissmiss itself if it is not on a
111 111 * word boundary like pressing tab after a space, and make it autopick the
112 112 * only choice if there is only one which prevent from popping the UI. as
113 113 * well as fast-forwarding the typing if all completion have a common
114 114 * shared start
115 115 **/
116 116 Completer.prototype.carry_on_completion = function (first_invocation) {
117 117 // Pass true as parameter if you want the completer to autopick when
118 118 // only one completion. This function is automatically reinvoked at
119 119 // each keystroke with first_invocation = false
120 120 var cur = this.editor.getCursor();
121 121 var line = this.editor.getLine(cur.line);
122 122 var pre_cursor = this.editor.getRange({
123 123 line: cur.line,
124 124 ch: cur.ch - 1
125 125 }, cur);
126 126
127 127 // we need to check that we are still on a word boundary
128 128 // because while typing the completer is still reinvoking itself
129 129 // so dismiss if we are on a "bad" caracter
130 130 if (!this.reinvoke(pre_cursor) && !first_invocation) {
131 131 this.close();
132 132 return;
133 133 }
134 134
135 135 this.autopick = false;
136 136 if (first_invocation) {
137 137 this.autopick = true;
138 138 }
139 139
140 140 // We want a single cursor position.
141 141 if (this.editor.somethingSelected()) {
142 142 return;
143 143 };
144 144
145 145 // one kernel completion came back, finish_completing will be called with the results
146 146 // we fork here and directly call finish completing if kernel is busy
147 147 if (this.skip_kernel_completion == true) {
148 148 this.finish_completing({
149 149 'matches': [],
150 150 matched_text: ""
151 151 })
152 152 } else {
153 153 this.cell.kernel.complete(line, cur.ch, $.proxy(this.finish_completing, this));
154 154 }
155 155 };
156 156
157 157 Completer.prototype.finish_completing = function (msg) {
158 158 // let's build a function that wrap all that stuff into what is needed
159 159 // for the new completer:
160 160 var content = msg.content;
161 161 var matched_text = content.matched_text;
162 162 var matches = content.matches;
163 163
164 164 var cur = this.editor.getCursor();
165 165 var results = CodeMirror.contextHint(this.editor);
166 166 var filterd_results = Array();
167 167 //remove results from context completion
168 168 //that are already in kernel completion
169 169 for(var elm in results) {
170 170 if(_existing_completion(results[elm]['str'], matches) == false)
171 171 { filterd_results.push(results[elm]); }
172 172 }
173 173
174 174 // append the introspection result, in order, at at the beginning of
175 175 // the table and compute the replacement range from current cursor
176 176 // positon and matched_text length.
177 177 for (var i = matches.length - 1; i >= 0; --i) {
178 178 filterd_results.unshift({
179 179 str: matches[i],
180 180 type: "introspection",
181 181 from: {
182 182 line: cur.line,
183 183 ch: cur.ch - matched_text.length
184 184 },
185 185 to: {
186 186 line: cur.line,
187 187 ch: cur.ch
188 188 }
189 189 });
190 190 }
191 191
192 192 // one the 2 sources results have been merge, deal with it
193 193 this.raw_result = filterd_results;
194 194
195 195 // if empty result return
196 196 if (!this.raw_result || !this.raw_result.length) return;
197 197
198 198 // When there is only one completion, use it directly.
199 199 if (this.autopick == true && this.raw_result.length == 1) {
200 200 this.insert(this.raw_result[0]);
201 201 return;
202 202 }
203 203
204 204 if (this.raw_result.length == 1) {
205 205 // test if first and only completion totally matches
206 206 // what is typed, in this case dismiss
207 207 var str = this.raw_result[0].str;
208 208 var pre_cursor = this.editor.getRange({
209 209 line: cur.line,
210 210 ch: cur.ch - str.length
211 211 }, cur);
212 212 if (pre_cursor == str) {
213 213 this.close();
214 214 return;
215 215 }
216 216 }
217 217
218 218 this.complete = $('<div/>').addClass('completions');
219 219 this.complete.attr('id', 'complete');
220 220
221 // Currently webkit doesn't use the size attr correctly. See:
222 // https://code.google.com/p/chromium/issues/detail?id=4579
221 223 this.sel = $('<select style="width: auto"/>')
222 224 .attr('multiple', 'true')
223 225 .attr('size', Math.min(10, this.raw_result.length));
224 226 this.complete.append(this.sel);
225 227 $('body').append(this.complete);
226 228
227 229 // After everything is on the page, compute the postion.
228 230 // We put it above the code if it is too close to the bottom of the page.
229 231 var cur = this.editor.getCursor();
230 232 cur.ch = cur.ch-matched_text.length;
231 233 var pos = this.editor.cursorCoords(cur);
232 234 var left = pos.left-3;
233 235 var top;
234 236 var cheight = this.complete.height();
235 237 var wheight = $(window).height();
236 238 if (pos.bottom+cheight+5 > wheight) {
237 239 top = pos.top-cheight-4;
238 240 } else {
239 241 top = pos.bottom+1;
240 242 }
241 243 this.complete.css('left', left + 'px');
242 244 this.complete.css('top', top + 'px');
243 245
244 246
245 247 //build the container
246 248 var that = this;
247 249 this.sel.dblclick(function () {
248 250 that.pick();
249 251 });
250 252 this.sel.blur(this.close);
251 253 this.sel.keydown(function (event) {
252 254 that.keydown(event);
253 255 });
254 256
255 257 this.build_gui_list(this.raw_result);
256 258
257 259 this.sel.focus();
258 // This needs to be after the focus() call because that puts the notebook into
259 // command mode.
260 260 IPython.keyboard_manager.null_mode();
261 261 // Opera sometimes ignores focusing a freshly created node
262 262 if (window.opera) setTimeout(function () {
263 263 if (!this.done) this.sel.focus();
264 264 }, 100);
265 265 return true;
266 266 }
267 267
268 268 Completer.prototype.insert = function (completion) {
269 269 this.editor.replaceRange(completion.str, completion.from, completion.to);
270 270 }
271 271
272 272 Completer.prototype.build_gui_list = function (completions) {
273 273 for (var i = 0; i < completions.length; ++i) {
274 274 var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
275 275 this.sel.append(opt);
276 276 }
277 277 this.sel.children().first().attr('selected', 'true');
278 278 this.sel.scrollTop(0);
279 279 }
280 280
281 281 Completer.prototype.close = function () {
282 282 if (this.done) return;
283 283 this.done = true;
284 284 $('.completions').remove();
285 285 IPython.keyboard_manager.edit_mode();
286 286 }
287 287
288 288 Completer.prototype.pick = function () {
289 289 this.insert(this.raw_result[this.sel[0].selectedIndex]);
290 290 this.close();
291 291 var that = this;
292 292 setTimeout(function () {
293 293 that.editor.focus();
294 294 }, 50);
295 295 }
296 296
297 297
298 298 Completer.prototype.keydown = function (event) {
299 299 var code = event.keyCode;
300 300 var that = this;
301 301 var special_key = false;
302 302
303 303 // detect special keys like SHIFT,PGUP,...
304 304 for( var _key in key ) {
305 305 if (code == key[_key] ) {
306 306 special_key = true;
307 307 }
308 308 };
309 309
310 310 // Enter
311 311 if (code == key.ENTER) {
312 312 CodeMirror.e_stop(event);
313 313 this.pick();
314 314 }
315 315 // Escape or backspace
316 316 else if (code == key.ESC) {
317 317 CodeMirror.e_stop(event);
318 318 this.close();
319 319 this.editor.focus();
320 320 } else if (code == key.SPACE || code == key.BACKSPACE) {
321 321 this.close();
322 322 this.editor.focus();
323 323 } else if (code == key.TAB) {
324 324 //all the fastforwarding operation,
325 325 //Check that shared start is not null which can append with prefixed completion
326 326 // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
327 327 // to erase py
328 328 var sh = shared_start(this.raw_result, true);
329 329 if (sh) {
330 330 this.insert(sh);
331 331 }
332 332 this.close();
333 333 CodeMirror.e_stop(event);
334 334 this.editor.focus();
335 335 //reinvoke self
336 336 setTimeout(function () {
337 337 that.carry_on_completion();
338 338 }, 50);
339 339 } else if (code == key.UPARROW || code == key.DOWNARROW) {
340 340 // need to do that to be able to move the arrow
341 341 // when on the first or last line ofo a code cell
342 342 event.stopPropagation();
343 343 } else if (special_key != true) {
344 344 this.close();
345 345 this.editor.focus();
346 346 //we give focus to the editor immediately and call sell in 50 ms
347 347 setTimeout(function () {
348 348 that.carry_on_completion();
349 349 }, 50);
350 350 }
351 351 }
352 352
353 353
354 354 IPython.Completer = Completer;
355 355
356 356 return IPython;
357 357 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now